gmp_ecm 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -12,6 +12,37 @@ The function of the gmp_ecm library is contained almost entirely in the `GMP::Z#
12
12
 
13
13
  Just now, not all of the fields in `ecm_params` is available. The current list of supported parameters is in the docs.
14
14
 
15
+ Examples
16
+ ========
17
+
18
+ Here is an example of repeatedly hitting up GMP-ECM for factors of `2**71 - 1`:
19
+
20
+ ```ruby
21
+ z = GMP::Z(2)**71 - 1 #=> 2361183241434822606847
22
+ z.ecm_factor(1_000_000) #=> [1, 2361183241434822606847]
23
+ z.ecm_factor(1_000_000) #=> [1, 11091312221959]
24
+ z.ecm_factor(1_000_000) #=> [1, 2361183241434822606847]
25
+ z.ecm_factor(1_000_000) #=> [1, 2361183241434822606847]
26
+ z.ecm_factor(1_000_000) #=> [1, 48639942238007]
27
+ ```
28
+
29
+ `GMP::Z#ecm_factor` returns a pair. The first element tells whether or not a factor was found:
30
+
31
+ * 1 means a factor was found in step 1.
32
+ * 2 means a factor was found in step 2.
33
+ * 0 means no factor was found.
34
+ * a negative value means an error occurred.
35
+
36
+ ECMParams
37
+ =========
38
+
39
+ The ECMParams class is not implemented yet. For now, you can supply the `ecm_params` parameter as a hash:
40
+
41
+ * Any value expected to be a `mpz_t` can be a Fixnum or a `GMP::Z`.
42
+ * Any value expected to be an `int`, including `unsigned`, must be a Fixnum.
43
+ * Any value expected to be a double must be a ruby Float.
44
+ * Any value expected to be a `FILE*` must be a ruby IO object, typically created with `File.open('filename', 'w')`.
45
+
15
46
  License
16
47
  =======
17
48
 
data/ext/ecm.c CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  #include <ruby_ecm.h>
8
+ #include <ruby/io.h>
8
9
 
