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 +31 -0
- data/ext/ecm.c +87 -40
- data/spec/basic_spec.rb +15 -0
- data/spec/io_spec.rb +40 -0
- data/spec/sanity_spec.rb +19 -1
- metadata +3 -1
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
|
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
|
-
*
|
124
|
-
*
|
125
|
-
*
|
126
|
-
*
|
127
|
-
*
|
128
|
-
*
|
129
|
-
*
|
130
|
-
*
|
131
|
-
*
|
132
|
-
*
|
133
|
-
*
|
134
|
-
*
|
135
|
-
*
|
136
|
-
*
|
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
|
178
|
-
ecmp_ecm_ecm_id
|
179
|
-
ecmp_ecm_pm1_id
|
180
|
-
ecmp_ecm_pp1_id
|
181
|
-
ecmp_x_id
|
182
|
-
ecmp_sigma_id
|
183
|
-
ecmp_sigma_is_A_id
|
184
|
-
|
185
|
-
ecmp_B1done_id
|
186
|
-
ecmp_B2min_id
|
187
|
-
ecmp_B2_id
|
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
|
data/spec/basic_spec.rb
ADDED
@@ -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
|
data/spec/io_spec.rb
ADDED
@@ -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
|
data/spec/sanity_spec.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|