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
@@ -13,73 +13,73 @@ VALUE method_congruence_solver_brute_force(VALUE self, VALUE funcoeffs, VALUE mo
|
|
13
13
|
|
14
14
|
|
15
15
|
void Init_congruence_solver(){
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
CongruenceSolver = rb_define_module("CongruenceSolver");
|
17
|
+
|
18
|
+
rb_define_singleton_method(CongruenceSolver, "lift",
|
19
|
+
method_congruence_solver_lift, 2);
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
rb_define_singleton_method(CongruenceSolver, "brute_force",
|
22
|
+
method_congruence_solver_brute_force, 2);
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
/*rb_define_singleton_method(CongruenceSolver, "solve_system_of_congruences",
|
25
|
+
method_congruence_solver_solve_system_of_congruence, 3);
|
26
|
+
*/
|
27
27
|
}
|
28
28
|
|
29
29
|
|
30
30
|
VALUE method_congruence_solver_brute_force(VALUE self, VALUE funcCoeffs, VALUE mod){
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
int i;
|
32
|
+
int * intSolutions;
|
33
|
+
VALUE rbSolutions;
|
34
|
+
int intMod = NUM2INT(mod);
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
int intFuncDegree = RARRAY_LEN(funcCoeffs)-1;
|
37
|
+
int * intFuncCoeffs = calloc(intFuncDegree+1, sizeof(int));
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
for(i = 0; i <= intFuncDegree; i++){
|
40
|
+
intFuncCoeffs[i] = NUM2INT(rb_ary_entry(funcCoeffs, i));
|
41
|
+
}
|
42
42
|
|
43
|
-
|
44
|
-
|
43
|
+
intSolutions = brute_force_congruence(intFuncDegree, intFuncCoeffs, intMod);
|
44
|
+
rbSolutions = rb_ary_new2(intSolutions[0]);
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
for(i=0; i<intSolutions[0]; i++){
|
47
|
+
rb_ary_store(rbSolutions, i, INT2NUM(intSolutions[i+1]));
|
48
|
+
}
|
49
49
|
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
free(intFuncCoeffs);
|
52
|
+
free(intSolutions);
|
53
53
|
|
54
|
-
|
54
|
+
return rbSolutions;
|
55
55
|
}
|
56
56
|
|
57
57
|
|
58
58
|
VALUE method_congruence_solver_lift(VALUE self, VALUE funcCoeffs, VALUE mod){
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
int i;
|
60
|
+
int * intSolutions;
|
61
|
+
VALUE rbSolutions;
|
62
|
+
int intMod = NUM2INT(mod);
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
int intFuncDegree = RARRAY_LEN(funcCoeffs)-1;
|
65
|
+
int * intFuncCoeffs = calloc(intFuncDegree+1, sizeof(int));
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
for(i=0; i<=intFuncDegree; i++){
|
68
|
+
intFuncCoeffs[i] = NUM2INT(rb_ary_entry(funcCoeffs, i));
|
69
|
+
}
|
70
70
|
|
71
71
|
|
72
|
-
|
73
|
-
|
72
|
+
intSolutions = solve_congruence(intFuncDegree, intFuncCoeffs, intMod);
|
73
|
+
rbSolutions = rb_ary_new2(intSolutions[0]);
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
for(i=0; i<intSolutions[0]; i++){
|
76
|
+
rb_ary_store(rbSolutions, i, INT2NUM(intSolutions[i+1]));
|
77
|
+
}
|
78
78
|
|
79
79
|
|
80
|
-
|
81
|
-
|
80
|
+
free(intFuncCoeffs);
|
81
|
+
free(intSolutions);
|
82
82
|
|
83
|
-
|
83
|
+
return rbSolutions;
|
84
84
|
}
|
85
85
|
|
@@ -3,197 +3,225 @@
|
|
3
3
|
#include <stdlib.h>
|
4
4
|
#include <stdio.h>
|
5
5
|
|
6
|
+
static int * adjust_coeffs_to_mod(int degree, int * coeffs, int mod);
|
6
7
|
static int * solve_prime_power_congruence(int degree, int coeffs[], int prime, int power);
|
7
8
|
static int * solve_system_of_order_1_congruence_sets(int numOfSets, int * lengthsOfSets, int ** sets, int mods[]);
|
8
9
|
|
9
10
|
int chinese_remainder_solution(int numberOfEquations, int scals[], int mods[]){
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
int i;
|
12
|
+
int x = 0;
|
13
|
+
int m = mods[0];
|
14
|
+
int modCoeff;
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
for(i=1; i<numberOfEquations; i++){
|
17
|
+
m *= mods[i];
|
18
|
+
}
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
for(i=0; i<numberOfEquations; i++){
|
21
|
+
modCoeff = m/mods[i];
|
22
|
+
x += modCoeff*mod_inv(modCoeff % mods[i], mods[i])*scals[i];
|
23
|
+
}
|
23
24
|
|
24
|
-
|
25
|
+
return x % m;
|
25
26
|
}
|
26
27
|
|
27
28
|
|
28
|
-
int *
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
*solutionList = numberOfSolutions;
|
44
|
-
|
45
|
-
return solutionList;
|
29
|
+
int * adjust_coeffs_to_mod(int degree, int * coeffs, int mod){
|
30
|
+
int * adjustedCoeffs = calloc(degree+1, sizeof(int));
|
31
|
+
int i;
|
32
|
+
|
33
|
+
for(i = 0; i <= degree; i++){
|
34
|
+
adjustedCoeffs[i] = coeffs[i] % mod;
|
35
|
+
if(adjustedCoeffs[i] < 0){
|
36
|
+
adjustedCoeffs[i] += mod;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
return adjustedCoeffs;
|
46
41
|
}
|
47
42
|
|
48
43
|
|
49
|
-
|
44
|
+
int * brute_force_congruence(int degree, int coeffs[], int primeMod){
|
45
|
+
//assumes a prime modulus. split congruences of composite modulus into systems of congrueneces
|
46
|
+
//of prime modulus and/or apply the lifting theorem to make use of this function
|
47
|
+
//solve a0x^n + a1x^n-1... = 0 (mod mod) where n is the order a0, a1, ... are coeffieicients
|
48
|
+
//also assumes positive representation of coeffs
|
49
|
+
int * adjustedCoeffs = adjust_coeffs_to_mod(degree, coeffs, primeMod);
|
50
|
+
int * solutionList = calloc(degree+1, sizeof(int));
|
51
|
+
int * solutions = solutionList+1;
|
52
|
+
int numberOfSolutions = 0;
|
53
|
+
int x;
|
50
54
|
|
51
|
-
int * baseSolutionList;
|
52
|
-
int numOfBaseSolutions;
|
53
|
-
int * baseSolutions;
|
54
55
|
|
55
|
-
|
56
|
-
|
56
|
+
for(x = 0; x < primeMod && numberOfSolutions <= degree; x++){
|
57
|
+
if(mod_eval_polynomial(degree, adjustedCoeffs, primeMod, x) == 0){
|
58
|
+
solutions[numberOfSolutions++] = x;
|
59
|
+
}
|
60
|
+
}
|
57
61
|
|
58
|
-
|
59
|
-
int * derivCoeffs;
|
60
|
-
int deriv;
|
61
|
-
long int divFunc;
|
62
|
+
*solutionList = numberOfSolutions;
|
62
63
|
|
63
|
-
|
64
|
-
int currentMod;
|
64
|
+
free(adjustedCoeffs);
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
}
|
66
|
+
return solutionList;
|
67
|
+
}
|
69
68
|
|
70
|
-
baseSolutionList = solve_prime_power_congruence(funcDegree, funcCoeffs, prime, power-1);
|
71
|
-
numOfBaseSolutions = *baseSolutionList;
|
72
|
-
baseSolutions = baseSolutionList+1;
|
73
69
|
|
74
|
-
|
75
|
-
numOfLiftedSolutions = 0;
|
70
|
+
static int * solve_prime_power_congruence(int funcDegree, int funcCoeffs[], int prime, int power){
|
76
71
|
|
77
|
-
|
78
|
-
derivCoeffs = calloc(derivDegree+1, sizeof(int));
|
72
|
+
int * adjustedCoeffs;
|
79
73
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
}
|
74
|
+
int * baseSolutionList;
|
75
|
+
int numOfBaseSolutions;
|
76
|
+
int * baseSolutions;
|
84
77
|
|
85
|
-
|
86
|
-
|
87
|
-
}
|
78
|
+
int * liftedSolutions;
|
79
|
+
int numOfLiftedSolutions;
|
88
80
|
|
81
|
+
int coeff;
|
89
82
|
|
90
|
-
|
91
|
-
|
92
|
-
|
83
|
+
int derivDegree;
|
84
|
+
int * derivCoeffs;
|
85
|
+
int deriv;
|
86
|
+
long int divFunc;
|
93
87
|
|
94
|
-
|
95
|
-
|
96
|
-
liftedSolutions[++numOfLiftedSolutions] = baseSolutions[j] + t*prime;
|
97
|
-
}
|
88
|
+
int i, j, t;
|
89
|
+
int currentMod;
|
98
90
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
}
|
104
|
-
}
|
91
|
+
if(power == 1){
|
92
|
+
baseSolutions = brute_force_congruence(funcDegree, funcCoeffs, prime);
|
93
|
+
return baseSolutions;
|
94
|
+
}
|
105
95
|
|
96
|
+
baseSolutionList = solve_prime_power_congruence(funcDegree, funcCoeffs, prime, power-1);
|
97
|
+
numOfBaseSolutions = *baseSolutionList;
|
98
|
+
baseSolutions = baseSolutionList+1;
|
106
99
|
|
107
|
-
|
100
|
+
liftedSolutions = calloc(prime*numOfBaseSolutions+1, sizeof(int));
|
101
|
+
numOfLiftedSolutions = 0;
|
108
102
|
|
109
|
-
|
110
|
-
|
103
|
+
derivDegree = funcDegree-1;
|
104
|
+
derivCoeffs = calloc(derivDegree+1, sizeof(int));
|
111
105
|
|
112
|
-
|
113
|
-
|
106
|
+
currentMod = prime;
|
107
|
+
for(j = 1; j < power; j++){
|
108
|
+
currentMod *= prime;
|
109
|
+
}
|
114
110
|
|
111
|
+
for(j = 0; j <= derivDegree; j++){
|
112
|
+
coeff = funcCoeffs[j+1] % prime;
|
113
|
+
if(coeff < 0){
|
114
|
+
coeff += prime;
|
115
|
+
}
|
116
|
+
derivCoeffs[j] = mod_product(coeff, j+1, prime);
|
117
|
+
}
|
115
118
|
|
116
|
-
static int * solve_system_of_order_1_congruence_sets(int numOfSets, int * setLengths, int * * sets, int * mods){
|
117
|
-
//allocate perumtation array
|
118
|
-
int * divAry = calloc(numOfSets, sizeof(int));
|
119
|
-
int * scalAry = calloc(numOfSets, sizeof(int));
|
120
|
-
int i, j;
|
121
|
-
int numOfSolutions;
|
122
|
-
int * solutionAry;
|
123
|
-
int * dest;
|
124
|
-
int idx;
|
125
|
-
|
126
|
-
for(i = 0, numOfSolutions = 1; i < numOfSets; i++){
|
127
|
-
divAry[i] = numOfSolutions;
|
128
|
-
numOfSolutions *= setLengths[i];
|
129
|
-
}
|
130
|
-
|
131
|
-
solutionAry = calloc(numOfSolutions+1, sizeof(int));
|
132
|
-
solutionAry[0] = numOfSolutions;
|
133
|
-
dest = solutionAry+1;
|
134
|
-
|
135
|
-
for(i = 0; i < numOfSolutions; i++){
|
136
|
-
for(j = 0; j < numOfSets; j++){
|
137
|
-
idx = (i / divAry[j]) % setLengths[j];
|
138
|
-
scalAry[j] = sets[j][idx];
|
139
|
-
}
|
140
|
-
|
141
|
-
*(dest++) = chinese_remainder_solution(numOfSets, scalAry, mods);
|
142
|
-
}
|
143
|
-
|
144
|
-
return solutionAry;
|
145
|
-
}
|
146
119
|
|
147
|
-
|
148
|
-
int * solutionList;
|
120
|
+
for(j = 0; j < numOfBaseSolutions; j++){
|
149
121
|
|
150
|
-
|
151
|
-
|
152
|
-
int * modFactors = modFactorList+1;
|
122
|
+
deriv = mod_eval_polynomial(derivDegree, derivCoeffs, prime, baseSolutions[j]);
|
123
|
+
divFunc = (eval_polynomial(funcDegree, funcCoeffs, baseSolutions[j]) / (currentMod/prime)) % prime;
|
153
124
|
|
154
|
-
|
155
|
-
|
156
|
-
|
125
|
+
if(deriv % prime != 0){
|
126
|
+
t = (-divFunc*mod_inv(deriv, prime) % prime) + prime;
|
127
|
+
liftedSolutions[++numOfLiftedSolutions] = baseSolutions[j] + t*prime;
|
128
|
+
}
|
157
129
|
|
158
|
-
|
159
|
-
|
130
|
+
else if(divFunc % prime == 0){
|
131
|
+
for(t = 1; t <= prime; t++){
|
132
|
+
liftedSolutions[++numOfLiftedSolutions] = baseSolutions[j] + t*(currentMod/prime);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
160
136
|
|
161
|
-
for(i = 0; i < numOfModFactors; i++){
|
162
|
-
primePowers[i] = modFactors[i];
|
163
|
-
power = 1;
|
164
|
-
|
165
|
-
while(mod % (primePowers[i]*modFactors[i]) == 0){
|
166
|
-
primePowers[i] *= modFactors[i];
|
167
|
-
power++;
|
168
|
-
}
|
169
137
|
|
170
|
-
|
171
|
-
primePowerSolutionLengths[i] = *(primePowerSolutions[i]++);
|
172
|
-
}
|
138
|
+
*liftedSolutions = numOfLiftedSolutions;
|
173
139
|
|
140
|
+
free(derivCoeffs);
|
141
|
+
free(baseSolutionList);
|
174
142
|
|
175
|
-
|
143
|
+
return liftedSolutions;
|
144
|
+
}
|
176
145
|
|
177
|
-
for(i = 0; i < numOfModFactors; i++){
|
178
|
-
free(primePowerSolutions[i] - 1);
|
179
|
-
}
|
180
|
-
free(primePowerSolutionLengths);
|
181
|
-
free(primePowerSolutions);
|
182
|
-
free(primePowers);
|
183
|
-
free(modFactorList);
|
184
146
|
|
185
|
-
|
147
|
+
static int * solve_system_of_order_1_congruence_sets(int numOfSets, int * setLengths, int * * sets, int * mods){
|
148
|
+
//allocate perumtation array
|
149
|
+
int * divAry = calloc(numOfSets, sizeof(int));
|
150
|
+
int * scalAry = calloc(numOfSets, sizeof(int));
|
151
|
+
int i, j;
|
152
|
+
int numOfSolutions;
|
153
|
+
int * solutionAry;
|
154
|
+
int * dest;
|
155
|
+
int idx;
|
156
|
+
|
157
|
+
for(i = 0, numOfSolutions = 1; i < numOfSets; i++){
|
158
|
+
divAry[i] = numOfSolutions;
|
159
|
+
numOfSolutions *= setLengths[i];
|
160
|
+
}
|
161
|
+
|
162
|
+
solutionAry = calloc(numOfSolutions+1, sizeof(int));
|
163
|
+
solutionAry[0] = numOfSolutions;
|
164
|
+
dest = solutionAry+1;
|
165
|
+
|
166
|
+
for(i = 0; i < numOfSolutions; i++){
|
167
|
+
for(j = 0; j < numOfSets; j++){
|
168
|
+
idx = (i / divAry[j]) % setLengths[j];
|
169
|
+
scalAry[j] = sets[j][idx];
|
170
|
+
}
|
171
|
+
|
172
|
+
*(dest++) = chinese_remainder_solution(numOfSets, scalAry, mods);
|
173
|
+
}
|
174
|
+
|
175
|
+
return solutionAry;
|
176
|
+
}
|
177
|
+
|
178
|
+
int * solve_congruence(int funcDegree, int funcCoeffs[], int mod){
|
179
|
+
int * solutionList;
|
180
|
+
|
181
|
+
int * modFactorList = prime_factors(mod);
|
182
|
+
int numOfModFactors = *modFactorList;
|
183
|
+
int * modFactors = modFactorList+1;
|
184
|
+
|
185
|
+
int * * primePowerSolutions = calloc(numOfModFactors, sizeof(int *));
|
186
|
+
int * primePowers = calloc(numOfModFactors, sizeof(int));
|
187
|
+
int * primePowerSolutionLengths = calloc(numOfModFactors, sizeof(int *));
|
188
|
+
|
189
|
+
int power;
|
190
|
+
int i;
|
191
|
+
|
192
|
+
for(i = 0; i < numOfModFactors; i++){
|
193
|
+
primePowers[i] = modFactors[i];
|
194
|
+
power = 1;
|
195
|
+
|
196
|
+
while(mod % (primePowers[i]*modFactors[i]) == 0){
|
197
|
+
primePowers[i] *= modFactors[i];
|
198
|
+
power++;
|
199
|
+
}
|
200
|
+
|
201
|
+
primePowerSolutions[i] = solve_prime_power_congruence(funcDegree, funcCoeffs, modFactors[i], power);
|
202
|
+
primePowerSolutionLengths[i] = *(primePowerSolutions[i]++);
|
203
|
+
}
|
204
|
+
|
205
|
+
solutionList = solve_system_of_order_1_congruence_sets(numOfModFactors, primePowerSolutionLengths, primePowerSolutions, primePowers);
|
206
|
+
|
207
|
+
for(i = 0; i < numOfModFactors; i++){
|
208
|
+
free(primePowerSolutions[i] - 1);
|
209
|
+
}
|
210
|
+
free(primePowerSolutionLengths);
|
211
|
+
free(primePowerSolutions);
|
212
|
+
free(primePowers);
|
213
|
+
free(modFactorList);
|
214
|
+
|
215
|
+
return solutionList;
|
186
216
|
}
|
187
217
|
|
188
218
|
/*
|
189
219
|
int * solve_system_of_congruences(int numOfFuncs, int * funcDegrees, int ** funcCoeffs, int * mods){
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
}
|
199
|
-
*/
|
220
|
+
int i;
|
221
|
+
int * * funcSolutionSets = calloc(numOfFuncs, sizeof(int *));
|
222
|
+
for(i=0; i<numOfFuncs; i++){
|
223
|
+
funcSolutionSets[i] = solve_congruence(funcDegrees[i], funcCoeffs[i], mods[i]);
|
224
|
+
}
|
225
|
+
return solve_system_of_congruence_sets(numOfFuncs, funcSolutionSets, mods);
|
226
|
+
}
|
227
|
+
*/
|
@@ -13,17 +13,4 @@ EXT_C = %w[
|
|
13
13
|
prime_gen.c
|
14
14
|
]
|
15
15
|
|
16
|
-
|
17
|
-
EXT_H.each do |fname|
|
18
|
-
unless File::exist? fname
|
19
|
-
raise "Ext header #{fname} does not exist in #{Dir::pwd}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
EXT_C.each do |fname|
|
24
|
-
unless File::exist? fname
|
25
|
-
raise "Ext file #{fname} does not exist in #{Dir::pwd}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
16
|
create_makefile "congruence_solver/congruence_solver"
|