congruence_solver 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0efc553f7421a673f9a66fcb7b3b0940f7f2f82
4
- data.tar.gz: 0146a9f00b2bf9f1ec49ae9180f4b82b817e3bf5
3
+ metadata.gz: d41325020ecfa40cc46d234828df89bc2fb40f49
4
+ data.tar.gz: acde70a6f821e7ec7952ab6db97a7c0fe4d61484
5
5
  SHA512:
6
- metadata.gz: cb9842feb8b1d72d26cfc1b6efbf86dcc357ee1a1c070dedcf6498c6a1c0c33f2cf96a5a9a0db5a224da1a74d751de3d8018dabf5bc9f67ce98126c672ee5e98
7
- data.tar.gz: 3c3bae47bde1f4d7d5cdd91b157b6a4b3e345859af280db80a5a311bf503577d56b70b7fa2a38829a909bb1e4f0dba93bad284c257a6cfb6b980cbd8c97220b6
6
+ metadata.gz: fd914b02d0bb19bec1f30558d3fde308225eafbebc11029d0d2fe0ef6f1c91f31cfb03896596cdbd8da194a176bb5cad7bcf5cf339f21fed0ba1377afaeeff62
7
+ data.tar.gz: af76d6607a3acf0c327c1d60e9daf7fbbca921bc1877dde9803928d49a4131bd62975766ac8f7a2f3452153da5a6be4b8c6549de4a55c4292c3edcf0aaac5422
data/.gitignore CHANGED
@@ -1,6 +1,9 @@
1
- Gemfile.lock
2
- tmp/
3
- todo
4
- lib/congruence_solver/congruence_solver.so
5
- *.gem
6
- *.html
1
+ Gemfile.lock
2
+ tmp/
3
+ todo
4
+ lib/congruence_solver/congruence_solver.bundle
5
+ *.gem
6
+ *.html
7
+ ext/congruence_solver/*
8
+ !ext/congruence_solver/extconf.rb
9
+ !ext/congruence_solver/congruence_solver.c
data/.gitmodules CHANGED
@@ -1,4 +0,0 @@
1
- [submodule "ext/congruence_solver"]
2
- path = ext/congruence_solver
3
- url = https://github.com/laneb/congruence_solver_ext.git
4
- branch = master
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
- --color
2
- --require spec_helper
1
+ --color
2
+ --require spec_helper
data/README.md CHANGED
@@ -1,64 +1,51 @@
1
- # CongruenceSolver
2
-
3
- CongruenceSolver is a gem for solving polynomial congruences. Should you ever need to solve polynomial congruences and have Ruby installed, this is the gem for you!
4
-
5
- ## Polynomial Congruences
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 [congruence_solver_ext](https://github.com/laneb/congruence_solver_ext).
8
-
9
- ## Installation
10
-
11
- With [RubyGems](https://rubygems.org/) on your machine, installation is as easy as
12
- ```shell
13
- gem install congruence_solver
14
- ```
15
-
16
- You may also install via [bundler](http://bundler.io/), by adding this line to your application's Gemfile:
17
-
18
- ```ruby
19
- gem 'congruence_solver'
20
- ```
21
-
22
- and executing
23
-
24
- ```shell
25
- $ bundle
26
- ```
27
-
28
- in the project directory.
29
-
30
- ## Usage
31
-
32
- To solve a polynomial congruence at the command line, simply invoke `csolve` and then enter the congruence at the prompt.
33
-
34
- ```
35
- csolve
36
- Congruence to solve:
37
- x^2 + 2x + 1 = x^3 + 3x^5 mod 49
38
- (0) 1
39
- (1) 8
40
- (2) 15
41
- (3) 22
42
- (4) 26
43
- (5) 29
44
- (6) 36
45
- (7) 43
46
- ```
47
-
48
- To use the CongruenceSolver in a Ruby program, use CongruenceSolve::solve_congruence(coeffs, mod), where coeffs is the ascending list of coefficients of the polynomial (congruent to 0) and mod is the modulus of the congruence.
49
-
50
- ```
51
- #solve -3x^5 - x^3 + x^2 + 2x + 1 = 0 mod 49
52
- coeffs = [1, 2, 1, 1, 0, 3]
53
- mod = 49
54
- CongruenceSolver.solve_congruence(coeffs, mod).sort #=> [1, 8, 15, 22, 26, 29, 36, 43]
55
- ```
56
-
57
- ## Development
58
-
59
- After checking out the repo, run `bundle install` to install dependencies and `bundle exec rake update_ext` to pull and compile the extension. Then, run `bundle exec rake spec` to run the tests, or `bundle exec rake bench` to run the benchmark. To build and install this gem onto your local machine, run `bundle exec rake install`.
60
-
61
- ## Contributing
62
-
63
- Bug reports and pull requests are welcome on GitHub at https://github.com/laneb/congruence_solver.
64
-
1
+ # CongruenceSolver
2
+
3
+ CongruenceSolver is a gem for solving polynomial congruences. Should you ever need to solve polynomial congruences, this is the gem for you!
4
+
5
+ ## Polynomial Congruences
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 [congruence_solver_ext](https://github.com/laneb/congruence_solver_ext).
8
+
9
+ ## Installation
10
+
11
+ With [RubyGems](https://rubygems.org/) on your machine, installation is as easy as
12
+ ```shell
13
+ gem install congruence_solver
14
+ ```
15
+
16
+ You may also include this gem in a project with [bundler](http://bundler.io/) or by adding it to your Gemfile.
17
+
18
+ ## Usage
19
+
20
+ To solve a polynomial congruence at the command line, simply invoke `csolve` and then enter the congruence at the prompt.
21
+
22
+ ```
23
+ csolve
24
+ Congruence to solve:
25
+ x^2 + 2x + 1 = x^3 + 3x^5 mod 49
26
+ (0) 1
27
+ (1) 8
28
+ (2) 15
29
+ (3) 22
30
+ (4) 26
31
+ (5) 29
32
+ (6) 36
33
+ (7) 43
34
+ ```
35
+
36
+ To use the CongruenceSolver in a Ruby program, use CongruenceSolve::solve_congruence(coeffs, mod), where coeffs is the ascending list of coefficients of the polynomial (congruent to 0) and mod is the modulus of the congruence.
37
+
38
+ ```
39
+ #solve -3x^5 - x^3 + x^2 + 2x + 1 = 0 mod 49
40
+ coeffs = [1, 2, 1, 1, 0, 3]
41
+ mod = 49
42
+ CongruenceSolver.solve_congruence(coeffs, mod).sort #=> [1, 8, 15, 22, 26, 29, 36, 43]
43
+ ```
44
+
45
+ ## Development
46
+
47
+ 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`.
48
+
49
+ ## Contributing
50
+
51
+ Bug reports and pull requests are welcome on GitHub at https://github.com/laneb/congruence_solver.
data/Rakefile CHANGED
@@ -1,87 +1,107 @@
1
- #require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
- require "rake/extensiontask"
4
- require "os"
5
-
6
-
7
- #exe runs the csolve binary
8
- task :exe do
9
- $LOAD_PATH << "#{Dir.pwd}/lib/"
10
- require_relative "bin/csolve.rb"
11
- end
12
-
13
- #spec runs all RSpec examples
14
- RSpec::Core::RakeTask.new :spec
15
-
16
- #uses task template provided by rake-compiler to run the extension compilation
17
- #workflow. Task name: compile (do not use task name: ext)
18
- Rake::ExtensionTask.new 'congruence_solver' do |ext|
19
- ext.lib_dir = "lib/congruence_solver"
20
- end
21
-
22
- task :bench do
23
- $LOAD_PATH << "#{Dir.pwd}/lib/"
24
- Dir.foreach("bench") do |bm_file|
25
- path = "bench/#{bm_file}"
26
- if File.file? path and path =~ "_bm.rb^"
27
- require_relative path
28
- end
29
- end
30
- end
31
-
32
- #update submodule containing extension.
33
-
34
- task :fetch_ext do
35
- EXT_DIR = "ext/congruence_solver"
36
- `git submodule update --init --remote #{EXT_DIR}`
37
- end
38
-
39
-
40
- #executes compile task defined above, then cleans up the tmp directory that
41
- #rake-compiler leaves behind for some reason
42
- task :compile_ext => :compile do
43
- if OS.windows? then
44
- `rmdir /s /q tmp`
45
- else
46
- `rm -rf tmp`
47
- end
48
- end
49
-
50
-
51
- task :update_ext => [:fetch_ext, :compile_ext]
52
-
53
-
54
- task :clean do
55
- FILES_TO_RM_ARY = %w[congruence_solver-*.gem
56
- lib/congruence_solver/congruence_solver.so]
57
-
58
- file_to_rm_str = FILES_TO_RM_ARY.inject("") {|file_list, file| file_list += file + " "}
59
-
60
- if OS.windows?
61
- `rm -f #{file_to_rm_str}`
62
- else
63
- `rm -f #{file_to_rm_str}`
64
- end
65
- end
66
-
67
-
68
- task :build => [:compile_ext] do
69
- GEMSPEC = "congruence_solver.gemspec"
70
- `gem build #{GEMSPEC}`
71
- end
72
-
73
-
74
- task :install => [:clean, :build] do
75
- dot_gem_files = Dir.entries(Dir.pwd).select {|f| f =~ /congruence_solver\-.*\.gem/}
76
- if dot_gem_files.empty?
77
- STDERR.puts "Failed to build gem. Exiting."
78
- elsif dot_gem_files.length > 1
79
- STDERR.puts "Error: conflicting .gem files in directory. Exiting."
80
- else
81
- `gem install #{dot_gem_files.first}`
82
- end
83
- end
84
-
85
-
86
-
87
-
1
+ #require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+ require "os"
5
+
6
+ def verbose_sh_exec(cmd)
7
+ puts cmd
8
+ `#{cmd}`
9
+ end
10
+
11
+ def verbose_rm_files(files_to_rm_ary)
12
+ if OS.windows?
13
+ rm_cmd = "rm /s /q"
14
+ else
15
+ rm_cmd = "rm -rf"
16
+ end
17
+
18
+ files_to_rm_ary.each {|fname| verbose_sh_exec "#{rm_cmd} #{fname}"}
19
+ end
20
+
21
+ # runs the csolve binary
22
+ task :exe do
23
+ $LOAD_PATH << "#{Dir.pwd}/lib/"
24
+ require_relative "bin/csolve.rb"
25
+ end
26
+
27
+ #spec runs all RSpec examples
28
+ RSpec::Core::RakeTask.new :spec
29
+
30
+ # run the tests shipped the extension
31
+ task :ctest => [:compile_ext] do
32
+ verbose_sh_exec "(cd ext/congruence_solver && make test)"
33
+ end
34
+
35
+ task :test => [:ctest, :spec]
36
+
37
+ # uses task template provided by rake-compiler to run the extension compilation
38
+ # workflow
39
+ Rake::ExtensionTask.new 'congruence_solver' do |ext|
40
+ ext.lib_dir = "lib/congruence_solver"
41
+ end
42
+
43
+ # runs benchmarks
44
+ task :bench do
45
+ $LOAD_PATH << "#{Dir.pwd}/lib/"
46
+ Dir.foreach("bench") do |bm_file|
47
+ path = "bench/#{bm_file}"
48
+ if File.file? path and path =~ "_bm.rb^"
49
+ require_relative path
50
+ end
51
+ end
52
+ end
53
+
54
+ # download source files for the extension
55
+ task :download_ext do
56
+ congruences_lib_url="https://github.com/laneb/congruences/archive/master.zip"
57
+ ext_dir = "ext/congruence_solver"
58
+ verbose_sh_exec "(cd #{ext_dir} && wget #{congruences_lib_url} && unzip master.zip && cp -r congruences-master/* . && rm -rf master.zip congruences-master )"
59
+ end
60
+
61
+
62
+ # executes compile task defined by RSpec, then cleans up the tmp directory that
63
+ # rake-compiler for some reason leaves behind
64
+ task :compile_ext => [:compile] do
65
+ verbose_rm_files ["./tmp"]
66
+ end
67
+
68
+ # remove all the source files for the extension
69
+ task :purge_ext do
70
+ ext_files = `ls ext/congruence_solver`.split("\n").map {|fname| "ext/congruence_solver/" + fname}
71
+ perm_ext_files = `git ls-files ext/congruence_solver`.split("\n")
72
+ files_to_rm = ext_files - perm_ext_files
73
+
74
+ verbose_rm_files files_to_rm
75
+ end
76
+
77
+ task :update_ext => [:purge_ext, :download_ext, :compile_ext]
78
+
79
+ # remove files generated or left behind by build
80
+ task :clean do
81
+ files_to_rm = `find ext/congruence_solver/* -not -path "*.[ch]" -not -path *Makefile -not -path *extconf.rb`.split("\n")
82
+ verbose_rm_files files_to_rm
83
+ end
84
+
85
+ # build Ruby gem
86
+ task :build => [:compile_ext] do
87
+ gemspec = "congruence_solver.gemspec"
88
+ verbose_sh_exec "gem build #{gemspec}"
89
+ end
90
+
91
+ # install gem locally
92
+ task :install => [:clean, :update_ext, :test, :build] do
93
+ dot_gem_files = Dir.entries(Dir.pwd).select {|f| f =~ /congruence_solver\-.*\.gem/}
94
+ if dot_gem_files.empty?
95
+ STDERR.puts "Failed to build gem. Exiting."
96
+ elsif dot_gem_files.length > 1
97
+ STDERR.puts "Error: conflicting .gem files in directory. Exiting."
98
+ else
99
+ verbose_sh_exec "gem install #{dot_gem_files.first}"
100
+ end
101
+ end
102
+
103
+ task :publish => [:clean, :update_ext, :build] do
104
+ cmd = "gem push *.gem"
105
+ p cmd
106
+ system cmd
107
+ end
data/bench/bench_tools.rb CHANGED
@@ -1,33 +1,33 @@
1
-
2
- def polynomial_to_s(coeffs)
3
- polynomial = ""
4
- is_first_term = true
5
-
6
- coeffs.reverse.each_with_index do |coe, idx|
7
- exp = coeffs.length - idx - 1
8
- if coe != 0
9
- if is_first_term
10
- is_first_term = false
11
- else
12
- if coe < 0
13
- polynomial << " - "
14
- else
15
- polynomial << " + "
16
- end
17
- end
18
-
19
- if coe.abs > 1 or (coe.abs == 1 and exp < 2)
20
- polynomial << coe.abs.to_s
21
- end
22
-
23
- if exp > 0
24
- polynomial << "x"
25
- if exp > 1
26
- polynomial << "^#{exp}"
27
- end
28
- end
29
- end
30
- end
31
-
32
- polynomial
1
+
2
+ def polynomial_to_s(coeffs)
3
+ polynomial = ""
4
+ is_first_term = true
5
+
6
+ coeffs.reverse.each_with_index do |coe, idx|
7
+ exp = coeffs.length - idx - 1
8
+ if coe != 0
9
+ if is_first_term
10
+ is_first_term = false
11
+ else
12
+ if coe < 0
13
+ polynomial << " - "
14
+ else
15
+ polynomial << " + "
16
+ end
17
+ end
18
+
19
+ if coe.abs > 1 or (coe.abs == 1 and exp < 2)
20
+ polynomial << coe.abs.to_s
21
+ end
22
+
23
+ if exp > 0
24
+ polynomial << "x"
25
+ if exp > 1
26
+ polynomial << "^#{exp}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ polynomial
33
33
  end
@@ -1,71 +1,71 @@
1
- require "congruence_solver"
2
- require "benchmark"
3
- require_relative "./bench_tools.rb"
4
-
5
-
6
- SMALL_DEG_POLYNOMIAL_COEFFS = [1, -4, 4]
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)
34
- 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
-
49
- 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
- bm.report("C/lifting") do
59
- CongruenceSolver.lift(coeffs, mod)
60
- end
61
- end
62
-
63
- print "\n\n"
64
- end
65
-
66
-
67
- [SMALL_DEG_POLYNOMIAL_COEFFS, LARGE_DEG_POLYNOMIAL_COEFFS].each do |coeffs|
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
1
+ require "congruence_solver"
2
+ require "benchmark"
3
+ require_relative "./bench_tools.rb"
4
+
5
+
6
+ SMALL_DEG_POLYNOMIAL_COEFFS = [1, -4, 4]
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)
34
+ 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
+
49
+ 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
+ bm.report("C/lifting") do
59
+ CongruenceSolver.lift(coeffs, mod)
60
+ end
61
+ end
62
+
63
+ print "\n\n"
64
+ end
65
+
66
+
67
+ [SMALL_DEG_POLYNOMIAL_COEFFS, LARGE_DEG_POLYNOMIAL_COEFFS].each do |coeffs|
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
71
  end