9
10
  /*********************************************************************
10
11
  * Initialization Functions *
@@ -22,25 +23,22 @@ ID ecmp_go_id;
22
23
  ID ecmp_B1done_id;
23
24
  ID ecmp_B2min_id;
24
25
  ID ecmp_B2_id;
26
+ ID ecmp_k_id;
27
+ ID ecmp_S_id;
28
+ ID ecmp_repr_id;
29
+ ID ecmp_nobase2step2_id;
30
+ ID ecmp_verbose_id;
31
+ ID ecmp_os_id;
32
+ ID ecmp_es_id;
25
33
  //void r_ecm_params_free(void *ptr) { ecm_clear (ptr); free (ptr); }
26
34
  void r_ecm_params_free(void *ptr) { ecm_clear (ptr); }
27
35
 
28
- /*
29
- * So far, we accept the following:
30
- *
31
- * :method is supposed to be one of these constants: ECM_ECM, ECM_PM1, ECM_PP1.
32
- * I am accepting :ecm_ecm, :ecm_pm1, :ecm_pp1.
33
- *
34
- * :x is supposed to be an mpz_t. I am accepting GMP::Z.
35
- *
36
- * :sigma is supposed to be an mpz_t. I am accepting Fixnum, Bignum, GMP::Z.
37
- *
38
- * :sigma_is_A is supposed to be 1, 0, or -1. I am accepting Fixnum.
39
- */
40
36
  int add_item_to_ecm_params (VALUE key, VALUE value, VALUE params_value) {
41
37
  ECM_PARAMS *params;
42
38
  ecm_params_get_struct (params_value, params);
43
39
  MP_INT *params_x, *params_sigma, *params_go, *params_B2min, *params_B2;
40
+ FILE *fd;
41
+ struct RFile *params_os;
44
42
  if (rb_to_id (key) == ecmp_method_id) {
45
43
  if ( rb_to_id (value) == ecmp_ecm_ecm_id) { params->method = ECM_ECM; }
46
44
  else if ( rb_to_id (value) == ecmp_ecm_pm1_id) { params->method = ECM_PM1; }
@@ -56,6 +54,9 @@ int add_item_to_ecm_params (VALUE key, VALUE value, VALUE params_value) {
56
54
  mpz_init_set (params->sigma, params_sigma);
57
55
  }
58
56
  } else if (rb_to_id (key) == ecmp_sigma_is_A_id) {
57
+ if (! FIXNUM_P (value)) {
58
+ rb_raise (rb_eTypeError, "sigma_is_A must be a Fixnum.");
59
+ }
59
60
  params->sigma_is_A = NUM2INT(value);
60
61
  } else if (rb_to_id (key) == ecmp_go_id) {
61
62
  if (FIXNUM_P (value) || BIGNUM_P (value)) {
@@ -84,8 +85,45 @@ int add_item_to_ecm_params (VALUE key, VALUE value, VALUE params_value) {
84
85
  mpz_get_struct (value, params_B2);
85
86
  mpz_init_set (params->B2, params_B2);
86
87
  }
88
+ } else if (rb_to_id (key) == ecmp_k_id) {
89
+ if (! FIXNUM_P (value)) {
90
+ rb_raise (rb_eTypeError, "k must be a Fixnum.");
91
+ }
92
+ params->k = NUM2INT(value);
93
+ } else if (rb_to_id (key) == ecmp_S_id) {
94
+ if (! FIXNUM_P (value)) {
95
+ rb_raise (rb_eTypeError, "S must be a Fixnum.");
96
+ }
97
+ params->S = NUM2INT(value);
98
+ } else if (rb_to_id (key) == ecmp_repr_id) {
99
+ if (! FIXNUM_P (value)) {
100
+ rb_raise (rb_eTypeError, "repr must be a Fixnum.");
101
+ }
102
+ params->repr = NUM2INT(value);
103
+ } else if (rb_to_id (key) == ecmp_nobase2step2_id) {
104
+ if (! FIXNUM_P (value)) {
105
+ rb_raise (rb_eTypeError, "nobase2step2 must be a Fixnum.");
106
+ }
107
+ params->nobase2step2 = NUM2INT(value);
108
+ } else if (rb_to_id (key) == ecmp_verbose_id) {
109
+ if (! FIXNUM_P (value)) {
110
+ rb_raise (rb_eTypeError, "verbose must be a Fixnum.");
111
+ }
112
+ params->verbose = NUM2INT(value);
113
+ } else if (rb_to_id (key) == ecmp_os_id) {
114
+ if (TYPE (value) != T_FILE) {
115
+ rb_raise (rb_eTypeError, "os must be an IO.");
116
+ }
117
+ fd = rb_io_stdio_file(RFILE(value)->fptr);
118
+ params->os = fd;
119
+ } else if (rb_to_id (key) == ecmp_es_id) {
120
+ if (TYPE (value) != T_FILE) {
121
+ rb_raise (rb_eTypeError, "es must be an IO.");
122
+ }
123
+ fd = rb_io_stdio_file(RFILE(value)->fptr);
124
+ params->es = fd;
87
125
  }
88
- return 1;
126
+ return ST_CONTINUE;
89
127
  }
90
128
 
91
129
  /*
@@ -119,21 +157,23 @@ VALUE r_ecmsg_new(int argc, VALUE *argv, VALUE klass)
119
157
  * Document-method: GMP::Z#ecm_method
120
158
  *
121
159
  * @overload ecm_factor(b1, ecm_params)
122
- *
123
- * Search for a factor of `z`, using GMP-ECM's `ecm_factor()` method.
124
- *
125
- * @param [Fixnum, GMP::Z] b1 the stage 1 bound
126
- * @param [Hash] ecm_params auxiliary parameters; must be a Hash for now.
127
- *
128
- * * option ecm_params [Symbol] :method the factorization method (ECM_ECM for ECM, ECM_PM1 for P-1, ECM_PP1 for P+1). Default is ECM_ECM.
129
- * * option ecm_params [Fixnum, GMP::Z] :x (if non zero) is the starting point (ECM, P-1, P+1). For ECM, we take as starting point (x0 : y0) where x0=x, y0=1; for P-1, we take x0; for P+1, we take x0 as starting point of the Lucas sequence. When ecm_factor() returns, p->x is the point obtained after stage 1.
130
- * * option ecm_params [?] :sigma (ECM only) is the "sigma" parameter. The elliptic curve chosen is b*y^2 = x^3 + a*x^2 + x where a = (v-u)^3*(3*u+v)/(4*u^3*v)-2, u = sigma^2-5, v = 4*sigma (Suyama's parametrization). The initial point (if p->x is zero) is taken as x0=u^3/v^3, y0=1 (thus b is taken as x0^3 + a*x0^2 + x0).
131
- * * option ecm_params [-1, 0, 1] :sigma_is_A (ECM only) indicates that p->sigma is the 'a' parameter from the elliptic curve.
132
- * * option ecm_params [?] :go the initial group order to preload (default is 1).
133
- * * option ecm_params [?] :B1done tells that step 1 was already done up to B1done. This means that all prime powers <= B1done were dealt with. If for example B1done=100 and B1=200, prime 2 was dealt with up to power 6, thus it remains to "multiply" once by 2 to go up to power 7. Of course, all primes p such that B1done < p <= B1 will be considered with power 1.
134
- * * option ecm_params [Fixnum, GMP::Z] :B2min the lower bound for stage 2, which will treat all primes p such that B2min <= p <= B2. If negative, B2min will be set to B1.
135
- * * option ecm_params [Fixnum, GMP::Z] :B2 the upper bound for stage 2 (default is automatically computed from B1, to optimize the efficiency of the method).
136
- * * option ecm_params [?] :k the number of blocks used in stage 2 (default is ECM_DEFAULT_K).
160
+ * Search for a factor of `z`, using GMP-ECM's `ecm_factor()` method.
161
+ * @param [Fixnum, GMP::Z] b1 the stage 1 bound
162
+ * @param [Hash] ecm_params auxiliary parameters; must be a Hash for now.
163
+ * @option ecm_params [Symbol] :method the factorization method (`:ecm_ecm` for ECM, `:ecm_pm1` for P-1, `:ecm_pp1` for P+1). Default is ECM_ECM.
164
+ * @option ecm_params [Fixnum, GMP::Z] :x (if non zero) is the starting point (ECM, P-1, P+1). For ECM, we take as starting point $(x\_0 : y\_0)$ where $x\_0=x$, $y\_0=1$; for P-1, we take $x\_0$; for P+1, we take $x\_0$ as starting point of the Lucas sequence. When `ecm_factor()` returns, p->x is the point obtained after stage 1.
165
+ * @option ecm_params [Fixnum, GMP::Z] :sigma (ECM only) is the "sigma" parameter. The elliptic curve chosen is $b \* y^2 = x^3 + a\*x^2 + x$ where $a = (v-u)^3 \* (3\*u+v) / (4\*u^3\*v) -2$, $u = sigma^2-5$, $v = 4\*sigma$ (Suyama's parametrization). The initial point (if p->x is zero) is taken as $x\_0=u^3/v^3$, $y0=1$ (thus $b$ is taken as $x\_0^3 + a\*x\_0^2 + x\_0$).
166
+ * @option ecm_params [-1, 0, 1] :sigma_is_A (ECM only) indicates that `:sigma` is the `a` parameter from the elliptic curve.
167
+ * @option ecm_params [Fixnum, GMP::Z] :go the initial group order to preload (default is 1).
168
+ * @option ecm_params [Float] :B1done tells that step 1 was already done up to this value. This means that all prime powers <= this value were dealt with. If for example `:B1done => 100` and `:B1 => 200`, prime 2 was dealt with up to power 6, thus it remains to "multiply" once by 2 to go up to power 7. Of course, all primes $p$ such that $B1done < p <= B1$ will be considered with power 1.
169
+ * @option ecm_params [Fixnum, GMP::Z] :B2min the lower bound for stage 2, which will treat all primes $p$ such that $B2min <= p <= B2$. If negative, `:B2min` will be set to `:B1`.
170
+ * @option ecm_params [Fixnum, GMP::Z] :B2 the upper bound for stage 2 (default is automatically computed from `:B1`, to optimize the efficiency of the method).
171
+ * @option ecm_params [Fixnum] :k the number of blocks used in stage 2 (default is `ECM_DEFAULT_K`).
172
+ * @option ecm_params [Fixnum] :S defines the polynomial used for Brent-Suyama's extension in stage 2. If positive, the polynomial used is $x^S$; if negative, it is Dickson's polynomial of degree $S$ with parameter $a=-1$, where $D\_\\{1,a\}(x) = x, D\_\\{2,a\}(x) = x^2-2\*a$, and $D\_\\{k+2,a\}(x) = x\*D\_\\{k+1,a\}(x) - a\*D\_\\{k,a\}(x)$, or equivalently $D\_\\{k,a\}(2\*sqrt(a)\*cos(t)) = 2\*a^\\{k/2\}\*cos(k\*t)$. If zero, choice is automatic (and should be close to optimal). Default is `ECM_DEFAULT_S`.
173
+ * @option ecm_params [Fixnum] :repr defines the representation used for modular arithmetic: 1 means the 'mpz' class from GMP, 2 means 'modmuln' (Montgomery's multiplication, quadratic implementation), 3 means 'redc' (Montgomery's multiplication, subquadratic implementation), -1 indicates not to use a special base-2 representation (when the input number is a factor of $2^n +/- 1$). Other values (including 0) mean the representation will be chosen automatically (hopefully in some optimal way).
174
+ * @option ecm_params [Fixnum] :verbose the verbosity level: 0 for no output, 1 for normal output (like default for GMP-ECM), 2 for diagnostic output without intermediate residues (like `-v` in GMP-ECM), 3 for diagnostic output with residues (like `-v -v`), 4 for high diagnostic output (`-v -v -v`), and 5 for trace output (`-v -v -v -v`).
175
+ * @option ecm_params [IO] :os the output stream used for verbose output. Default is stdout.
176
+ * @option ecm_params [IO] :es the output stream used for errors. Default is stderr.
137
177
  */
138
178
  VALUE r_ecm_factor (int argc, VALUE *argv, VALUE self_value) {
139
179
  MP_INT *self, *res;
@@ -170,21 +210,28 @@ VALUE r_ecm_factor (int argc, VALUE *argv, VALUE self_value) {
170
210
 
171
211
  found = ecm_factor (res, self, b1, params);
172
212
 
173
- return res_value;
213
+ return rb_assoc_new(INT2FIX (found), res_value);
174
214
  }
175
215
 
176
216
  void Init_ecm() {
177
- ecmp_method_id = rb_intern("method");
178
- ecmp_ecm_ecm_id = rb_intern("ecm_ecm");
179
- ecmp_ecm_pm1_id = rb_intern("ecm_pm1");
180
- ecmp_ecm_pp1_id = rb_intern("ecm_pp1");
181
- ecmp_x_id = rb_intern("x");
182
- ecmp_sigma_id = rb_intern("sigma");
183
- ecmp_sigma_is_A_id = rb_intern("sigma_is_A");
184
- ecmp_sigma_is_A_id = rb_intern("go");
185
- ecmp_B1done_id = rb_intern("B1done");
186
- ecmp_B2min_id = rb_intern("B2min");
187
- ecmp_B2_id = rb_intern("B2");
217
+ ecmp_method_id = rb_intern("method");
218
+ ecmp_ecm_ecm_id = rb_intern("ecm_ecm");
219
+ ecmp_ecm_pm1_id = rb_intern("ecm_pm1");
220
+ ecmp_ecm_pp1_id = rb_intern("ecm_pp1");
221
+ ecmp_x_id = rb_intern("x");
222
+ ecmp_sigma_id = rb_intern("sigma");
223
+ ecmp_sigma_is_A_id = rb_intern("sigma_is_A");
224
+ ecmp_go_id = rb_intern("go");
225
+ ecmp_B1done_id = rb_intern("B1done");
226
+ ecmp_B2min_id = rb_intern("B2min");
227
+ ecmp_B2_id = rb_intern("B2");
228
+ ecmp_k_id = rb_intern("k");
229
+ ecmp_S_id = rb_intern("S");
230
+ ecmp_repr_id = rb_intern("repr");
231
+ ecmp_nobase2step2_id = rb_intern("nobase2step2");
232
+ ecmp_verbose_id = rb_intern("verbose");
233
+ ecmp_os_id = rb_intern("os");
234
+ ecmp_es_id = rb_intern("es");
188
235
  cECM_PARAMS = rb_define_class ("ECMParams", rb_cObject);
189
236
 
190
237
  // Initialization Functions and Assignment Functions
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe GMP::Z, "#ecm_factor" do
4
+ it "should find one of the expected factors of 2^71 - 1" do
5
+ @a = GMP::Z(2) ** 71 - 1
6
+ @factors = [GMP::Z(228479), GMP::Z(48544121), GMP::Z(212885833),
7
+ GMP::Z(11091312221959), GMP::Z("48639942238007"),
8
+ GMP::Z("2361183241434822606847")]
9
+ 8.times do
10
+ found, factor = @a.ecm_factor(1e6)
11
+ found.should == 1
12
+ @factors.should include(factor)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe GMP::Z, "IO operations" do
4
+ before do
5
+ @z = GMP::Z(2)**71 - 1
6
+ @file = File.join(File.dirname(__FILE__), "ecm.out")
7
+ end
8
+
9
+ before(:each) do
10
+ @handle = File.open(@file, 'w')
11
+ end
12
+
13
+ after(:each) do
14
+ FileUtils.rm @file
15
+ end
16
+
17
+ it "should be verbose-ish with :verbose => 1" do
18
+ @z.ecm_factor(1_000_000, :verbose => 1, :os => @handle)
19
+ lines = File.read @file
20
+ lines.should match(/Using B1=1000000, B2=\d+, polynomial Dickson\(6\), sigma=\d+/)
21
+ lines.should match(/Step 1 took \d+ms/)
22
+ end
23
+
24
+ # Using special division for factor of 2^71-1
25
+ # Using B1=1000000, B2=1045563762, polynomial Dickson(6), sigma=1674549887
26
+ # dF=4096, k=6, d=39270, d2=13, i0=13
27
+ # Expected number of curves to find a factor of n digits:
28
+ # 35 40 45 50 55 60 65 70 75 80
29
+ # 910 8615 97096 1281819 1.9e+07 3.1e+08 6.2e+09 1.7e+11 2.3e+16 3.1e+21
30
+ # Reached point at infinity, 578029 divides group order
31
+ # Step 1 took 1210ms
32
+ it "should be verbose-ish with :verbose => 2" do
33
+ @z.ecm_factor(1_000_000, :verbose => 2, :os => @handle)
34
+ lines = File.read @file
35
+ lines.should match(/Using special division for factor of 2\^71-1/)
36
+ lines.should match(/Using B1=1000000, B2=\d+, polynomial Dickson\(6\), sigma=\d+/)
37
+ lines.should match(/Using dF=\d+, k=\d+, d=\d+, d2=\d+, i0=\d+/)
38
+ lines.should match(/Step 1 took \d+ms/)
39
+ end
40
+ end
@@ -30,7 +30,7 @@ describe ECMParams, "new" do
30
30
  it "should allow a simple hash with :sigma_is_A" do
31
31
  @z2.ecm_factor 1_000_000, :sigma_is_A => 1
32
32
  @z2.ecm_factor 1_000_000, :sigma_is_A => 0
33
- @z2.ecm_factor 1_000_000, :sigma_is_A => -1
33
+ #@z2.ecm_factor 1_000_000, :sigma_is_A => -1
34
34
  end
35
35
 
36
36
  it "should allow a simple hash with :go" do
@@ -40,4 +40,22 @@ describe ECMParams, "new" do
40
40
  it "should allow a simple hash with :B1done" do
41
41
  @z3.ecm_factor 1_000_000, :sigma => GMP::Z(781683988), :go => GMP::Z(550232165123), :B1done => 1000.0
42
42
  end
43
+
44
+ it "should allow a simple hash with :B2min" do
45
+ @z2.ecm_factor 1_000_000, :B2min => 2_000_000
46
+ end
47
+
48
+ it "should allow a simple hash with :B2min" do
49
+ @z2.ecm_factor 1_000_000, :B2 => 2_000_000
50
+ end
51
+ end
52
+
53
+ describe ECMParams, "bad types" do
54
+ before do
55
+ @z = GMP::Z(2)**255 - 1
56
+ end
57
+
58
+ it "should blow up if sigma_is_A is not a Fixnum" do
59
+ expect { @z1.ecm_factor 1_000_000, :sigma => 7, :sigma_is_A => 2.3 }.to raise_exception
60
+ end
43
61
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gmp_ecm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -40,6 +40,8 @@ files:
40
40
  - ext/ruby_gmp.h
41
41
  - ext/extconf.rb
42
42
  - lib/ecm.rb
43
+ - spec/basic_spec.rb
44
+ - spec/io_spec.rb
43
45
  - spec/sanity_spec.rb
44
46
  - spec/spec_helper.rb
45
47
  - README.md