congruence_solver 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/Rakefile +15 -12
- data/bench/solve_congruence_bm.rb +7 -57
- data/congruence_solver.gemspec +3 -3
- data/ext/congruence_solver/Makefile +4 -3
- data/ext/congruence_solver/arith_utils.c +11 -85
- data/ext/congruence_solver/arith_utils.h +4 -8
- data/ext/congruence_solver/congruence_solver.c +23 -30
- data/ext/congruence_solver/congruences.c +48 -61
- data/ext/congruence_solver/congruences.h +4 -5
- data/ext/congruence_solver/extconf.rb +11 -1
- data/ext/congruence_solver/prime_gen.c +24 -24
- data/ext/congruence_solver/prime_gen.h +6 -6
- data/lib/congruence_solver/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3add01a35c88d8001cd82b2548555e8823686167
|
4
|
+
data.tar.gz: e703f137d82640832dade1cddc55e327578eda3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8e31f46ac56d4e4f3303363947078e53f307060c4c31359ac95e7c7b80c951db44cf2b4f81acf734a1beaad371f87d4009fe61bcc8ca196d60058349ded7f80
|
7
|
+
data.tar.gz: 07cdad5666f6fb71decfb1bdb304a9fd16cb7b9fe9d0c4ee211959927d98e04f2d41f837fdb7c6c641805a781197a1448f6f6ba00176411bb80d2ebbe4dfb858
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ CongruenceSolver is a gem for solving polynomial congruences. Should you ever ne
|
|
4
4
|
|
5
5
|
## Polynomial Congruences
|
6
6
|
|
7
|
-
Polynomial congruences are the central topic of most elementary number theory and abstract algebra curricula. Similar to an equation, a [congruence](https://en.wikipedia.org/wiki/Modular_equation) is an [equivalence relation](https://en.wikipedia.org/wiki/Equivalence_relation) arising from [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic) (also knowsn as "clock arithmetic"). For example, the idea "5 hours past 8 is 1" is expressed in the congruence ```8 + 5 = 1 mod 12```. A polynomial congruence is simply a congruence involving a polynomial, like ```x + 5 = 1 mod 12```. The problem of solving a congruence is to find all inputs satisfying the congruence, much like solving an equation (in this case, ```x = 8```). Generally speaking, congruences become more difficult to solve as the degree of the polynomial and the modulus grow. Elementary number theory develops tools like [Hensel Lifting](https://en.wikipedia.org/wiki/Hensel%27s_lemma#Hensel_Lifting) for solving polynomial congruences and the [Chinese Remainder Theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem) for solving systems of polynomial congruences. This gem leverages these methods as implemented in C in [
|
7
|
+
Polynomial congruences are the central topic of most elementary number theory and abstract algebra curricula. Similar to an equation, a [congruence](https://en.wikipedia.org/wiki/Modular_equation) is an [equivalence relation](https://en.wikipedia.org/wiki/Equivalence_relation) arising from [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic) (also knowsn as "clock arithmetic"). For example, the idea "5 hours past 8 is 1" is expressed in the congruence ```8 + 5 = 1 mod 12```. A polynomial congruence is simply a congruence involving a polynomial, like ```x + 5 = 1 mod 12```. The problem of solving a congruence is to find all inputs satisfying the congruence, much like solving an equation (in this case, ```x = 8```). Generally speaking, congruences become more difficult to solve as the degree of the polynomial and the modulus grow. Elementary number theory develops tools like [Hensel Lifting](https://en.wikipedia.org/wiki/Hensel%27s_lemma#Hensel_Lifting) for solving polynomial congruences and the [Chinese Remainder Theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem) for solving systems of polynomial congruences. This gem leverages these methods as implemented in C in [congruence_solver](https://github.com/laneb/congruence_solver).
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -42,6 +42,10 @@ mod = 49
|
|
42
42
|
CongruenceSolver.solve_congruence(coeffs, mod).sort #=> [1, 8, 15, 22, 26, 29, 36, 43]
|
43
43
|
```
|
44
44
|
|
45
|
+
## Limitations
|
46
|
+
|
47
|
+
What are the limitations on the size of the numbers, you ask? CongruenceSolver can solve any congruence with a 16 bit degree that 32 bit coefficients and modulus. Of course, Ruby's Bignum can manage arbitrarily large integers without overflow, but the extension that powers has limitations to maintain speed and simplicity.
|
48
|
+
|
45
49
|
## Development
|
46
50
|
|
47
51
|
First, install bundler (`gem install bundler`). Then install this project's dependencies with `bundle install`. Use `bundle exec rake update_ext` to pull and compile the extension. Use `bundle exec rake spec` to run the tests and `bundle exec rake bench` to run the benchmark. To build and install this gem locally, run `bundle exec rake install`.
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require "os"
|
|
5
5
|
|
6
6
|
def verbose_sh_exec(cmd)
|
7
7
|
puts cmd
|
8
|
-
|
8
|
+
system cmd
|
9
9
|
end
|
10
10
|
|
11
11
|
def verbose_rm_files(files_to_rm_ary)
|
@@ -20,14 +20,14 @@ end
|
|
20
20
|
|
21
21
|
# runs the csolve binary
|
22
22
|
task :exe do
|
23
|
-
|
23
|
+
verbose_sh_exec "bin/csolve"
|
24
24
|
end
|
25
25
|
|
26
26
|
#spec runs all RSpec examples
|
27
27
|
RSpec::Core::RakeTask.new :spec
|
28
28
|
|
29
29
|
# run the tests shipped the extension
|
30
|
-
task :ctest
|
30
|
+
task :ctest do
|
31
31
|
verbose_sh_exec "(cd ext/congruence_solver && make test)"
|
32
32
|
end
|
33
33
|
|
@@ -37,11 +37,12 @@ task :test => [:ctest, :spec]
|
|
37
37
|
# workflow
|
38
38
|
Rake::ExtensionTask.new 'congruence_solver' do |ext|
|
39
39
|
ext.lib_dir = "lib/congruence_solver"
|
40
|
+
ext.config_options << "--openmp=-fopenmp"
|
40
41
|
end
|
41
42
|
|
42
43
|
# runs benchmarks
|
43
44
|
task :bench do
|
44
|
-
|
45
|
+
verbose_sh_exec "csolve bench"
|
45
46
|
end
|
46
47
|
|
47
48
|
# download source files for the extension
|
@@ -80,25 +81,27 @@ task :clean do
|
|
80
81
|
end
|
81
82
|
|
82
83
|
# build Ruby gem
|
83
|
-
task :build
|
84
|
+
task :build do
|
84
85
|
gemspec = "congruence_solver.gemspec"
|
85
86
|
verbose_sh_exec "gem build #{gemspec}"
|
86
87
|
end
|
87
88
|
|
88
|
-
|
89
|
-
task :install => [:clean, :update_ext, :test, :build] do
|
89
|
+
def gemfile
|
90
90
|
dot_gem_files = Dir.entries(Dir.pwd).select {|f| f =~ /congruence_solver\-.*\.gem/}
|
91
91
|
if dot_gem_files.empty?
|
92
92
|
STDERR.puts "Failed to build gem. Exiting."
|
93
93
|
elsif dot_gem_files.length > 1
|
94
94
|
STDERR.puts "Error: conflicting .gem files in directory. Exiting."
|
95
95
|
else
|
96
|
-
|
96
|
+
dot_gem_files.first
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
# install gem locally
|
101
|
+
task :install => [:compile_ext, :build] do
|
102
|
+
verbose_sh_exec "gem install #{gemfile}"
|
103
|
+
end
|
104
|
+
|
105
|
+
task :publish => [:clean, :compile_ext, :test, :build] do
|
106
|
+
verbose_sh_exec "gem push #{gemfile}"
|
104
107
|
end
|
@@ -2,70 +2,20 @@ require "congruence_solver"
|
|
2
2
|
require "benchmark"
|
3
3
|
require_relative "./bench_tools.rb"
|
4
4
|
|
5
|
+
COEFFS = [-11, 0, 1, 3, 0, 5, 4, 180, 0, 10]
|
6
|
+
COMPOSITE_MOD = 4837012493
|
7
|
+
PRIME_MOD = 57081391
|
5
8
|
|
6
|
-
|
7
|
-
LARGE_DEG_POLYNOMIAL_COEFFS = [-11, 0, 0, 3, 0, 0, 0, 0, 0, 10]
|
8
|
-
SMALL_MOD = 49
|
9
|
-
MED_MOD = 5104
|
10
|
-
LARGE_MOD = 94122
|
11
|
-
XTRA_LARGE_MOD = 401249
|
12
|
-
XTRA_LARGE_PRIME_MOD = 306893
|
13
|
-
SMALL_FACTORED_LARGE_MOD = 510510
|
14
|
-
|
15
|
-
|
16
|
-
def solve_congruence_brute_force(coeffs, mod)
|
17
|
-
solutions = []
|
18
|
-
|
19
|
-
0.upto(mod) do |x|
|
20
|
-
sum = 0
|
21
|
-
|
22
|
-
coeffs.each_with_index do |coe, exp|
|
23
|
-
sum = (sum + coe*x**exp) % mod
|
24
|
-
end
|
25
|
-
|
26
|
-
if sum == 0 then solutions << x end
|
27
|
-
end
|
28
|
-
|
29
|
-
solutions
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
def bm_solve_congruence(coeffs, mod)
|
9
|
+
def bm_solve_congruence(coeffs, mod)
|
34
10
|
puts "Solving #{polynomial_to_s(coeffs)} = 0 mod #{mod}"
|
35
|
-
|
36
|
-
rb_bf_solutions = solve_congruence_brute_force(coeffs, mod).sort
|
37
|
-
#c_bf_solutions = CongruenceSolver.brute_force(coeffs, mod).sort
|
38
|
-
c_lifting_solutions = CongruenceSolver.lift(coeffs, mod).sort
|
39
|
-
|
40
|
-
unless rb_bf_solutions == c_lifting_solutions #and c_bf_solutions c_lift_solutions
|
41
|
-
puts "Solutions do not match:"
|
42
|
-
puts "Ruby/force solutions #{rb_bf_solutions.inspect}"
|
43
|
-
#puts "C/force solutions #{c_bf_solutions}"
|
44
|
-
puts "C/lifting solutions: #{c_lifting_solutions.inspect}"
|
45
|
-
end
|
46
|
-
|
47
|
-
puts "Time measurements:"
|
48
|
-
|
11
|
+
puts "Measurements:"
|
49
12
|
Benchmark.bmbm do |bm|
|
50
|
-
bm.report("Ruby/force") do
|
51
|
-
solve_congruence_brute_force(coeffs, mod)
|
52
|
-
end
|
53
|
-
=begin
|
54
|
-
bm.report("C/force") do
|
55
|
-
CongruenceSolver.brute_force(coeffs, mod)
|
56
|
-
end
|
57
|
-
=end
|
58
13
|
bm.report("C/lifting") do
|
59
14
|
CongruenceSolver.lift(coeffs, mod)
|
60
15
|
end
|
61
16
|
end
|
62
|
-
|
63
17
|
print "\n\n"
|
64
18
|
end
|
65
19
|
|
66
|
-
|
67
|
-
|
68
|
-
[SMALL_MOD, MED_MOD, LARGE_MOD, XTRA_LARGE_MOD, XTRA_LARGE_PRIME_MOD, SMALL_FACTORED_LARGE_MOD].each do |mod|
|
69
|
-
bm_solve_congruence(coeffs, mod)
|
70
|
-
end
|
71
|
-
end
|
20
|
+
bm_solve_congruence(COEFFS,COMPOSITE_MOD)
|
21
|
+
bm_solve_congruence(COEFFS,PRIME_MOD)
|
data/congruence_solver.gemspec
CHANGED
@@ -24,9 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.extensions << "ext/congruence_solver/extconf.rb"
|
26
26
|
|
27
|
-
spec.add_development_dependency "bundler", "~>
|
28
|
-
spec.add_development_dependency "rake", "~>
|
29
|
-
spec.add_development_dependency "rspec", "~>
|
27
|
+
spec.add_development_dependency "bundler", "~>1.10"
|
28
|
+
spec.add_development_dependency "rake", "~>10.0"
|
29
|
+
spec.add_development_dependency "rspec", "~>3.0"
|
30
30
|
spec.add_development_dependency "rake-compiler", "~>0.9"
|
31
31
|
spec.add_development_dependency "os", "~>0.9"
|
32
32
|
end
|
@@ -1,12 +1,13 @@
|
|
1
|
+
CC = gcc-6 -fopenmp -std=c99
|
1
2
|
|
2
3
|
congruences_test: congruences.c test/congruences_test.c test/congruences_test.h arith_utils.c prime_gen.c
|
3
|
-
|
4
|
+
$(CC) -g prime_gen.c arith_utils.c congruences.c test/congruences_test.c -o congruences_test
|
4
5
|
|
5
6
|
arith_utils_test: arith_utils.c test/arith_utils_test.c test/arith_utils_test.h prime_gen.c
|
6
|
-
|
7
|
+
$(CC) -g prime_gen.c arith_utils.c test/arith_utils_test.c -o arith_utils_test
|
7
8
|
|
8
9
|
prime_gen_test: prime_gen.c test/prime_gen_test.c test/prime_gen_test.h
|
9
|
-
|
10
|
+
$(CC) -g prime_gen.c test/prime_gen_test.c -o prime_gen_test
|
10
11
|
|
11
12
|
test: prime_gen_test arith_utils_test congruences_test
|
12
13
|
./prime_gen_test
|
@@ -4,7 +4,7 @@
|
|
4
4
|
#include "arith_utils.h"
|
5
5
|
|
6
6
|
//Expects 0 <= x,y < mod
|
7
|
-
|
7
|
+
long mod_sum(long x, long y, long mod){
|
8
8
|
if(y >= mod - x){
|
9
9
|
return y - (mod - x);
|
10
10
|
}
|
@@ -15,8 +15,8 @@ int mod_sum(int x, int y, int mod){
|
|
15
15
|
}
|
16
16
|
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
long mod_inv(long n, long mod){
|
19
|
+
long y, a;
|
20
20
|
|
21
21
|
if(n!=0){
|
22
22
|
|
@@ -37,30 +37,9 @@ int mod_inv(int n, int mod){
|
|
37
37
|
}
|
38
38
|
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
int numOfFactors = *n1Factors;
|
44
|
-
int * factors = n1Factors+1;
|
45
|
-
int shareFactor = 0;
|
46
|
-
int i;
|
47
|
-
|
48
|
-
for(i=0; i<numOfFactors; i++){
|
49
|
-
if(n2 % factors[i] == 0){
|
50
|
-
shareFactor = 1;
|
51
|
-
break;
|
52
|
-
}
|
53
|
-
}
|
54
|
-
|
55
|
-
free(n1Factors);
|
56
|
-
|
57
|
-
return !shareFactor;
|
58
|
-
}
|
59
|
-
|
60
|
-
|
61
|
-
int mod_product(int num1, int num2, int mod){
|
62
|
-
int prod = 0;
|
63
|
-
int i;
|
40
|
+
long mod_product(long num1, long num2, long mod){
|
41
|
+
long prod = 0;
|
42
|
+
long i;
|
64
43
|
|
65
44
|
for(i = 0; i < num1; i++){
|
66
45
|
prod = mod_sum(prod, num2, mod);
|
@@ -69,68 +48,15 @@ int mod_product(int num1, int num2, int mod){
|
|
69
48
|
return prod;
|
70
49
|
}
|
71
50
|
|
72
|
-
//expects 0 <= n < mod
|
73
|
-
int mod_power(int n, int power, int mod){
|
74
|
-
int product = n;
|
75
|
-
int i;
|
76
|
-
|
77
|
-
for(i = 1; i < power; i++){
|
78
|
-
product = mod_product(product, n, mod);
|
79
|
-
}
|
80
|
-
|
81
|
-
return product;
|
82
|
-
}
|
83
|
-
|
84
|
-
|
85
|
-
int totient(int n){
|
86
|
-
int * divisorList = prime_factors(n);
|
87
|
-
int listLength = divisorList[0];
|
88
|
-
int * divisors = divisorList+1;
|
89
|
-
int i;
|
90
|
-
|
91
|
-
for(i = 0; i < listLength; i++){
|
92
|
-
n *= (divisors[i] - 1);
|
93
|
-
n /= divisors[i];
|
94
|
-
}
|
95
|
-
|
96
|
-
free(divisorList);
|
97
|
-
|
98
|
-
return n;
|
99
|
-
}
|
100
|
-
|
101
51
|
|
102
|
-
|
103
|
-
|
104
|
-
int mod_eval_polynomial(int degree, int coeffs[], int mod, int x){
|
105
|
-
long tot = coeffs[degree];
|
52
|
+
long mod_eval_polynomial(int degree, long coeffs[], long mod, long x){
|
53
|
+
long long tot = coeffs[degree];
|
106
54
|
int i;
|
107
55
|
|
108
56
|
for(i = degree - 1; i >= 0; i--){
|
109
|
-
tot = (x
|
110
|
-
tot = (tot + coeffs[i]) % mod;
|
57
|
+
tot = ( (long long) tot*x ) % mod;
|
58
|
+
tot = ( (long long) tot + coeffs[i] ) % mod;
|
111
59
|
}
|
112
60
|
|
113
|
-
return (
|
61
|
+
return (long) tot;
|
114
62
|
}
|
115
|
-
|
116
|
-
|
117
|
-
long eval_polynomial(int degree, int coeffs[], int x){
|
118
|
-
long int sum = coeffs[0];
|
119
|
-
long int powx;
|
120
|
-
int i;
|
121
|
-
|
122
|
-
for(i = 1, powx = x; i <= degree; i++, powx*=x){
|
123
|
-
sum += powx*coeffs[i];
|
124
|
-
}
|
125
|
-
|
126
|
-
return sum;
|
127
|
-
}
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
/*
|
133
|
-
int * linear_diophantine_solution(int order, int coeffs[], int scal){
|
134
|
-
|
135
|
-
*=}
|
136
|
-
*/
|
@@ -1,10 +1,6 @@
|
|
1
1
|
#ifndef H_ARITH_UTILS
|
2
2
|
#define H_ARITH_UTILS
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
long eval_polynomial(int degree, int coeffs[], int x);
|
8
|
-
int coprime(int n1, int n2);
|
9
|
-
int totient(int n);
|
10
|
-
#endif
|
3
|
+
long mod_inv(long n, long mod);
|
4
|
+
long mod_product(long n1, long n2, long mod);
|
5
|
+
long mod_eval_polynomial(int degree, long coeffs[], long mod, long x);
|
6
|
+
#endif
|
@@ -9,77 +9,70 @@ VALUE CongruenceSolver = Qnil;
|
|
9
9
|
void Init_congruence_solver();
|
10
10
|
VALUE method_congruence_solver_lift(VALUE self, VALUE funcCoeffs, VALUE mod);
|
11
11
|
VALUE method_congruence_solver_brute_force(VALUE self, VALUE funcoeffs, VALUE mod);
|
12
|
-
//VALUE method_congruence_solver_solve_system_of_congruences(VALUE self, VALUE funcDegreeAry, VALUE aryOfFuncCoeffArys, VALUE modAry);
|
13
|
-
|
14
12
|
|
15
13
|
void Init_congruence_solver(){
|
16
14
|
CongruenceSolver = rb_define_module("CongruenceSolver");
|
17
|
-
|
15
|
+
|
18
16
|
rb_define_singleton_method(CongruenceSolver, "lift",
|
19
17
|
method_congruence_solver_lift, 2);
|
20
18
|
|
21
|
-
rb_define_singleton_method(CongruenceSolver, "brute_force",
|
19
|
+
rb_define_singleton_method(CongruenceSolver, "brute_force",
|
22
20
|
method_congruence_solver_brute_force, 2);
|
23
|
-
|
24
|
-
/*rb_define_singleton_method(CongruenceSolver, "solve_system_of_congruences",
|
25
|
-
method_congruence_solver_solve_system_of_congruence, 3);
|
26
|
-
*/
|
27
21
|
}
|
28
22
|
|
29
23
|
|
30
24
|
VALUE method_congruence_solver_brute_force(VALUE self, VALUE funcCoeffs, VALUE mod){
|
31
25
|
int i;
|
32
|
-
|
26
|
+
long * longSolutions;
|
33
27
|
VALUE rbSolutions;
|
34
|
-
|
28
|
+
long longMod = NUM2LONG(mod);
|
35
29
|
|
36
30
|
int intFuncDegree = RARRAY_LEN(funcCoeffs)-1;
|
37
|
-
|
31
|
+
long * longFuncCoeffs = calloc(intFuncDegree+1, sizeof(long));
|
38
32
|
|
39
33
|
for(i = 0; i <= intFuncDegree; i++){
|
40
|
-
|
34
|
+
longFuncCoeffs[i] = NUM2LONG(rb_ary_entry(funcCoeffs, i));
|
41
35
|
}
|
42
36
|
|
43
|
-
|
44
|
-
rbSolutions = rb_ary_new2(
|
37
|
+
longSolutions = brute_force_congruence(intFuncDegree, longFuncCoeffs, longMod);
|
38
|
+
rbSolutions = rb_ary_new2(longSolutions[0]);
|
45
39
|
|
46
|
-
for(i=0; i<
|
47
|
-
rb_ary_store(rbSolutions, i,
|
40
|
+
for(i=0; i<longSolutions[0]; i++){
|
41
|
+
rb_ary_store(rbSolutions, i, LONG2NUM(longSolutions[i+1]));
|
48
42
|
}
|
49
43
|
|
50
44
|
|
51
|
-
free(
|
52
|
-
free(
|
45
|
+
free(longFuncCoeffs);
|
46
|
+
free(longSolutions);
|
53
47
|
|
54
48
|
return rbSolutions;
|
55
49
|
}
|
56
|
-
|
50
|
+
|
57
51
|
|
58
52
|
VALUE method_congruence_solver_lift(VALUE self, VALUE funcCoeffs, VALUE mod){
|
59
53
|
int i;
|
60
|
-
|
54
|
+
long * longSolutions;
|
61
55
|
VALUE rbSolutions;
|
62
|
-
|
56
|
+
long longMod = NUM2LONG(mod);
|
63
57
|
|
64
58
|
int intFuncDegree = RARRAY_LEN(funcCoeffs)-1;
|
65
|
-
|
59
|
+
long * longFuncCoeffs = calloc(intFuncDegree+1, sizeof(long));
|
66
60
|
|
67
61
|
for(i=0; i<=intFuncDegree; i++){
|
68
|
-
|
62
|
+
longFuncCoeffs[i] = NUM2LONG(rb_ary_entry(funcCoeffs, i));
|
69
63
|
}
|
70
64
|
|
65
|
+
longSolutions = solve_congruence(intFuncDegree, longFuncCoeffs, longMod);
|
71
66
|
|
72
|
-
|
73
|
-
rbSolutions = rb_ary_new2(intSolutions[0]);
|
67
|
+
rbSolutions = rb_ary_new2(longSolutions[0]);
|
74
68
|
|
75
|
-
for(i=0; i<
|
76
|
-
rb_ary_store(rbSolutions, i,
|
69
|
+
for(i=0; i<longSolutions[0]; i++){
|
70
|
+
rb_ary_store(rbSolutions, i, LONG2NUM(longSolutions[i+1]));
|
77
71
|
}
|
78
72
|
|
79
73
|
|
80
|
-
free(
|
81
|
-
free(
|
74
|
+
free(longFuncCoeffs);
|
75
|
+
free(longSolutions);
|
82
76
|
|
83
77
|
return rbSolutions;
|
84
78
|
}
|
85
|
-
|
@@ -3,15 +3,15 @@
|
|
3
3
|
#include <stdlib.h>
|
4
4
|
#include <stdio.h>
|
5
5
|
|
6
|
-
static
|
7
|
-
static
|
8
|
-
static
|
6
|
+
static long * adjust_coeffs_to_mod(int degree, long * coeffs, long mod);
|
7
|
+
static long * solve_prime_power_congruence(int degree, long coeffs[], long prime, int power);
|
8
|
+
static long * solve_system_of_order_1_congruence_sets(int numOfSets, int * lengthsOfSets, long ** sets, long mods[]);
|
9
9
|
|
10
|
-
|
10
|
+
long chinese_remainder_solution(int numberOfEquations, long scals[], long mods[]){
|
11
11
|
int i;
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
long long x = 0;
|
13
|
+
long m = mods[0];
|
14
|
+
long modCoeff;
|
15
15
|
|
16
16
|
for(i=1; i<numberOfEquations; i++){
|
17
17
|
m *= mods[i];
|
@@ -19,15 +19,16 @@ int chinese_remainder_solution(int numberOfEquations, int scals[], int mods[]){
|
|
19
19
|
|
20
20
|
for(i=0; i<numberOfEquations; i++){
|
21
21
|
modCoeff = m/mods[i];
|
22
|
-
x += modCoeff*mod_inv(modCoeff % mods[i], mods[i])*scals[i];
|
22
|
+
x += (long long) modCoeff*mod_inv(modCoeff % mods[i], mods[i])*scals[i] % m;
|
23
|
+
x %= m;
|
23
24
|
}
|
24
25
|
|
25
26
|
return x % m;
|
26
27
|
}
|
27
28
|
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
long * adjust_coeffs_to_mod(int degree, long * coeffs, long mod){
|
31
|
+
long * adjustedCoeffs = calloc(degree+1, sizeof(long));
|
31
32
|
int i;
|
32
33
|
|
33
34
|
for(i = 0; i <= degree; i++){
|
@@ -41,19 +42,19 @@ int * adjust_coeffs_to_mod(int degree, int * coeffs, int mod){
|
|
41
42
|
}
|
42
43
|
|
43
44
|
|
44
|
-
|
45
|
+
long * brute_force_congruence(int degree, long coeffs[], long primeMod){
|
45
46
|
//assumes a prime modulus. split congruences of composite modulus into systems of congrueneces
|
46
47
|
//of prime modulus and/or apply the lifting theorem to make use of this function
|
47
48
|
//solve a0x^n + a1x^n-1... = 0 (mod mod) where n is the order a0, a1, ... are coeffieicients
|
48
49
|
//also assumes positive representation of coeffs
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
long * adjustedCoeffs = adjust_coeffs_to_mod(degree, coeffs, primeMod);
|
51
|
+
long * solutionList = calloc(degree+1, sizeof(long));
|
52
|
+
long * solutions = solutionList+1;
|
52
53
|
int numberOfSolutions = 0;
|
53
|
-
|
54
|
+
long x;
|
54
55
|
|
55
|
-
|
56
|
-
for(x = 0; x < primeMod
|
56
|
+
#pragma omp parallel for
|
57
|
+
for(x = 0; x < primeMod; x++){
|
57
58
|
if(mod_eval_polynomial(degree, adjustedCoeffs, primeMod, x) == 0){
|
58
59
|
solutions[numberOfSolutions++] = x;
|
59
60
|
}
|
@@ -67,26 +68,23 @@ int * brute_force_congruence(int degree, int coeffs[], int primeMod){
|
|
67
68
|
}
|
68
69
|
|
69
70
|
|
70
|
-
static
|
71
|
-
|
72
|
-
int * adjustedCoeffs;
|
73
|
-
|
74
|
-
int * baseSolutionList;
|
71
|
+
static long * solve_prime_power_congruence(int funcDegree, long funcCoeffs[], long prime, int power){
|
72
|
+
long * baseSolutionList;
|
75
73
|
int numOfBaseSolutions;
|
76
|
-
|
74
|
+
long * baseSolutions;
|
77
75
|
|
78
|
-
|
76
|
+
long * liftedSolutions;
|
79
77
|
int numOfLiftedSolutions;
|
80
78
|
|
81
|
-
|
79
|
+
long coeff;
|
82
80
|
|
83
81
|
int derivDegree;
|
84
|
-
|
85
|
-
|
86
|
-
long
|
82
|
+
long * derivCoeffs;
|
83
|
+
long deriv;
|
84
|
+
long func;
|
87
85
|
|
88
86
|
int i, j, t;
|
89
|
-
|
87
|
+
long currentMod;
|
90
88
|
|
91
89
|
if(power == 1){
|
92
90
|
baseSolutions = brute_force_congruence(funcDegree, funcCoeffs, prime);
|
@@ -97,11 +95,11 @@ static int * solve_prime_power_congruence(int funcDegree, int funcCoeffs[], int
|
|
97
95
|
numOfBaseSolutions = *baseSolutionList;
|
98
96
|
baseSolutions = baseSolutionList+1;
|
99
97
|
|
100
|
-
liftedSolutions = calloc(prime*numOfBaseSolutions+1, sizeof(
|
98
|
+
liftedSolutions = calloc(prime*numOfBaseSolutions+1, sizeof(long));
|
101
99
|
numOfLiftedSolutions = 0;
|
102
100
|
|
103
101
|
derivDegree = funcDegree-1;
|
104
|
-
derivCoeffs = calloc(derivDegree+1, sizeof(
|
102
|
+
derivCoeffs = calloc(derivDegree+1, sizeof(long));
|
105
103
|
|
106
104
|
currentMod = prime;
|
107
105
|
for(j = 1; j < power; j++){
|
@@ -120,14 +118,14 @@ static int * solve_prime_power_congruence(int funcDegree, int funcCoeffs[], int
|
|
120
118
|
for(j = 0; j < numOfBaseSolutions; j++){
|
121
119
|
|
122
120
|
deriv = mod_eval_polynomial(derivDegree, derivCoeffs, prime, baseSolutions[j]);
|
123
|
-
|
121
|
+
func = mod_eval_polynomial(funcDegree, funcCoeffs, currentMod, baseSolutions[j]);
|
124
122
|
|
125
123
|
if(deriv % prime != 0){
|
126
|
-
t = (-
|
124
|
+
t = ((-func / (currentMod/prime))*mod_inv(deriv, currentMod) % prime) + prime;
|
127
125
|
liftedSolutions[++numOfLiftedSolutions] = baseSolutions[j] + t*prime;
|
128
126
|
}
|
129
127
|
|
130
|
-
else if(
|
128
|
+
else if(func == 0){
|
131
129
|
for(t = 1; t <= prime; t++){
|
132
130
|
liftedSolutions[++numOfLiftedSolutions] = baseSolutions[j] + t*(currentMod/prime);
|
133
131
|
}
|
@@ -144,14 +142,14 @@ static int * solve_prime_power_congruence(int funcDegree, int funcCoeffs[], int
|
|
144
142
|
}
|
145
143
|
|
146
144
|
|
147
|
-
static
|
145
|
+
static long * solve_system_of_order_1_congruence_sets(int numOfSets, int * setLengths, long * * sets, long * mods){
|
148
146
|
//allocate perumtation array
|
149
|
-
|
150
|
-
|
147
|
+
long * divAry = calloc(numOfSets, sizeof(long));
|
148
|
+
long * scalAry = calloc(numOfSets, sizeof(long));
|
151
149
|
int i, j;
|
152
150
|
int numOfSolutions;
|
153
|
-
|
154
|
-
|
151
|
+
long * solutionAry;
|
152
|
+
long * dest;
|
155
153
|
int idx;
|
156
154
|
|
157
155
|
for(i = 0, numOfSolutions = 1; i < numOfSets; i++){
|
@@ -159,7 +157,7 @@ static int * solve_system_of_order_1_congruence_sets(int numOfSets, int * setLen
|
|
159
157
|
numOfSolutions *= setLengths[i];
|
160
158
|
}
|
161
159
|
|
162
|
-
solutionAry = calloc(numOfSolutions+1, sizeof(
|
160
|
+
solutionAry = calloc(numOfSolutions+1, sizeof(long));
|
163
161
|
solutionAry[0] = numOfSolutions;
|
164
162
|
dest = solutionAry+1;
|
165
163
|
|
@@ -175,24 +173,24 @@ static int * solve_system_of_order_1_congruence_sets(int numOfSets, int * setLen
|
|
175
173
|
return solutionAry;
|
176
174
|
}
|
177
175
|
|
178
|
-
|
179
|
-
|
176
|
+
long * solve_congruence(int funcDegree, long funcCoeffs[], long mod){
|
177
|
+
long * solutionList;
|
180
178
|
|
181
|
-
|
182
|
-
|
183
|
-
|
179
|
+
long * modFactorList = prime_factors(mod);
|
180
|
+
long numOfModFactors = *modFactorList;
|
181
|
+
long * modFactors = modFactorList+1;
|
184
182
|
|
185
|
-
|
186
|
-
|
183
|
+
long * * primePowerSolutions = calloc(numOfModFactors, sizeof(long *));
|
184
|
+
long * primePowers = calloc(numOfModFactors, sizeof(long));
|
187
185
|
int * primePowerSolutionLengths = calloc(numOfModFactors, sizeof(int *));
|
188
186
|
|
189
187
|
int power;
|
190
|
-
int i;
|
188
|
+
int i,j,k;
|
191
189
|
|
192
190
|
for(i = 0; i < numOfModFactors; i++){
|
193
|
-
primePowers[i] = modFactors[i];
|
191
|
+
primePowers[i] = modFactors[i];
|
194
192
|
power = 1;
|
195
|
-
|
193
|
+
|
196
194
|
while(mod % (primePowers[i]*modFactors[i]) == 0){
|
197
195
|
primePowers[i] *= modFactors[i];
|
198
196
|
power++;
|
@@ -214,14 +212,3 @@ int * solve_congruence(int funcDegree, int funcCoeffs[], int mod){
|
|
214
212
|
|
215
213
|
return solutionList;
|
216
214
|
}
|
217
|
-
|
218
|
-
/*
|
219
|
-
int * solve_system_of_congruences(int numOfFuncs, int * funcDegrees, int ** funcCoeffs, int * mods){
|
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
|
-
*/
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#ifndef H_CONGRUENCES
|
2
2
|
#define H_CONGRUENCES
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#endif
|
3
|
+
long chinese_remainder_solution(int numOfEquations, int long[], long mods[]);
|
4
|
+
long * solve_congruence(int funcDegree, long funcCoeffs[], long mod);
|
5
|
+
long * brute_force_congruence(int degree, long coeffs[], long primeMod);
|
6
|
+
#endif
|
@@ -13,4 +13,14 @@ EXT_C = %w[
|
|
13
13
|
prime_gen.c
|
14
14
|
]
|
15
15
|
|
16
|
-
|
16
|
+
omp_opt = arg_config("--openmp")
|
17
|
+
if omp_opt
|
18
|
+
if have_header("omp.h")
|
19
|
+
$CFLAGS += " " + omp_opt
|
20
|
+
$DLDFLAGS += " " + omp_opt
|
21
|
+
else
|
22
|
+
raise "OpenMP unsupported by this compiler"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
create_makefile "congruence_solver/congruence_solver"
|
@@ -7,40 +7,40 @@
|
|
7
7
|
#define FIRST_PRIME 2
|
8
8
|
|
9
9
|
static void expand_prime_list_to_length(int length);
|
10
|
-
static void expand_prime_list_past(
|
11
|
-
static int any_divisors(
|
12
|
-
static
|
10
|
+
static void expand_prime_list_past(long max);
|
11
|
+
static int any_divisors(long * div_list, int length, long num);
|
12
|
+
static long least_divisor(long n);
|
13
13
|
|
14
|
-
static
|
14
|
+
static long * PRIME_LIST = NULL;
|
15
15
|
static int PRIME_LIST_LENGTH = 0;
|
16
16
|
static int PRIME_LIST_MAX_LENGTH = 0;
|
17
|
-
static
|
17
|
+
static long NEXT_INTEGER = FIRST_PRIME;
|
18
18
|
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
long * primes(int length){
|
21
|
+
long * rtrn_list = calloc(length, sizeof(long));
|
22
22
|
|
23
23
|
expand_prime_list_to_length(length);
|
24
24
|
|
25
25
|
if(rtrn_list != NULL){
|
26
|
-
memcpy(rtrn_list, PRIME_LIST, length*sizeof(
|
26
|
+
memcpy(rtrn_list, PRIME_LIST, length*sizeof(long));
|
27
27
|
}
|
28
28
|
|
29
29
|
return rtrn_list;
|
30
30
|
}
|
31
31
|
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
long * primes_upto(long max){
|
34
|
+
long * rtrn_list;
|
35
35
|
int i;
|
36
36
|
|
37
37
|
expand_prime_list_past(max);
|
38
38
|
|
39
39
|
for(i = PRIME_LIST_LENGTH; i > 0; i--){
|
40
40
|
if(PRIME_LIST[i-1] <= max){
|
41
|
-
rtrn_list = calloc(i+1, sizeof(
|
41
|
+
rtrn_list = calloc(i+1, sizeof(long));
|
42
42
|
rtrn_list[0] = i;
|
43
|
-
memcpy(rtrn_list+1, PRIME_LIST, i*sizeof(
|
43
|
+
memcpy(rtrn_list+1, PRIME_LIST, i*sizeof(long));
|
44
44
|
return rtrn_list;
|
45
45
|
}
|
46
46
|
}
|
@@ -49,14 +49,14 @@ int * primes_upto(int max){
|
|
49
49
|
}
|
50
50
|
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
long * prime_factors(long n){
|
53
|
+
long * rtrn_list = malloc(sizeof(long));
|
54
54
|
int rtrn_list_length = 0;
|
55
|
-
|
55
|
+
long least_div;
|
56
56
|
|
57
57
|
while(n != 1){
|
58
58
|
least_div = least_divisor(n);
|
59
|
-
rtrn_list = realloc(rtrn_list, (rtrn_list_length+2)*sizeof(
|
59
|
+
rtrn_list = realloc(rtrn_list, (rtrn_list_length+2)*sizeof(long));
|
60
60
|
rtrn_list[++rtrn_list_length] = least_div;
|
61
61
|
|
62
62
|
if(least_div == n){
|
@@ -74,9 +74,9 @@ int * prime_factors(int n){
|
|
74
74
|
}
|
75
75
|
|
76
76
|
|
77
|
-
static
|
77
|
+
static long least_divisor(long n){
|
78
78
|
//Calculate maximum for least divisor (sqrt)
|
79
|
-
|
79
|
+
long least_div_max = sqrt(n) + 1;
|
80
80
|
int i;
|
81
81
|
//Expand prime list up to the least divisor
|
82
82
|
expand_prime_list_past(least_div_max);
|
@@ -96,7 +96,7 @@ static void expand_prime_list_to_length(int length){
|
|
96
96
|
|
97
97
|
if(PRIME_LIST_MAX_LENGTH < length){
|
98
98
|
PRIME_LIST_MAX_LENGTH = 2*length;
|
99
|
-
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(
|
99
|
+
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(long));
|
100
100
|
}
|
101
101
|
|
102
102
|
while(PRIME_LIST_LENGTH < length){
|
@@ -110,15 +110,15 @@ static void expand_prime_list_to_length(int length){
|
|
110
110
|
}
|
111
111
|
|
112
112
|
|
113
|
-
static void expand_prime_list_past(
|
113
|
+
static void expand_prime_list_past(long max){
|
114
114
|
if(PRIME_LIST == NULL){
|
115
115
|
//TODO: find better heuristic limit on memory necessary to allocate
|
116
116
|
PRIME_LIST_MAX_LENGTH = (max/2) + 1;
|
117
|
-
PRIME_LIST = calloc(PRIME_LIST_MAX_LENGTH, sizeof(
|
117
|
+
PRIME_LIST = calloc(PRIME_LIST_MAX_LENGTH, sizeof(long));
|
118
118
|
PRIME_LIST[0] = NEXT_INTEGER++;
|
119
119
|
PRIME_LIST_LENGTH = 1;
|
120
120
|
}
|
121
|
-
|
121
|
+
|
122
122
|
while(PRIME_LIST[PRIME_LIST_LENGTH-1] <= max){
|
123
123
|
while(any_divisors(PRIME_LIST, PRIME_LIST_LENGTH, NEXT_INTEGER) ){
|
124
124
|
NEXT_INTEGER++;
|
@@ -126,7 +126,7 @@ static void expand_prime_list_past(int max){
|
|
126
126
|
|
127
127
|
if( PRIME_LIST_MAX_LENGTH <= PRIME_LIST_LENGTH){
|
128
128
|
PRIME_LIST_MAX_LENGTH *= 2;
|
129
|
-
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(
|
129
|
+
PRIME_LIST = realloc(PRIME_LIST, PRIME_LIST_MAX_LENGTH*sizeof(long));
|
130
130
|
}
|
131
131
|
|
132
132
|
PRIME_LIST[PRIME_LIST_LENGTH++] = NEXT_INTEGER;
|
@@ -134,7 +134,7 @@ static void expand_prime_list_past(int max){
|
|
134
134
|
}
|
135
135
|
|
136
136
|
|
137
|
-
static int any_divisors(
|
137
|
+
static int any_divisors(long * div_list, int length, long num){
|
138
138
|
int i;
|
139
139
|
for(i = 0; i < length; i++){
|
140
140
|
if(num % div_list[i] == 0){
|
@@ -1,10 +1,10 @@
|
|
1
1
|
#ifndef H_PRIME_GEN
|
2
2
|
#define H_PRIME_GEN
|
3
|
-
|
3
|
+
long * primes(int length);
|
4
4
|
|
5
|
-
//Generation of primes is currently time-prohibitive when generating up to large maximums.
|
6
|
-
//This should be acceptable for prime factorization because although naive the algorithm is
|
5
|
+
//Generation of primes is currently time-prohibitive when generating up to large maximums.
|
6
|
+
//This should be acceptable for prime factorization because although naive the algorithm is
|
7
7
|
//somewhat optimized to detect relatively large prime factors.
|
8
|
-
|
9
|
-
|
10
|
-
#endif
|
8
|
+
long * primes_upto(long max);
|
9
|
+
long * prime_factors(long n);
|
10
|
+
#endif
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: congruence_solver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|