congruence_solver 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.
- checksums.yaml +4 -4
- data/.gitignore +2 -2
- data/.gitmodules +3 -2
- data/Gemfile +4 -0
- data/README.md +17 -10
- data/Rakefile +59 -25
- data/bench/bench_tools.rb +26 -26
- data/bench/solve_congruence_bm.rb +35 -35
- data/bin/csolve.rb +21 -21
- data/congruence_solver.gemspec +6 -6
- data/ext/congruence_solver/arith_utils.c +72 -75
- data/ext/congruence_solver/congruence_solver.c +43 -43
- data/ext/congruence_solver/congruences.c +175 -147
- data/ext/congruence_solver/extconf.rb +0 -13
- data/ext/congruence_solver/prime_gen.c +83 -83
- data/ext/congruence_solver/test/arith_utils_test.h +7 -7
- data/ext/congruence_solver/test/congruences_test.c +2 -2
- data/ext/congruence_solver/test/congruences_test.h +36 -1
- data/lib/congruence_solver/version.rb +1 -1
- data/lib/polynomial_interpreter.rb +114 -114
- data/spec/congruence_solver_spec.rb +34 -34
- data/spec/csolve_spec.rb +74 -74
- metadata +18 -3
@@ -18,129 +18,129 @@ static int NEXT_INTEGER = FIRST_PRIME;
|
|
18
18
|
|
19
19
|
|
20
20
|
int * primes(int length){
|
21
|
-
|
21
|
+
int * rtrn_list = calloc(length, sizeof(int));
|
22
22
|
|
23
|
-
|
23
|
+
expand_prime_list_to_length(length);
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
if(rtrn_list != NULL){
|
26
|
+
memcpy(rtrn_list, PRIME_LIST, length*sizeof(int));
|
27
|
+
}
|
28
28
|
|
29
|
-
|
29
|
+
return rtrn_list;
|
30
30
|
}
|
31
31
|
|
32
32
|
|
33
33
|
int * primes_upto(int max){
|
34
|
-
|
35
|
-
|
34
|
+
int * rtrn_list;
|
35
|
+
int i;
|
36
36
|
|
37
|
-
|
37
|
+
expand_prime_list_past(max);
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
for(i = PRIME_LIST_LENGTH; i > 0; i--){
|
40
|
+
if(PRIME_LIST[i-1] <= max){
|
41
|
+
rtrn_list = calloc(i+1, sizeof(int));
|
42
|
+
rtrn_list[0] = i;
|
43
|
+
memcpy(rtrn_list+1, PRIME_LIST, i*sizeof(int));
|
44
|
+
return rtrn_list;
|
45
|
+
}
|
46
|
+
}
|
47
47
|
|
48
|
-
|
48
|
+
return NULL;
|
49
49
|
}
|
50
50
|
|
51
51
|
|
52
52
|
int * prime_factors(int n){
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
int * rtrn_list = malloc(sizeof(int));
|
54
|
+
int rtrn_list_length = 0;
|
55
|
+
int least_div;
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
while(n != 1){
|
58
|
+
least_div = least_divisor(n);
|
59
|
+
rtrn_list = realloc(rtrn_list, (rtrn_list_length+2)*sizeof(int));
|
60
|
+
rtrn_list[++rtrn_list_length] = least_div;
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
if(least_div == n){
|
63
|
+
break;
|
64
|
+
}
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
while(n % least_div == 0){
|
67
|
+
n /= least_div;
|
68
|
+
}
|
69
|
+
}
|
70
70
|
|
71
|
-
|
71
|
+
rtrn_list[0] = rtrn_list_length;
|
72
72
|
|
73
|
-
|
73
|
+
return rtrn_list;
|
74
74
|
}
|
75
75
|
|
76
76
|
|
77
77
|
static int least_divisor(int n){
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
//Calculate maximum for least divisor (sqrt)
|
79
|
+
int least_div_max = sqrt(n) + 1;
|
80
|
+
int i;
|
81
|
+
//Expand prime list up to the least divisor
|
82
|
+
expand_prime_list_past(least_div_max);
|
83
83
|
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
for(i = 0; PRIME_LIST[i] < least_div_max; i++){
|
86
|
+
if(n % PRIME_LIST[i] == 0){
|
87
|
+
return PRIME_LIST[i];
|
88
|
+
}
|
89
|
+
}
|
90
90
|
|
91
|
-
|
91
|
+
return n;
|
92
92
|
}
|
93
93
|
|
94
94
|
|
95
95
|
static void expand_prime_list_to_length(int length){
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
if(PRIME_LIST_MAX_LENGTH < length){
|
98
|
+
PRIME_LIST_MAX_LENGTH = 2*length;
|
99
|
+
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(int));
|
100
|
+
}
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
while(PRIME_LIST_LENGTH < length){
|
103
|
+
if( !any_divisors(PRIME_LIST, PRIME_LIST_LENGTH, NEXT_INTEGER) ){
|
104
|
+
PRIME_LIST[PRIME_LIST_LENGTH++] = NEXT_INTEGER;
|
105
|
+
}
|
106
106
|
|
107
|
-
|
108
|
-
|
107
|
+
NEXT_INTEGER++;
|
108
|
+
}
|
109
109
|
|
110
110
|
}
|
111
111
|
|
112
112
|
|
113
113
|
static void expand_prime_list_past(int max){
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
114
|
+
if(PRIME_LIST == NULL){
|
115
|
+
//TODO: find better heuristic limit on memory necessary to allocate
|
116
|
+
PRIME_LIST_MAX_LENGTH = (max/2) + 1;
|
117
|
+
PRIME_LIST = calloc(PRIME_LIST_MAX_LENGTH, sizeof(int));
|
118
|
+
PRIME_LIST[0] = NEXT_INTEGER++;
|
119
|
+
PRIME_LIST_LENGTH = 1;
|
120
|
+
}
|
121
|
+
|
122
|
+
while(PRIME_LIST[PRIME_LIST_LENGTH-1] <= max){
|
123
|
+
while(any_divisors(PRIME_LIST, PRIME_LIST_LENGTH, NEXT_INTEGER) ){
|
124
|
+
NEXT_INTEGER++;
|
125
|
+
}
|
126
|
+
|
127
|
+
if( PRIME_LIST_MAX_LENGTH <= PRIME_LIST_LENGTH){
|
128
|
+
PRIME_LIST_MAX_LENGTH *= 2;
|
129
|
+
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(int));
|
130
|
+
}
|
131
|
+
|
132
|
+
PRIME_LIST[PRIME_LIST_LENGTH++] = NEXT_INTEGER;
|
133
|
+
}
|
134
134
|
}
|
135
135
|
|
136
136
|
|
137
137
|
static int any_divisors(int * div_list, int length, int num){
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
138
|
+
int i;
|
139
|
+
for(i = 0; i < length; i++){
|
140
|
+
if(num % div_list[i] == 0){
|
141
|
+
return 1;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
return 0;
|
146
146
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#define NUM_OF_MOD_INV_TESTS 4
|
2
|
-
#define NUM_OF_MOD_PRODUCT_TESTS
|
2
|
+
#define NUM_OF_MOD_PRODUCT_TESTS 3
|
3
3
|
#define NUM_OF_MOD_POWER_TESTS 4
|
4
4
|
#define NUM_OF_COPRIME_TESTS 5
|
5
5
|
#define NUM_OF_TOTIENT_TESTS 6
|
@@ -11,15 +11,15 @@ int TOTIENT_EVALS[NUM_OF_TOTIENT_TESTS] = {1, 1, 2, 40, 100, 3680};
|
|
11
11
|
int COPRIME_NUM_PAIRS[NUM_OF_COPRIME_TESTS][2] = {{3,5}, {9, 28}, {100, 34}, {1000512415, 557825}, {2286144, 1515839}};
|
12
12
|
int COPRIME_EVALS[NUM_OF_COPRIME_TESTS] = {1, 1, 0, 0, 1};
|
13
13
|
|
14
|
-
int MOD_PRODUCT_NUM_PAIRS[NUM_OF_MOD_PRODUCT_TESTS][2] = {{5,6}, {41,3}, {16,
|
15
|
-
int MOD_PRODUCT_MODS[NUM_OF_MOD_PRODUCT_TESTS] = {10, 8, 19
|
16
|
-
int MOD_PRODUCT_PRODUCTS[NUM_OF_MOD_PRODUCT_TESTS] = {0, 3, 10
|
14
|
+
int MOD_PRODUCT_NUM_PAIRS[NUM_OF_MOD_PRODUCT_TESTS][2] = {{5,6}, {41,3}, {16, 3}};
|
15
|
+
int MOD_PRODUCT_MODS[NUM_OF_MOD_PRODUCT_TESTS] = {10, 8, 19};
|
16
|
+
int MOD_PRODUCT_PRODUCTS[NUM_OF_MOD_PRODUCT_TESTS] = {0, 3, 10};
|
17
17
|
|
18
|
-
int MOD_INV_NUMS[NUM_OF_MOD_INV_TESTS] = {5, 4, 53,
|
18
|
+
int MOD_INV_NUMS[NUM_OF_MOD_INV_TESTS] = {5, 4, 53, 3};
|
19
19
|
int MOD_INV_MODS[NUM_OF_MOD_INV_TESTS] = {12, 23, 105, 7};
|
20
20
|
int MOD_INV_INVS[NUM_OF_MOD_INV_TESTS] = {5, 6, 2, 5};
|
21
21
|
|
22
|
-
int MOD_POWER_NUMS[NUM_OF_MOD_POWER_TESTS] = {
|
22
|
+
int MOD_POWER_NUMS[NUM_OF_MOD_POWER_TESTS] = {3, 19, 6, 7};
|
23
23
|
int MOD_POWER_MODS[NUM_OF_MOD_POWER_TESTS] = {4, 23, 7, 33};
|
24
24
|
int MOD_POWER_POWERS[NUM_OF_MOD_POWER_TESTS] = {10, 3, 4, 635};
|
25
|
-
int MOD_POWER_EVALS[NUM_OF_MOD_POWER_TESTS] ={1, 5, 1,
|
25
|
+
int MOD_POWER_EVALS[NUM_OF_MOD_POWER_TESTS] ={1, 5, 1, 10};
|
@@ -13,7 +13,7 @@ int main(){
|
|
13
13
|
failures += solve_congruence_test(POL_3_DEGREE, POL_3_COEFFS, POL_3_MOD, NUM_OF_POL_3_SOLS, POL_3_SOLS);
|
14
14
|
failures += solve_congruence_test(POL_4_DEGREE, POL_4_COEFFS, POL_4_MOD, NUM_OF_POL_4_SOLS, POL_4_SOLS);
|
15
15
|
failures += solve_congruence_test(POL_5_DEGREE, POL_5_COEFFS, POL_5_MOD, NUM_OF_POL_5_SOLS, POL_5_SOLS);
|
16
|
-
|
16
|
+
failures += solve_congruence_test(POL_6_DEGREE, POL_6_COEFFS, POL_6_MOD, NUM_OF_POL_6_SOLS, POL_6_SOLS);
|
17
17
|
|
18
18
|
return failures;
|
19
19
|
}
|
@@ -54,7 +54,7 @@ int solve_congruence_test(int func_degree, int * func_coeffs, int mod, int num_o
|
|
54
54
|
|
55
55
|
print_polynomial_inline(func_degree, func_coeffs);
|
56
56
|
|
57
|
-
printf(" = 0: %d given instead of %d.\n\n", solutions_to_test[i+1], solutions[i]);
|
57
|
+
printf(" = 0 (mod %d): %d given instead of %d.\n\n", mod, solutions_to_test[i+1], solutions[i]);
|
58
58
|
|
59
59
|
return 1;
|
60
60
|
}
|
@@ -105,6 +105,24 @@
|
|
105
105
|
#define POL_5_SOL_7 299584
|
106
106
|
#define POL_5_SOL_8 313399
|
107
107
|
|
108
|
+
#define POL_6_DEGREE 5
|
109
|
+
#define POL_6_COEFF_0 1
|
110
|
+
#define POL_6_COEFF_1 2
|
111
|
+
#define POL_6_COEFF_2 1
|
112
|
+
#define POL_6_COEFF_3 -1
|
113
|
+
#define POL_6_COEFF_4 0
|
114
|
+
#define POL_6_COEFF_5 -3
|
115
|
+
#define POL_6_MOD 49
|
116
|
+
#define NUM_OF_POL_6_SOLS 8
|
117
|
+
#define POL_6_SOL_0 1
|
118
|
+
#define POL_6_SOL_1 8
|
119
|
+
#define POL_6_SOL_2 15
|
120
|
+
#define POL_6_SOL_3 22
|
121
|
+
#define POL_6_SOL_4 26
|
122
|
+
#define POL_6_SOL_5 29
|
123
|
+
#define POL_6_SOL_6 36
|
124
|
+
#define POL_6_SOL_7 43
|
125
|
+
|
108
126
|
|
109
127
|
int POL_1_COEFFS[POL_1_DEGREE+1] = {POL_1_COEFF_0,
|
110
128
|
POL_1_COEFF_1,
|
@@ -201,4 +219,21 @@ int POL_5_SOLS[NUM_OF_POL_5_SOLS] = {POL_5_SOL_0,
|
|
201
219
|
POL_5_SOL_5,
|
202
220
|
POL_5_SOL_6,
|
203
221
|
POL_5_SOL_7,
|
204
|
-
POL_5_SOL_8};
|
222
|
+
POL_5_SOL_8};
|
223
|
+
|
224
|
+
int POL_6_COEFFS[POL_6_DEGREE+1] = {POL_6_COEFF_0,
|
225
|
+
POL_6_COEFF_1,
|
226
|
+
POL_6_COEFF_2,
|
227
|
+
POL_6_COEFF_3,
|
228
|
+
POL_6_COEFF_4,
|
229
|
+
POL_6_COEFF_5
|
230
|
+
};
|
231
|
+
|
232
|
+
int POL_6_SOLS[NUM_OF_POL_6_SOLS] = {POL_6_SOL_0,
|
233
|
+
POL_6_SOL_1,
|
234
|
+
POL_6_SOL_2,
|
235
|
+
POL_6_SOL_3,
|
236
|
+
POL_6_SOL_4,
|
237
|
+
POL_6_SOL_5,
|
238
|
+
POL_6_SOL_6,
|
239
|
+
POL_6_SOL_7};
|
@@ -1,116 +1,116 @@
|
|
1
1
|
class PolynomialInterpreter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
2
|
+
module Errors
|
3
|
+
POLYNOMIAL_INVALID = ArgumentError.new "polynomial invalid"
|
4
|
+
CONGRUENCE_INVALID = ArgumentError.new "congruence invalid"
|
5
|
+
LHS_POLYNOMIAL_INVALID = ArgumentError.new "lhs polynomial invalid"
|
6
|
+
RHS_POLYNOMIAL_INVALID = ArgumentError.new "rhs polynomial invalid"
|
7
|
+
MOD_INVALID = ArgumentError.new "mod invalid"
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def self.read_congruence(input_congruence)
|
12
|
+
match_data = input_congruence.match(/^(.*)=(.*) +(?:mod +(.*)|\(mod +(.*)\))$/)
|
13
|
+
|
14
|
+
if match_data.nil?
|
15
|
+
raise Errors::CONGRUENCE_INVALID
|
16
|
+
end
|
17
|
+
|
18
|
+
lhs = match_data[1]
|
19
|
+
rhs = match_data[2]
|
20
|
+
mod = match_data[3] || match_data[4]
|
21
|
+
|
22
|
+
begin
|
23
|
+
lh_coeffs = read_coeffs(lhs.gsub(" ", ""))
|
24
|
+
rescue ArgumentError => e
|
25
|
+
if(e == Errors::POLYNOMIAL_INVALID)
|
26
|
+
raise "#{lhs}:#{Errors::RHS_POLYNOMIAL_INVALID}"
|
27
|
+
else
|
28
|
+
raise e
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
rh_coeffs = read_coeffs(rhs.gsub(" ", ""))
|
34
|
+
rescue ArgumentError => e
|
35
|
+
if e == Errors::POLYNOMIAL_INVALID
|
36
|
+
raise "#{rhs}:#{Errors::RHS_POLYNOMIAL_INVALID}"
|
37
|
+
else
|
38
|
+
raise e
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if mod !~ /\d+/ or mod.to_i < 2
|
43
|
+
raise Errors::MOD_INVALID
|
44
|
+
end
|
45
|
+
|
46
|
+
0.upto rh_coeffs.length-1 do |idx|
|
47
|
+
unless rh_coeffs[idx].nil?
|
48
|
+
lh_coeffs[idx] ||= 0
|
49
|
+
lh_coeffs[idx] -= rh_coeffs[idx]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
[lh_coeffs, mod.to_i]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.read_coeffs(input_polynomial)
|
57
|
+
if input_polynomial == ""
|
58
|
+
raise Errors::POLYNOMIAL_INVALID
|
59
|
+
end
|
60
|
+
|
61
|
+
last_var = nil
|
62
|
+
coeffs = Array.new
|
63
|
+
|
64
|
+
loop do
|
65
|
+
input_polynomial.slice!(/^(\d+)\*?/)
|
66
|
+
match_data_coe = Regexp.last_match
|
67
|
+
|
68
|
+
input_polynomial.slice!(/^([a-zA-Z])(?:\^(\d+))?/)
|
69
|
+
match_data_exp = Regexp.last_match
|
70
|
+
|
71
|
+
if match_data_coe.nil? and match_data_exp.nil?
|
72
|
+
raise Errors::POLYNOMIAL_INVALID
|
73
|
+
else
|
74
|
+
if match_data_exp.nil?
|
75
|
+
coe = match_data_coe[1].to_i
|
76
|
+
exp = 0
|
77
|
+
else
|
78
|
+
unless last_var.nil? or last_var == match_data_exp[1]
|
79
|
+
raise Errors::POLYNOMIAL_INVALID
|
80
|
+
end
|
81
|
+
|
82
|
+
last_var = match_data_exp[1]
|
83
|
+
|
84
|
+
if match_data_coe.nil?
|
85
|
+
coe = 1
|
86
|
+
else
|
87
|
+
coe = match_data_coe[1].to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
if match_data_exp[2].nil?
|
91
|
+
exp = 1
|
92
|
+
else
|
93
|
+
exp = match_data_exp[2].to_i
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
coeffs[exp] ||= 0
|
99
|
+
coeffs[exp] += coe.to_i
|
100
|
+
|
101
|
+
break if input_polynomial.length == 0
|
102
|
+
|
103
|
+
op = input_polynomial.slice!(0)
|
104
|
+
|
105
|
+
unless op.match /[-+]/
|
106
|
+
raise Errors::POLYNOMIAL_INVALID
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
0.upto(coeffs.length-1) do |idx|
|
111
|
+
coeffs[idx] ||= 0
|
112
|
+
end
|
113
|
+
|
114
|
+
coeffs
|
115
|
+
end
|
116
116
|
end
|
@@ -1,38 +1,38 @@
|
|
1
1
|
require "congruence_solver"
|
2
2
|
|
3
3
|
RSpec.describe CongruenceSolver do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
4
|
+
describe "::lift" do
|
5
|
+
it "expects 3 arguments" do
|
6
|
+
expect {CongruenceSolver.lift()}.to raise_error ArgumentError
|
7
|
+
expect {CongruenceSolver.lift(0)}.to raise_error ArgumentError
|
8
|
+
expect {CongruenceSolver.lift([1,2], 3)}.not_to raise_error
|
9
|
+
expect {CongruenceSolver.lift([1], [1], nil)}.to raise_error ArgumentError
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
it "solves individual polynomial congruences defined by their coefficients and mod" do
|
14
|
+
coeffs = [-1, 0, 4]
|
15
|
+
mod = 5
|
16
|
+
expect(CongruenceSolver.lift(coeffs, mod).sort).to eq [2, 3]
|
17
|
+
|
18
|
+
coeffs = [-3, 4, 9]
|
19
|
+
mod = 49
|
20
|
+
expect(CongruenceSolver.lift(coeffs, mod).sort).to eq []
|
21
|
+
|
22
|
+
coeffs = [1, -4, 4]
|
23
|
+
mod = 5104
|
24
|
+
expect(CongruenceSolver.lift(coeffs, mod).sort).to eq []
|
25
|
+
|
26
|
+
coeffs = [4, -4, 1]
|
27
|
+
mod = 5104
|
28
|
+
expect(CongruenceSolver.lift(coeffs, mod).sort).to eq [2, 1278, 2554, 3830]
|
29
|
+
|
30
|
+
coeffs = Array.new(500, 0)
|
31
|
+
coeffs[0] = -1
|
32
|
+
coeffs[500] = 1
|
33
|
+
mod = 15
|
34
|
+
expect(CongruenceSolver.lift(coeffs, mod).sort).to eq [1, 2, 4, 7, 8, 11, 13, 14]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
38
|
end
|