gmp_ecm 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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