prime_lib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 648f92351097b6d1df329f8fc3f87497852c098d
4
+ data.tar.gz: 1f641e1d56f54b6f919e08a8fa790b7ffc7289fd
5
+ SHA512:
6
+ metadata.gz: 415051537d123ca4b6329cc39dd249caf9775a187141d9397cdd7f32d619d9a63ea4660dab0ccd8b0a42eae8806b1d4cc7ae3f912ebb4fa4b4466013aa910f33
7
+ data.tar.gz: 964c19325578dc3c77cc9e1d28a21b1958911f4357851e8898b9854f5c9231cf8271058e956d14158281311d7f25e3623e48202e92d12b9d6155f05678b4109f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prime_numbers.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ prime_lib (0.1.0)
5
+ RubyInline (~> 3.12.2)
6
+ terminal-table
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ RubyInline (3.12.2)
12
+ ZenTest (~> 4.3)
13
+ ZenTest (4.9.5)
14
+ awesome_print (1.2.0)
15
+ diff-lcs (1.2.5)
16
+ rake (10.1.1)
17
+ rspec (2.14.1)
18
+ rspec-core (~> 2.14.0)
19
+ rspec-expectations (~> 2.14.0)
20
+ rspec-mocks (~> 2.14.0)
21
+ rspec-core (2.14.7)
22
+ rspec-expectations (2.14.4)
23
+ diff-lcs (>= 1.1.3, < 2.0)
24
+ rspec-mocks (2.14.4)
25
+ terminal-table (1.4.5)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ awesome_print
32
+ prime_lib!
33
+ rake
34
+ rspec
data/LICENCE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Adit
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # PrimeLib
2
+
3
+ This gem generates a table of the product of N prime numbers.
4
+ Current version aims to improve its performance on prime numbers retrieval.
5
+
6
+ ## Installation
7
+
8
+ Gemfile:
9
+
10
+ gem 'prime_lib', '~> 0.1.0'
11
+
12
+ ## Usage (in terminal)
13
+
14
+ > puts PrimeLib::GeneratorProduct.new(10).to_table
15
+
16
+
17
+ ## WHAT I'VE DONE
18
+
19
+ 1. Optimization of prime generation based on Sieve of Eratosthenes algorithm
20
+
21
+ Currently my implementation runs slightly faster[1] than sieve generator of Ruby 2.0 Prime lib[2] which has been greatly improved 6 month ago by Yugui[3].
22
+
23
+ [1] Ruby Lib takes roughly 1 sec to calculate 200,000 primes, my lib takes 0.6 secs
24
+ [2] https://github.com/ruby/ruby/blob/trunk/lib/prime.rb
25
+ [3] https://github.com/ruby/ruby/commit/def83bff91d59fa5fdd1898f903f9000ad1dbe13#diff-cb376f8d9ff85c3dbf97cd71a7463898
26
+
27
+ 2. To maximize performance gain I decided to run on C code
28
+
29
+
30
+ > PrimeLib::Generator.new(n_samples, engine: PrimeLib::Generator::SieveOfEratosthenesC.new).to_a
31
+
32
+ Known issues:
33
+ - Ruby Inline C compiler throws some[1] warnings I should optimize, meanwhile I've not experienced any trouble (tested till 20_000_000 primes)
34
+ [1] "left-hand operand of comma expression has no effect",
35
+ "passing argument 3 of ‘rb_ary_store’ makes integer from pointer without a cast"
36
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = 'spec/**/*_spec.rb'
7
+ spec.rspec_opts = ['--color']
8
+ end
9
+ task :default => :spec
10
+ rescue LoadError
11
+ nil
12
+ end
@@ -0,0 +1,27 @@
1
+ module PrimeLib
2
+
3
+ module Decorator
4
+
5
+ module Table
6
+
7
+ def lister_keys
8
+ %W(i Prime)
9
+ end
10
+
11
+ def lister
12
+ primes = []
13
+ each_with_index do |i, prime|
14
+ primes << [i, prime.to_i]
15
+ end
16
+ primes
17
+ end
18
+
19
+ def to_table
20
+ ::Terminal::Table.new :headings => lister_keys, :rows => lister
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,27 @@
1
+ module PrimeLib
2
+
3
+ module Decorator
4
+
5
+ module TableProduct
6
+
7
+ def lister_keys
8
+ %W(i Prime Product)
9
+ end
10
+
11
+ def lister
12
+ primes = []
13
+ each_with_index do |i, prime, product|
14
+ primes << [i, prime, product]
15
+ end
16
+ primes
17
+ end
18
+
19
+ def to_table
20
+ ::Terminal::Table.new :headings => lister_keys, :rows => lister
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,25 @@
1
+ module PrimeLib
2
+
3
+ class Generator
4
+
5
+ class GenEngine
6
+
7
+ include Enumerable
8
+
9
+ attr_accessor :max_els
10
+ attr_reader :primes
11
+
12
+ def initialize(max_els = 10)
13
+ @max_els = max_els
14
+ @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
15
+ end
16
+
17
+ def retrieve_primes
18
+ raise 'define me'
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,47 @@
1
+ module PrimeLib
2
+
3
+ class Generator
4
+
5
+ class SieveOfEratosthenes < GenEngine
6
+
7
+ def initialize(max_els = 10)
8
+ super
9
+ @max_checked = @primes.last + 1
10
+ end
11
+
12
+ def retrieve_primes
13
+ next_batch while primes.size <= max_els
14
+ primes.take(max_els)
15
+ end
16
+
17
+ private
18
+
19
+ def next_batch
20
+ max_segment_size = 1e6.to_i
21
+ max_cached_prime = @primes.last
22
+
23
+ segment_min = @max_checked
24
+ segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min
25
+
26
+ root = Math.sqrt(segment_max).floor
27
+
28
+ sieving_primes = @primes[1..-1].take_while { |prime| prime <= root } # 3 5 7 11 13
29
+ segment = ((segment_min + 1) .. segment_max).step(2).to_a
30
+
31
+ sieving_primes.each do |sieving_prime| # 102 103 104 105 106 ~ 3 5 7 11 13 -> 1 1 1 9 7
32
+ cursor = (-(segment_min + 1 + sieving_prime) / 2) % sieving_prime
33
+ while cursor <= segment.size
34
+ segment[cursor] = nil
35
+ cursor += sieving_prime
36
+ end
37
+ end
38
+
39
+ @primes = @primes + segment.compact
40
+ @max_checked = segment_max
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,74 @@
1
+ module PrimeLib
2
+
3
+ class Generator
4
+
5
+ class SieveOfEratosthenesC < GenEngine
6
+
7
+ def initialize(max_els = 10)
8
+ super
9
+ @max_checked = @primes.last + 1
10
+ end
11
+
12
+ def retrieve_primes
13
+ max_segment_size = 1e6.to_i
14
+
15
+ while primes.size <= max_els do
16
+ max_cached_prime = @primes.last
17
+
18
+ segment_min = @max_checked
19
+ segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min
20
+
21
+ @primes = @primes + next_batch(@primes, segment_min, segment_max).reject{|el| el == false }
22
+ @max_checked = segment_max
23
+ end
24
+
25
+ primes.take(max_els)
26
+ end
27
+
28
+ inline(:C) do |builder|
29
+ builder.c <<-EOC
30
+ static VALUE next_batch(VALUE primes, double segment_min, double segment_max)
31
+ {
32
+ int i;
33
+ int root = sqrt(segment_max);
34
+
35
+ VALUE sieving_primes = rb_ary_new();
36
+ int prime;
37
+
38
+ VALUE segment = rb_ary_new();
39
+
40
+ int neg_offset, cursor;
41
+
42
+ for (i = 1; i < RARRAY_LEN(primes), (prime = NUM2DBL(rb_ary_entry(primes, i))) <= root; i++) {
43
+ rb_ary_push(sieving_primes, INT2NUM(prime) );
44
+ }
45
+
46
+ for (i = segment_min + 1; i < segment_max; i += 2) {
47
+ rb_ary_push(segment, INT2NUM(i));
48
+ }
49
+
50
+ for (i = 0; i < RARRAY_LEN(sieving_primes); i++) {
51
+ int sieving_prime = NUM2DBL(rb_ary_entry(sieving_primes, i));
52
+
53
+ neg_offset = (-(segment_min + 1 + sieving_prime) / 2);
54
+ cursor = neg_offset + (int)((neg_offset < 0 ? -neg_offset : neg_offset) / sieving_prime) * sieving_prime;
55
+ if (cursor < 0) {
56
+ cursor += sieving_prime;
57
+ }
58
+
59
+ while(cursor <= RARRAY_LEN(segment)){
60
+ rb_ary_store(segment, cursor, NULL);
61
+ cursor += sieving_prime;
62
+ }
63
+ }
64
+
65
+ return segment;
66
+ }
67
+ EOC
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,32 @@
1
+ module PrimeLib
2
+
3
+ class Generator
4
+ extend Forwardable
5
+
6
+ include PrimeLib::Decorator::Table
7
+
8
+ def_delegators :@engine, :retrieve_primes
9
+
10
+ def initialize(max_els = 100, engine: SieveOfEratosthenes.new)
11
+ @engine = engine
12
+ engine.max_els = max_els
13
+ @max_els = max_els
14
+ end
15
+
16
+ def method_added(method)
17
+ (class<< self;self;end).def_delegator :instance, method
18
+ end
19
+
20
+ def each_with_index
21
+ retrieve_primes.each_with_index do |prime, iprime|
22
+ yield(prime, iprime)
23
+ end
24
+ end
25
+
26
+ def to_a
27
+ retrieve_primes
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,28 @@
1
+ module PrimeLib
2
+
3
+ class GeneratorProduct < Generator
4
+ include PrimeLib::Decorator::TableProduct
5
+
6
+ def initialize(*args)
7
+ super(*args)
8
+ @product = 1
9
+ end
10
+
11
+ def each_with_index
12
+ retrieve_primes.each_with_index do |prime, iprime|
13
+ @product *= prime
14
+ yield(iprime, prime, @product)
15
+ end
16
+ end
17
+
18
+ def to_a
19
+ ret_primes = []
20
+ each_with_index do |iprime, prime, product|
21
+ ret_primes << [iprime, prime, product]
22
+ end
23
+ ret_primes
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,3 @@
1
+ module PrimeLib
2
+ VERSION = "0.1.0"
3
+ end
data/lib/prime_lib.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'inline'
2
+ require 'forwardable'
3
+ require 'terminal-table'
4
+ require 'prime_lib/version'
5
+
6
+ require 'prime_lib/decorator/table'
7
+ require 'prime_lib/decorator/table_product'
8
+
9
+ require 'prime_lib/generator'
10
+ require 'prime_lib/generator_product'
11
+ require 'prime_lib/generator/gen_engine'
12
+ require 'prime_lib/generator/sieve_of_eratosthenes'
13
+ require 'prime_lib/generator/sieve_of_eratosthenes_c'
14
+
15
+ module PrimeLib
16
+
17
+ ROOT_PATH = File.expand_path '../..', __FILE__
18
+
19
+ end
data/prime_lib.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'prime_lib/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "prime_lib"
8
+ gem.version = PrimeLib::VERSION
9
+ gem.authors = ["Adit"]
10
+ gem.email = ["a.saxena@email.it"]
11
+ gem.description = "Write a program that calculates and prints out a multiplication table of the first 10 calculated prime numbers."
12
+ gem.summary = "The program must run from the command line and print to screen 1 table.\nAcross the top and down the left side should be the 10 primes, and the body of the table should contain the product of multiplying these numbers."
13
+
14
+ gem.homepage = "https://bitbucket.org/aditsaxena/prime_lib/src"
15
+
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+
19
+ gem.files = `git ls-files`.split($/)
20
+ # s.files = Dir["[A-Z]*[^~]"] + Dir["lib/**/*.rb"] + Dir["spec/*"] + [".gitignore"]
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+
23
+ gem.require_paths = ["lib"]
24
+
25
+ gem.add_dependency "RubyInline", "~> 3.12.2"
26
+
27
+ gem.add_dependency "terminal-table"
28
+
29
+ gem.add_development_dependency "rake"
30
+ gem.add_development_dependency "rspec"
31
+ gem.add_development_dependency "awesome_print"
32
+
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe "PrimeLib" do
4
+
5
+ it "Prints a table of first N primes" do
6
+ primelib = PrimeLib::Generator.new(10)
7
+ puts primelib.to_table
8
+ end
9
+
10
+ it "Prints a table of first N primes products" do
11
+ primelib = PrimeLib::GeneratorProduct.new(10)
12
+ puts primelib.to_table
13
+ end
14
+
15
+ it "Prints a table of first N primes products", focus: true do
16
+ primelib = PrimeLib::GeneratorProduct.new(10, engine: PrimeLib::Generator::SieveOfEratosthenesC.new)
17
+ puts primelib.to_table
18
+ end
19
+
20
+ # primes = PrimeLib::Generator.new(n_samples, engine: PrimeLib::Generator::SieveOfEratosthenesC.new).to_a
21
+
22
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe PrimeLib::Generator do
4
+
5
+ it "provides a multiplication list of the first N primes" do
6
+ qty = 30
7
+ qty.should be > 1
8
+
9
+ primes_prods = PrimeLib::GeneratorProduct.new(qty).to_a
10
+ manual_prod = 1
11
+
12
+ (qty - 1).times do |i|
13
+ manual_prod = manual_prod * primes_prods[i][1]
14
+ expect(manual_prod).to eq primes_prods[i][2]
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe PrimeLib::Generator do
4
+
5
+ let(:first_primes) {
6
+ # http://primes.utm.edu/lists/small/1000.txt
7
+ %W( 2 3 5 7 11 13 17 19 23 29
8
+ 31 37 41 43 47 53 59 61 67 71
9
+ 73 79 83 89 97 101 103 107 109 113
10
+ 127 131 137 139 149 151 157 163 167 173
11
+ 179 181 191 193 197 199 211 223 227 229
12
+ 233 239 241 251 257 263 269 271 277 281
13
+ 283 293 307 311 313 317 331 337 347 349
14
+ 353 359 367 373 379 383 389 397 401 409
15
+ 419 421 431 433 439 443 449 457 461 463
16
+ 467 479 487 491 499 503 509 521 523 541
17
+ 547 557 563 569 571 577 587 593 599 601
18
+ 607 613 617 619 631 641 643 647 653 659
19
+ 661 673 677 683 691 701 709 719 727 733
20
+ 739 743 751 757 761 769 773 787 797 809
21
+ 811 821 823 827 829 839 853 857 859 863
22
+ 877 881 883 887 907 911 919 929 937 941
23
+ 947 953 967 971 977 983 991 997 1009 1013
24
+ 1019 1021 1031 1033 1039 1049 1051 1061 1063 1069
25
+ 1087 1091 1093 1097 1103 1109 1117 1123 1129 1151
26
+ 1153 1163 1171 1181 1187 1193 1201 1213 1217 1223
27
+ 1229 1231 1237 1249 1259 1277 1279 1283 1289 1291
28
+ 1297 1301 1303 1307 1319 1321 1327 1361 1367 1373
29
+ 1381 1399 1409 1423 1427 1429 1433 1439 1447 1451
30
+ 1453 1459 1471 1481 1483 1487 1489 1493 1499 1511
31
+ 1523 1531 1543 1549 1553 1559 1567 1571 1579 1583
32
+ 1597 1601 1607 1609 1613 1619 1621 1627 1637 1657
33
+ 1663 1667 1669 1693 1697 1699 1709 1721 1723 1733 ).map{ |el| el.to_i }
34
+ }
35
+
36
+ it "fetches N primes, test with a precalculated list" do
37
+ n_samples = 100
38
+ primes = PrimeLib::Generator.new(n_samples).to_a
39
+ expect(primes).to eq first_primes.take(n_samples)
40
+ end
41
+
42
+ it "fetches N primes, with C code" do
43
+ n_samples = 100
44
+ primes = PrimeLib::Generator.new(n_samples, engine: PrimeLib::Generator::SieveOfEratosthenesC.new).to_a
45
+ expect(primes.to_a).to eq first_primes.take(n_samples)
46
+ end
47
+
48
+ it "fetches N primes, test with regex" do
49
+ primes = PrimeLib::Generator.new(1000).to_a
50
+
51
+ for i in 0..1000
52
+ is_prime = primes.include?(i)
53
+ expect(RegexPrime.is_prime? i).to eq is_prime
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe "PrimeLib" do
4
+
5
+ let(:prime_200_000th) { 2750159 } # http://is.2750159.aprimenumber.com/
6
+
7
+ it "compares Ruby 2.0 Prime class" do
8
+ std_class_prime = Prime::EratosthenesGenerator.new
9
+ std_class_last = 200_000.times.map { std_class_prime.next }.last
10
+ expect(std_class_last).to eq prime_200_000th
11
+ end
12
+
13
+ it "produces same values as standard Ruby 2.0 Prime class" do
14
+ primelib = PrimeLib::Generator.new(200_000)
15
+ primelib_last = primelib.to_a.last
16
+ expect(primelib_last).to eq prime_200_000th
17
+ end
18
+
19
+ it "can speed its calculation time piping directly C code" do
20
+ primelib = PrimeLib::Generator.new(200_000, engine: PrimeLib::Generator::SieveOfEratosthenesC.new)
21
+
22
+ primelib_last = primelib.to_a.last
23
+ expect(primelib_last).to eq prime_200_000th
24
+ end
25
+
26
+ it "have an performance improvement over its competitor" do
27
+ Benchmark.bm(1) do |bm|
28
+ bm.report("Prime Ruby 2.0 "){ prime = Prime::EratosthenesGenerator.new ; 200_000.times.map { prime.next }.last }
29
+ bm.report("My implem. in ruby "){ primelib = PrimeLib::Generator.new(200_000) ; primelib.to_a.last }
30
+ bm.report("My implem. inline C"){ primelib = PrimeLib::Generator.new(200_000, engine: PrimeLib::Generator::SieveOfEratosthenesC.new) ; primelib.to_a.last }
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require "benchmark"
5
+ require "awesome_print"
6
+ require "prime_lib"
7
+
8
+ Dir["#{PrimeLib::ROOT_PATH}/spec/support/**/*.rb"].each { |f| require f }
9
+
10
+ RSpec.configure do |config|
11
+ config.order = "random" # --seed 1234
12
+
13
+ config.filter_run :focus => true
14
+ config.run_all_when_everything_filtered = true
15
+
16
+ # Coloring stuff
17
+ config.color_enabled = true # Use color in STDOUT
18
+ config.tty = true
19
+
20
+ # config.include MyTestingHelpers #, :type => :controller # http://stackoverflow.com/questions/20614213/how-to-include-a-support-file-in-rails-4-for-rspec
21
+
22
+ end
@@ -0,0 +1,500 @@
1
+ # https://github.com/ruby/ruby/blob/trunk/lib/prime.rb
2
+ #
3
+ # = prime.rb
4
+ #
5
+ # Prime numbers and factorization library.
6
+ #
7
+ # Copyright::
8
+ # Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.)
9
+ # Copyright (c) 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp>
10
+ #
11
+ # Documentation::
12
+ # Yuki Sonoda
13
+ #
14
+
15
+ require "singleton"
16
+ require "forwardable"
17
+
18
+ class Integer
19
+ # Re-composes a prime factorization and returns the product.
20
+ #
21
+ # See Prime#int_from_prime_division for more details.
22
+ def Integer.from_prime_division(pd)
23
+ Prime.int_from_prime_division(pd)
24
+ end
25
+
26
+ # Returns the factorization of +self+.
27
+ #
28
+ # See Prime#prime_division for more details.
29
+ def prime_division(generator = Prime::Generator23.new)
30
+ Prime.prime_division(self, generator)
31
+ end
32
+
33
+ # Returns true if +self+ is a prime number, false for a composite.
34
+ def prime?
35
+ Prime.prime?(self)
36
+ end
37
+
38
+ # Iterates the given block over all prime numbers.
39
+ #
40
+ # See +Prime+#each for more details.
41
+ def Integer.each_prime(ubound, &block) # :yields: prime
42
+ Prime.each(ubound, &block)
43
+ end
44
+ end
45
+
46
+ #
47
+ # The set of all prime numbers.
48
+ #
49
+ # == Example
50
+ #
51
+ # Prime.each(100) do |prime|
52
+ # p prime #=> 2, 3, 5, 7, 11, ...., 97
53
+ # end
54
+ #
55
+ # Prime is Enumerable:
56
+ #
57
+ # Prime.first 5 # => [2, 3, 5, 7, 11]
58
+ #
59
+ # == Retrieving the instance
60
+ #
61
+ # +Prime+.new is obsolete. Now +Prime+ has the default instance and you can
62
+ # access it as +Prime+.instance.
63
+ #
64
+ # For convenience, each instance method of +Prime+.instance can be accessed
65
+ # as a class method of +Prime+.
66
+ #
67
+ # e.g.
68
+ # Prime.instance.prime?(2) #=> true
69
+ # Prime.prime?(2) #=> true
70
+ #
71
+ # == Generators
72
+ #
73
+ # A "generator" provides an implementation of enumerating pseudo-prime
74
+ # numbers and it remembers the position of enumeration and upper bound.
75
+ # Furthermore, it is an external iterator of prime enumeration which is
76
+ # compatible with an Enumerator.
77
+ #
78
+ # +Prime+::+PseudoPrimeGenerator+ is the base class for generators.
79
+ # There are few implementations of generator.
80
+ #
81
+ # [+Prime+::+EratosthenesGenerator+]
82
+ # Uses eratosthenes' sieve.
83
+ # [+Prime+::+TrialDivisionGenerator+]
84
+ # Uses the trial division method.
85
+ # [+Prime+::+Generator23+]
86
+ # Generates all positive integers which are not divisible by either 2 or 3.
87
+ # This sequence is very bad as a pseudo-prime sequence. But this
88
+ # is faster and uses much less memory than the other generators. So,
89
+ # it is suitable for factorizing an integer which is not large but
90
+ # has many prime factors. e.g. for Prime#prime? .
91
+
92
+ class Prime
93
+ include Enumerable
94
+ @the_instance = Prime.new
95
+
96
+ # obsolete. Use +Prime+::+instance+ or class methods of +Prime+.
97
+ def initialize
98
+ @generator = EratosthenesGenerator.new
99
+ extend OldCompatibility
100
+ warn "Prime::new is obsolete. use Prime::instance or class methods of Prime."
101
+ end
102
+
103
+ class << self
104
+ extend Forwardable
105
+ include Enumerable
106
+ # Returns the default instance of Prime.
107
+ def instance; @the_instance end
108
+
109
+ def method_added(method) # :nodoc:
110
+ (class<< self;self;end).def_delegator :instance, method
111
+ end
112
+ end
113
+
114
+ # Iterates the given block over all prime numbers.
115
+ #
116
+ # == Parameters
117
+ #
118
+ # +ubound+::
119
+ # Optional. An arbitrary positive number.
120
+ # The upper bound of enumeration. The method enumerates
121
+ # prime numbers infinitely if +ubound+ is nil.
122
+ # +generator+::
123
+ # Optional. An implementation of pseudo-prime generator.
124
+ #
125
+ # == Return value
126
+ #
127
+ # An evaluated value of the given block at the last time.
128
+ # Or an enumerator which is compatible to an +Enumerator+
129
+ # if no block given.
130
+ #
131
+ # == Description
132
+ #
133
+ # Calls +block+ once for each prime number, passing the prime as
134
+ # a parameter.
135
+ #
136
+ # +ubound+::
137
+ # Upper bound of prime numbers. The iterator stops after it
138
+ # yields all prime numbers p <= +ubound+.
139
+ #
140
+ # == Note
141
+ #
142
+ # +Prime+.+new+ returns an object extended by +Prime+::+OldCompatibility+
143
+ # in order to be compatible with Ruby 1.8, and +Prime+#each is overwritten
144
+ # by +Prime+::+OldCompatibility+#+each+.
145
+ #
146
+ # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply
147
+ # +Prime+.+each+.
148
+ def each(ubound = nil, generator = EratosthenesGenerator.new, &block)
149
+ generator.upper_bound = ubound
150
+ generator.each(&block)
151
+ end
152
+
153
+
154
+ # Returns true if +value+ is prime, false for a composite.
155
+ #
156
+ # == Parameters
157
+ #
158
+ # +value+:: an arbitrary integer to be checked.
159
+ # +generator+:: optional. A pseudo-prime generator.
160
+ def prime?(value, generator = Prime::Generator23.new)
161
+ value = -value if value < 0
162
+ return false if value < 2
163
+ for num in generator
164
+ q,r = value.divmod num
165
+ return true if q < num
166
+ return false if r == 0
167
+ end
168
+ end
169
+
170
+ # Re-composes a prime factorization and returns the product.
171
+ #
172
+ # == Parameters
173
+ # +pd+:: Array of pairs of integers. The each internal
174
+ # pair consists of a prime number -- a prime factor --
175
+ # and a natural number -- an exponent.
176
+ #
177
+ # == Example
178
+ # For <tt>[[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]]</tt>, it returns:
179
+ #
180
+ # p_1**e_1 * p_2**e_2 * .... * p_n**e_n.
181
+ #
182
+ # Prime.int_from_prime_division([[2,2], [3,1]]) #=> 12
183
+ def int_from_prime_division(pd)
184
+ pd.inject(1){|value, (prime, index)|
185
+ value *= prime**index
186
+ }
187
+ end
188
+
189
+ # Returns the factorization of +value+.
190
+ #
191
+ # == Parameters
192
+ # +value+:: An arbitrary integer.
193
+ # +generator+:: Optional. A pseudo-prime generator.
194
+ # +generator+.succ must return the next
195
+ # pseudo-prime number in the ascending
196
+ # order. It must generate all prime numbers,
197
+ # but may also generate non prime numbers too.
198
+ #
199
+ # === Exceptions
200
+ # +ZeroDivisionError+:: when +value+ is zero.
201
+ #
202
+ # == Example
203
+ # For an arbitrary integer:
204
+ #
205
+ # n = p_1**e_1 * p_2**e_2 * .... * p_n**e_n,
206
+ #
207
+ # prime_division(n) returns:
208
+ #
209
+ # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]].
210
+ #
211
+ # Prime.prime_division(12) #=> [[2,2], [3,1]]
212
+ #
213
+ def prime_division(value, generator = Prime::Generator23.new)
214
+ raise ZeroDivisionError if value == 0
215
+ if value < 0
216
+ value = -value
217
+ pv = [[-1, 1]]
218
+ else
219
+ pv = []
220
+ end
221
+ for prime in generator
222
+ count = 0
223
+ while (value1, mod = value.divmod(prime)
224
+ mod) == 0
225
+ value = value1
226
+ count += 1
227
+ end
228
+ if count != 0
229
+ pv.push [prime, count]
230
+ end
231
+ break if value1 <= prime
232
+ end
233
+ if value > 1
234
+ pv.push [value, 1]
235
+ end
236
+ return pv
237
+ end
238
+
239
+ # An abstract class for enumerating pseudo-prime numbers.
240
+ #
241
+ # Concrete subclasses should override succ, next, rewind.
242
+ class PseudoPrimeGenerator
243
+ include Enumerable
244
+
245
+ def initialize(ubound = nil)
246
+ @ubound = ubound
247
+ end
248
+
249
+ def upper_bound=(ubound)
250
+ @ubound = ubound
251
+ end
252
+ def upper_bound
253
+ @ubound
254
+ end
255
+
256
+ # returns the next pseudo-prime number, and move the internal
257
+ # position forward.
258
+ #
259
+ # +PseudoPrimeGenerator+#succ raises +NotImplementedError+.
260
+ def succ
261
+ raise NotImplementedError, "need to define `succ'"
262
+ end
263
+
264
+ # alias of +succ+.
265
+ def next
266
+ raise NotImplementedError, "need to define `next'"
267
+ end
268
+
269
+ # Rewinds the internal position for enumeration.
270
+ #
271
+ # See +Enumerator+#rewind.
272
+ def rewind
273
+ raise NotImplementedError, "need to define `rewind'"
274
+ end
275
+
276
+ # Iterates the given block for each prime number.
277
+ def each(&block)
278
+ return self.dup unless block
279
+ if @ubound
280
+ last_value = nil
281
+ loop do
282
+ prime = succ
283
+ break last_value if prime > @ubound
284
+ last_value = block.call(prime)
285
+ end
286
+ else
287
+ loop do
288
+ block.call(succ)
289
+ end
290
+ end
291
+ end
292
+
293
+ # see +Enumerator+#with_index.
294
+ alias with_index each_with_index
295
+
296
+ # see +Enumerator+#with_object.
297
+ def with_object(obj)
298
+ return enum_for(:with_object) unless block_given?
299
+ each do |prime|
300
+ yield prime, obj
301
+ end
302
+ end
303
+ end
304
+
305
+ # An implementation of +PseudoPrimeGenerator+.
306
+ #
307
+ # Uses +EratosthenesSieve+.
308
+ class EratosthenesGenerator < PseudoPrimeGenerator
309
+ def initialize
310
+ @last_prime_index = -1
311
+ super
312
+ end
313
+
314
+ def succ
315
+ @last_prime_index += 1
316
+ EratosthenesSieve.instance.get_nth_prime(@last_prime_index)
317
+ end
318
+
319
+ def curr
320
+ EratosthenesSieve.instance.get_last
321
+ end
322
+
323
+ def rewind
324
+ initialize
325
+ end
326
+ alias next succ
327
+ end
328
+
329
+ # An implementation of +PseudoPrimeGenerator+ which uses
330
+ # a prime table generated by trial division.
331
+ class TrialDivisionGenerator<PseudoPrimeGenerator
332
+ def initialize
333
+ @index = -1
334
+ super
335
+ end
336
+
337
+ def succ
338
+ TrialDivision.instance[@index += 1]
339
+ end
340
+ def rewind
341
+ initialize
342
+ end
343
+ alias next succ
344
+ end
345
+
346
+ # Generates all integers which are greater than 2 and
347
+ # are not divisible by either 2 or 3.
348
+ #
349
+ # This is a pseudo-prime generator, suitable on
350
+ # checking primality of an integer by brute force
351
+ # method.
352
+ class Generator23<PseudoPrimeGenerator
353
+ def initialize
354
+ @prime = 1
355
+ @step = nil
356
+ super
357
+ end
358
+
359
+ def succ
360
+ loop do
361
+ if (@step)
362
+ @prime += @step
363
+ @step = 6 - @step
364
+ else
365
+ case @prime
366
+ when 1; @prime = 2
367
+ when 2; @prime = 3
368
+ when 3; @prime = 5; @step = 2
369
+ end
370
+ end
371
+ return @prime
372
+ end
373
+ end
374
+ alias next succ
375
+ def rewind
376
+ initialize
377
+ end
378
+ end
379
+
380
+ # Internal use. An implementation of prime table by trial division method.
381
+ class TrialDivision
382
+ include Singleton
383
+
384
+ def initialize # :nodoc:
385
+ # These are included as class variables to cache them for later uses. If memory
386
+ # usage is a problem, they can be put in Prime#initialize as instance variables.
387
+
388
+ # There must be no primes between @primes[-1] and @next_to_check.
389
+ @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
390
+ # @next_to_check % 6 must be 1.
391
+ @next_to_check = 103 # @primes[-1] - @primes[-1] % 6 + 7
392
+ @ulticheck_index = 3 # @primes.index(@primes.reverse.find {|n|
393
+ # n < Math.sqrt(@@next_to_check) })
394
+ @ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2
395
+ end
396
+
397
+ # Returns the cached prime numbers.
398
+ def cache
399
+ return @primes
400
+ end
401
+ alias primes cache
402
+ alias primes_so_far cache
403
+
404
+ # Returns the +index+th prime number.
405
+ #
406
+ # +index+ is a 0-based index.
407
+ def [](index)
408
+ while index >= @primes.length
409
+ # Only check for prime factors up to the square root of the potential primes,
410
+ # but without the performance hit of an actual square root calculation.
411
+ if @next_to_check + 4 > @ulticheck_next_squared
412
+ @ulticheck_index += 1
413
+ @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2
414
+ end
415
+ # Only check numbers congruent to one and five, modulo six. All others
416
+
417
+ # are divisible by two or three. This also allows us to skip checking against
418
+ # two and three.
419
+ @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil?
420
+ @next_to_check += 4
421
+ @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil?
422
+ @next_to_check += 2
423
+ end
424
+ return @primes[index]
425
+ end
426
+ end
427
+
428
+ # Internal use. An implementation of eratosthenes' sieve
429
+ class EratosthenesSieve
430
+ include Singleton
431
+
432
+ def initialize
433
+ @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
434
+ # @max_checked must be an even number
435
+ @max_checked = @primes.last + 1
436
+ end
437
+
438
+ def get_last
439
+ @primes.last
440
+ end
441
+
442
+ def get_nth_prime(n)
443
+ compute_primes while @primes.size <= n
444
+ @primes[n]
445
+ end
446
+
447
+ private
448
+ def compute_primes
449
+ # max_segment_size must be an even number
450
+ max_segment_size = 1e6.to_i
451
+ max_cached_prime = @primes.last
452
+ # do not double count primes if #compute_primes is interrupted
453
+ # by Timeout.timeout
454
+ @max_checked = max_cached_prime + 1 if max_cached_prime > @max_checked
455
+
456
+ segment_min = @max_checked
457
+ segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min
458
+ root = Integer(Math.sqrt(segment_max).floor)
459
+
460
+ sieving_primes = @primes[1 .. -1].take_while { |prime| prime <= root }
461
+ offsets = Array.new(sieving_primes.size) do |i|
462
+ (-(segment_min + 1 + sieving_primes[i]) / 2) % sieving_primes[i]
463
+ end
464
+
465
+ segment = ((segment_min + 1) .. segment_max).step(2).to_a
466
+ sieving_primes.each_with_index do |prime, index|
467
+ composite_index = offsets[index]
468
+ while composite_index < segment.size do
469
+ segment[composite_index] = nil
470
+ composite_index += prime
471
+ end
472
+ end
473
+
474
+ segment.each do |prime|
475
+ @primes.push prime unless prime.nil?
476
+ end
477
+ @max_checked = segment_max
478
+ end
479
+ end
480
+
481
+ # Provides a +Prime+ object with compatibility to Ruby 1.8 when instantiated via +Prime+.+new+.
482
+ module OldCompatibility
483
+ # Returns the next prime number and forwards internal pointer.
484
+ def succ
485
+ @generator.succ
486
+ end
487
+ alias next succ
488
+
489
+ # Overwrites Prime#each.
490
+ #
491
+ # Iterates the given block over all prime numbers. Note that enumeration
492
+ # starts from the current position of internal pointer, not rewound.
493
+ def each(&block)
494
+ return @generator.dup unless block_given?
495
+ loop do
496
+ yield succ
497
+ end
498
+ end
499
+ end
500
+ end
@@ -0,0 +1,5 @@
1
+ module RegexPrime
2
+ def self.is_prime?(n)
3
+ ('1' * n) !~ /^1?$|^(11+?)\1+$/
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prime_lib
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: RubyInline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.12.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.12.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: terminal-table
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Write a program that calculates and prints out a multiplication table
84
+ of the first 10 calculated prime numbers.
85
+ email:
86
+ - a.saxena@email.it
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - LICENCE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/prime_lib.rb
98
+ - lib/prime_lib/decorator/table.rb
99
+ - lib/prime_lib/decorator/table_product.rb
100
+ - lib/prime_lib/generator.rb
101
+ - lib/prime_lib/generator/gen_engine.rb
102
+ - lib/prime_lib/generator/sieve_of_eratosthenes.rb
103
+ - lib/prime_lib/generator/sieve_of_eratosthenes_c.rb
104
+ - lib/prime_lib/generator_product.rb
105
+ - lib/prime_lib/version.rb
106
+ - prime_lib.gemspec
107
+ - spec/decorator/table_spec.rb
108
+ - spec/generator/generator_product_spec.rb
109
+ - spec/generator/generator_spec.rb
110
+ - spec/performance/performance_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/support/original_prime.rb
113
+ - spec/support/regex_prime.rb
114
+ homepage: https://bitbucket.org/aditsaxena/prime_lib/src
115
+ licenses: []
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.1.11
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: The program must run from the command line and print to screen 1 table. Across
137
+ the top and down the left side should be the 10 primes, and the body of the table
138
+ should contain the product of multiplying these numbers.
139
+ test_files:
140
+ - spec/decorator/table_spec.rb
141
+ - spec/generator/generator_product_spec.rb
142
+ - spec/generator/generator_spec.rb
143
+ - spec/performance/performance_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/original_prime.rb
146
+ - spec/support/regex_prime.rb