congruence_solver 0.3.1 → 0.3.2

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 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