From: Shota Fukumori <redmine@...> Date: 2010-09-10T15:47:31+09:00 Subject: [ruby-dev:42224] [Ruby-Feature#3814][Open] Prime.prime?をC拡張にする Feature #3814: Prime.prime?をC拡張にする http://redmine.ruby-lang.org/issues/show/3814 起票者: Shota Fukumori ステータス: Open, 優先度: Normal カテゴリ: lib 福森 (sora_h)です。 連続した自然数をInteger#timesで流していきそれが素数かを判定するスクリプトを書いたのですが、 Rubyでprime.rbを使って書いたら200万で270秒ほどかかってテストに不便なので、 ためしにPrime.prime?だけC拡張にしてみたら4秒になりました。 時間がかかるメソッドなどはc拡張にしてもいいかもしれません。 http://github.com/sorah/euler/blob/master/problem10.rb 尚、270秒かかるスクリプトはこちらです require 'prime' p (1..2000000).to_a.reject { |x| p x if x % 10000 == 0; !x.prime? }.inject(:+) 以下にgit diff --no-prefixで生成したパッチを掲載します。 diff --git ext/prime/extconf.rb ext/prime/extconf.rb new file mode 100644 index 0000000..c7e6941 --- /dev/null +++ ext/prime/extconf.rb @@ -0,0 +1,3 @@ +require 'mkmf' + +create_makefile('prime') diff --git ext/prime/lib/prime.rb ext/prime/lib/prime.rb new file mode 100644 index 0000000..741164a --- /dev/null +++ ext/prime/lib/prime.rb @@ -0,0 +1,499 @@ +# +# = prime.rb +# +# Prime numbers and factorization library. +# +# Copyright:: +# Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.) +# Copyright (c) 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp> +# +# Documentation:: +# Yuki Sonoda +# + +require "singleton" +require "forwardable" + +class Integer + # Re-composes a prime factorization and returns the product. + # + # See Prime#int_from_prime_division for more details. + def Integer.from_prime_division(pd) + Prime.int_from_prime_division(pd) + end + + # Returns the factorization of +self+. + # + # See Prime#prime_division for more details. + def prime_division(generator = Prime::Generator23.new) + Prime.prime_division(self, generator) + end + + # Returns true if +self+ is a prime number, false for a composite. + def prime? + Prime.prime?(self) + end + + # Iterates the given block over all prime numbers. + # + # See +Prime+#each for more details. + def Integer.each_prime(ubound, &block) # :yields: prime + Prime.each(ubound, &block) + end +end + +# +# The set of all prime numbers. +# +# == Example +# Prime.each(100) do |prime| +# p prime #=> 2, 3, 5, 7, 11, ...., 97 +# end +# +# == Retrieving the instance +# +Prime+.new is obsolete. Now +Prime+ has the default instance and you can +# access it as +Prime+.instance. +# +# For convenience, each instance method of +Prime+.instance can be accessed +# as a class method of +Prime+. +# +# e.g. +# Prime.instance.prime?(2) #=> true +# Prime.prime?(2) #=> true +# +# == Generators +# A "generator" provides an implementation of enumerating pseudo-prime +# numbers and it remembers the position of enumeration and upper bound. +# Futhermore, it is a external iterator of prime enumeration which is +# compatible to an Enumerator. +# +# +Prime+::+PseudoPrimeGenerator+ is the base class for generators. +# There are few implementations of generator. +# +# [+Prime+::+EratosthenesGenerator+] +# Uses eratosthenes's sieve. +# [+Prime+::+TrialDivisionGenerator+] +# Uses the trial division method. +# [+Prime+::+Generator23+] +# Generates all positive integers which is not divided by 2 nor 3. +# This sequence is very bad as a pseudo-prime sequence. But this +# is faster and uses much less memory than other generators. So, +# it is suitable for factorizing an integer which is not large but +# has many prime factors. e.g. for Prime#prime? . +class Prime + include Enumerable + @the_instance = Prime.new + + # obsolete. Use +Prime+::+instance+ or class methods of +Prime+. + def initialize + @generator = EratosthenesGenerator.new + extend OldCompatibility + warn "Prime::new is obsolete. use Prime::instance or class methods of Prime." + end + + class << self + extend Forwardable + include Enumerable + # Returns the default instance of Prime. + def instance; @the_instance end + + def method_added(method) # :nodoc: + (class<< self;self;end).def_delegator :instance, method + end + end + + # Iterates the given block over all prime numbers. + # + # == Parameters + # +ubound+:: + # Optional. An arbitrary positive number. + # The upper bound of enumeration. The method enumerates + # prime numbers infinitely if +ubound+ is nil. + # +generator+:: + # Optional. An implementation of pseudo-prime generator. + # + # == Return value + # An evaluated value of the given block at the last time. + # Or an enumerator which is compatible to an +Enumerator+ + # if no block given. + # + # == Description + # Calls +block+ once for each prime number, passing the prime as + # a parameter. + # + # +ubound+:: + # Upper bound of prime numbers. The iterator stops after + # yields all prime numbers p <= +ubound+. + # + # == Note + # +Prime+.+new+ returns a object extended by +Prime+::+OldCompatibility+ + # in order to compatibility to Ruby 1.8, and +Prime+#each is overwritten + # by +Prime+::+OldCompatibility+#+each+. + # + # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply + # +Prime+.+each+. + def each(ubound = nil, generator = EratosthenesGenerator.new, &block) + generator.upper_bound = ubound + generator.each(&block) + end + + + # Returns true if +value+ is prime, false for a composite. + # + # == Parameters + # +value+:: an arbitrary integer to be checked. + # +generator+:: optional. A pseudo-prime generator. + def prime?(value, generator = Prime::Generator23.new) + value = -value if value < 0 + return false if value < 2 + for num in generator + q,r = value.divmod num + return true if q < num + return false if r == 0 + end + end + + # Re-composes a prime factorization and returns the product. + # + # == Parameters + # +pd+:: Array of pairs of integers. The each internal + # pair consists of a prime number -- a prime factor -- + # and a natural number -- an exponent. + # + # == Example + # For [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]], it returns + # p_1**e_1 * p_2**e_2 * .... * p_n**e_n. + # + # Prime.int_from_prime_division([[2,2], [3,1]]) #=> 12 + def int_from_prime_division(pd) + pd.inject(1){|value, (prime, index)| + value *= prime**index + } + end + + # Returns the factorization of +value+. + # + # == Parameters + # +value+:: An arbitrary integer. + # +generator+:: Optional. A pseudo-prime generator. + # +generator+.succ must return the next + # pseudo-prime number in the ascendent + # order. It must generate all prime numbers, + # but may generate non prime numbers. + # + # === Exceptions + # +ZeroDivisionError+:: when +value+ is zero. + # + # == Example + # For an arbitrary integer + # n = p_1**e_1 * p_2**e_2 * .... * p_n**e_n, + # prime_division(n) returns + # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]]. + # + # Prime.prime_division(12) #=> [[2,2], [3,1]] + # + def prime_division(value, generator= Prime::Generator23.new) + raise ZeroDivisionError if value == 0 + if value < 0 + value = -value + pv = [[-1, 1]] + else + pv = [] + end + for prime in generator + count = 0 + while (value1, mod = value.divmod(prime) + mod) == 0 + value = value1 + count += 1 + end + if count != 0 + pv.push [prime, count] + end + break if value1 <= prime + end + if value > 1 + pv.push [value, 1] + end + return pv + end + + # An abstract class for enumerating pseudo-prime numbers. + # + # Concrete subclasses should override succ, next, rewind. + class PseudoPrimeGenerator + include Enumerable + + def initialize(ubound = nil) + @ubound = ubound + end + + def upper_bound=(ubound) + @ubound = ubound + end + def upper_bound + @ubound + end + + # returns the next pseudo-prime number, and move the internal + # position forward. + # + # +PseudoPrimeGenerator+#succ raises +NotImplementedError+. + def succ + raise NotImplementedError, "need to define `succ'" + end + + # alias of +succ+. + def next + raise NotImplementedError, "need to define `next'" + end + + # Rewinds the internal position for enumeration. + # + # See +Enumerator+#rewind. + def rewind + raise NotImplementedError, "need to define `rewind'" + end + + # Iterates the given block for each prime numbers. + def each(&block) + return self.dup unless block + if @ubound + last_value = nil + loop do + prime = succ + break last_value if prime > @ubound + last_value = block.call(prime) + end + else + loop do + block.call(succ) + end + end + end + + # see +Enumerator+#with_index. + alias with_index each_with_index + + # see +Enumerator+#with_object. + def with_object(obj) + return enum_for(:with_object) unless block_given? + each do |prime| + yield prime, obj + end + end + end + + # An implementation of +PseudoPrimeGenerator+. + # + # Uses +EratosthenesSieve+. + class EratosthenesGenerator < PseudoPrimeGenerator + def initialize + @last_prime = nil + super + end + + def succ + @last_prime = @last_prime ? EratosthenesSieve.instance.next_to(@last_prime) : 2 + end + def rewind + initialize + end + alias next succ + end + + # An implementation of +PseudoPrimeGenerator+ which uses + # a prime table generated by trial division. + class TrialDivisionGenerator<PseudoPrimeGenerator + def initialize + @index = -1 + super + end + + def succ + TrialDivision.instance[@index += 1] + end + def rewind + initialize + end + alias next succ + end + + # Generates all integer which are greater than 2 and + # are not divided by 2 nor 3. + # + # This is a pseudo-prime generator, suitable on + # checking primality of a integer by brute force + # method. + class Generator23<PseudoPrimeGenerator + def initialize + @prime = 1 + @step = nil + super + end + + def succ + loop do + if (@step) + @prime += @step + @step = 6 - @step + else + case @prime + when 1; @prime = 2 + when 2; @prime = 3 + when 3; @prime = 5; @step = 2 + end + end + return @prime + end + end + alias next succ + def rewind + initialize + end + end + + + + + # Internal use. An implementation of prime table by trial division method. + class TrialDivision + include Singleton + + def initialize # :nodoc: + # These are included as class variables to cache them for later uses. If memory + # usage is a problem, they can be put in Prime#initialize as instance variables. + + # There must be no primes between @primes[-1] and @next_to_check. + @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] + # @next_to_check % 6 must be 1. + @next_to_check = 103 # @primes[-1] - @primes[-1] % 6 + 7 + @ulticheck_index = 3 # @primes.index(@primes.reverse.find {|n| + # n < Math.sqrt(@@next_to_check) }) + @ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2 + end + + # Returns the cached prime numbers. + def cache + return @primes + end + alias primes cache + alias primes_so_far cache + + # Returns the +index+th prime number. + # + # +index+ is a 0-based index. + def [](index) + while index >= @primes.length + # Only check for prime factors up to the square root of the potential primes, + # but without the performance hit of an actual square root calculation. + if @next_to_check + 4 > @ulticheck_next_squared + @ulticheck_index += 1 + @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2 + end + # Only check numbers congruent to one and five, modulo six. All others + + # are divisible by two or three. This also allows us to skip checking against + # two and three. + @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? + @next_to_check += 4 + @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? + @next_to_check += 2 + end + return @primes[index] + end + end + + # Internal use. An implementation of eratosthenes's sieve + class EratosthenesSieve + include Singleton + + BITS_PER_ENTRY = 16 # each entry is a set of 16-bits in a Fixnum + NUMS_PER_ENTRY = BITS_PER_ENTRY * 2 # twiced because even numbers are omitted + ENTRIES_PER_TABLE = 8 + NUMS_PER_TABLE = NUMS_PER_ENTRY * ENTRIES_PER_TABLE + FILLED_ENTRY = (1 << NUMS_PER_ENTRY) - 1 + + def initialize # :nodoc: + # bitmap for odd prime numbers less than 256. + # For an arbitrary odd number n, @tables[i][j][k] is + # * 1 if n is prime, + # * 0 if n is composite, + # where i,j,k = indices(n) + @tables = [[0xcb6e, 0x64b4, 0x129a, 0x816d, 0x4c32, 0x864a, 0x820d, 0x2196].freeze] + end + + # returns the least odd prime number which is greater than +n+. + def next_to(n) + n = (n-1).div(2)*2+3 # the next odd number to given n + table_index, integer_index, bit_index = indices(n) + loop do + extend_table until @tables.length > table_index + for j in integer_index...ENTRIES_PER_TABLE + if !@tables[table_index][j].zero? + for k in bit_index...BITS_PER_ENTRY + return NUMS_PER_TABLE*table_index + NUMS_PER_ENTRY*j + 2*k+1 if !@tables[table_index][j][k].zero? + end + end + bit_index = 0 + end + table_index += 1; integer_index = 0 + end + end + + private + # for an odd number +n+, returns (i, j, k) such that @tables[i][j][k] represents primarity of the number + def indices(n) + # binary digits of n: |0|1|2|3|4|5|6|7|8|9|10|11|.... + # indices: |-| k | j | i + # because of NUMS_PER_ENTRY, NUMS_PER_TABLE + + k = (n & 0b00011111) >> 1 + j = (n & 0b11100000) >> 5 + i = n >> 8 + return i, j, k + end + + def extend_table + lbound = NUMS_PER_TABLE * @tables.length + ubound = lbound + NUMS_PER_TABLE + new_table = [FILLED_ENTRY] * ENTRIES_PER_TABLE # which represents primarity in lbound...ubound + (3..Integer(Math.sqrt(ubound))).step(2) do |p| + i, j, k = indices(p) + next if @tables[i][j][k].zero? + + start = (lbound.div(p)+1)*p # least multiple of p which is >= lbound + start += p if start.even? + (start...ubound).step(2*p) do |n| + _, j, k = indices(n) + new_table[j] &= FILLED_ENTRY^(1<<k) + end + end + @tables << new_table.freeze + end + end + + # Provides a +Prime+ object with compatibility to Ruby 1.8 when instantiated via +Prime+.+new+. + module OldCompatibility + # Returns the next prime number and forwards internal pointer. + def succ + @generator.succ + end + alias next succ + + # Overwrites Prime#each. + # + # Iterates the given block over all prime numbers. Note that enumeration starts from + # the current position of internal pointer, not rewound. + def each(&block) + return @generator.dup unless block_given? + loop do + yield succ + end + end + end +end + + +require 'prime.so' + diff --git ext/prime/prime.c ext/prime/prime.c new file mode 100644 index 0000000..9f182a3 --- /dev/null +++ ext/prime/prime.c @@ -0,0 +1,96 @@ +/************************************************ + + prime.c - + + Copyright (C) 2010 Shota Fukumori (sora_h) + +************************************************/ + +#include "ruby/ruby.h" + +VALUE prime_um_value; + +static VALUE +prime_is_value_prime(int argc, VALUE *argv, VALUE self) { + int step23, v, g; + long int i, x; + VALUE value, generator, t, iv; + + rb_scan_args(argc, argv, "11", &value, &generator); + + if(TYPE(value) == T_FLOAT) + return Qfalse; + + if(!(FIXNUM_P(value) || TYPE(value) == T_BIGNUM)) + rb_raise(rb_eTypeError, "value must be a numeric"); + if(!prime_um_value) + prime_um_value = ULONG2NUM(ULONG_MAX); + + if (!FIXNUM_P(value) && rb_funcall(value,rb_intern(">"),1,prime_um_value) == Qtrue){ + v = 1; + if (rb_funcall(value,rb_intern("<"),1,INT2FIX(2)) == Qtrue) + return Qfalse; + if (rb_funcall(value,rb_intern("=="),1,INT2FIX(2)) == Qtrue || + rb_funcall(value,rb_intern("=="),1,INT2FIX(3)) == Qtrue) + return Qtrue; + } else { + v = 0; + x = NUM2LONG(value); + if (x < 0) x = x * -1; + + if (x < 2) return Qfalse; + if (x == 2) return Qtrue; + if (x == 3) return Qtrue; + } + step23 = 0; + i = 1; + while(1) { + if(g = NIL_P(generator)) { + if (step23 < 1) { + switch(i) { + case 1: + i = 2; + break; + case 2: + i = 3; + break; + case 3: + i = 5; + step23 = 2; + break; + } + }else{ + i += step23; + step23 = 6 - step23; + } + }else{ + iv = rb_funcall(generator,rb_intern("succ"),0); + if (!v) i = NUM2ULONG(iv); + } + if (v) { + if (!g) iv = ULONG2NUM(i); + t = rb_funcall(value,rb_intern("divmod"),1,iv); + if (rb_funcall(rb_ary_shift(t),rb_intern("<"),1,iv) == Qtrue) + return Qtrue; + if (rb_funcall(rb_ary_shift(t),rb_intern("=="),1,INT2FIX(0)) == Qtrue) + return Qfalse; + } else { + if (x / i < i) + return Qtrue; + if (x % i == 0) + return Qfalse; + } + } + /*}else{ + i = rb + return Qfalse; NOTE: fix this + }*/ +} + +void +Init_prime(void) { + VALUE rb_cPrime; + rb_cPrime = rb_define_class("Prime", rb_cObject); + + rb_define_singleton_method(rb_cPrime, "prime?", prime_is_value_prime, -1); +} diff --git lib/prime.rb lib/prime.rb deleted file mode 100644 index a40d90e..0000000 --- lib/prime.rb +++ /dev/null @@ -1,495 +0,0 @@ -# -# = prime.rb -# -# Prime numbers and factorization library. -# -# Copyright:: -# Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.) -# Copyright (c) 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp> -# -# Documentation:: -# Yuki Sonoda -# - -require "singleton" -require "forwardable" - -class Integer - # Re-composes a prime factorization and returns the product. - # - # See Prime#int_from_prime_division for more details. - def Integer.from_prime_division(pd) - Prime.int_from_prime_division(pd) - end - - # Returns the factorization of +self+. - # - # See Prime#prime_division for more details. - def prime_division(generator = Prime::Generator23.new) - Prime.prime_division(self, generator) - end - - # Returns true if +self+ is a prime number, false for a composite. - def prime? - Prime.prime?(self) - end - - # Iterates the given block over all prime numbers. - # - # See +Prime+#each for more details. - def Integer.each_prime(ubound, &block) # :yields: prime - Prime.each(ubound, &block) - end -end - -# -# The set of all prime numbers. -# -# == Example -# Prime.each(100) do |prime| -# p prime #=> 2, 3, 5, 7, 11, ...., 97 -# end -# -# == Retrieving the instance -# +Prime+.new is obsolete. Now +Prime+ has the default instance and you can -# access it as +Prime+.instance. -# -# For convenience, each instance method of +Prime+.instance can be accessed -# as a class method of +Prime+. -# -# e.g. -# Prime.instance.prime?(2) #=> true -# Prime.prime?(2) #=> true -# -# == Generators -# A "generator" provides an implementation of enumerating pseudo-prime -# numbers and it remembers the position of enumeration and upper bound. -# Futhermore, it is a external iterator of prime enumeration which is -# compatible to an Enumerator. -# -# +Prime+::+PseudoPrimeGenerator+ is the base class for generators. -# There are few implementations of generator. -# -# [+Prime+::+EratosthenesGenerator+] -# Uses eratosthenes's sieve. -# [+Prime+::+TrialDivisionGenerator+] -# Uses the trial division method. -# [+Prime+::+Generator23+] -# Generates all positive integers which is not divided by 2 nor 3. -# This sequence is very bad as a pseudo-prime sequence. But this -# is faster and uses much less memory than other generators. So, -# it is suitable for factorizing an integer which is not large but -# has many prime factors. e.g. for Prime#prime? . -class Prime - include Enumerable - @the_instance = Prime.new - - # obsolete. Use +Prime+::+instance+ or class methods of +Prime+. - def initialize - @generator = EratosthenesGenerator.new - extend OldCompatibility - warn "Prime::new is obsolete. use Prime::instance or class methods of Prime." - end - - class << self - extend Forwardable - include Enumerable - # Returns the default instance of Prime. - def instance; @the_instance end - - def method_added(method) # :nodoc: - (class<< self;self;end).def_delegator :instance, method - end - end - - # Iterates the given block over all prime numbers. - # - # == Parameters - # +ubound+:: - # Optional. An arbitrary positive number. - # The upper bound of enumeration. The method enumerates - # prime numbers infinitely if +ubound+ is nil. - # +generator+:: - # Optional. An implementation of pseudo-prime generator. - # - # == Return value - # An evaluated value of the given block at the last time. - # Or an enumerator which is compatible to an +Enumerator+ - # if no block given. - # - # == Description - # Calls +block+ once for each prime number, passing the prime as - # a parameter. - # - # +ubound+:: - # Upper bound of prime numbers. The iterator stops after - # yields all prime numbers p <= +ubound+. - # - # == Note - # +Prime+.+new+ returns a object extended by +Prime+::+OldCompatibility+ - # in order to compatibility to Ruby 1.8, and +Prime+#each is overwritten - # by +Prime+::+OldCompatibility+#+each+. - # - # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply - # +Prime+.+each+. - def each(ubound = nil, generator = EratosthenesGenerator.new, &block) - generator.upper_bound = ubound - generator.each(&block) - end - - - # Returns true if +value+ is prime, false for a composite. - # - # == Parameters - # +value+:: an arbitrary integer to be checked. - # +generator+:: optional. A pseudo-prime generator. - def prime?(value, generator = Prime::Generator23.new) - value = -value if value < 0 - return false if value < 2 - for num in generator - q,r = value.divmod num - return true if q < num - return false if r == 0 - end - end - - # Re-composes a prime factorization and returns the product. - # - # == Parameters - # +pd+:: Array of pairs of integers. The each internal - # pair consists of a prime number -- a prime factor -- - # and a natural number -- an exponent. - # - # == Example - # For [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]], it returns - # p_1**e_1 * p_2**e_2 * .... * p_n**e_n. - # - # Prime.int_from_prime_division([[2,2], [3,1]]) #=> 12 - def int_from_prime_division(pd) - pd.inject(1){|value, (prime, index)| - value *= prime**index - } - end - - # Returns the factorization of +value+. - # - # == Parameters - # +value+:: An arbitrary integer. - # +generator+:: Optional. A pseudo-prime generator. - # +generator+.succ must return the next - # pseudo-prime number in the ascendent - # order. It must generate all prime numbers, - # but may generate non prime numbers. - # - # === Exceptions - # +ZeroDivisionError+:: when +value+ is zero. - # - # == Example - # For an arbitrary integer - # n = p_1**e_1 * p_2**e_2 * .... * p_n**e_n, - # prime_division(n) returns - # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]]. - # - # Prime.prime_division(12) #=> [[2,2], [3,1]] - # - def prime_division(value, generator= Prime::Generator23.new) - raise ZeroDivisionError if value == 0 - if value < 0 - value = -value - pv = [[-1, 1]] - else - pv = [] - end - for prime in generator - count = 0 - while (value1, mod = value.divmod(prime) - mod) == 0 - value = value1 - count += 1 - end - if count != 0 - pv.push [prime, count] - end - break if value1 <= prime - end - if value > 1 - pv.push [value, 1] - end - return pv - end - - # An abstract class for enumerating pseudo-prime numbers. - # - # Concrete subclasses should override succ, next, rewind. - class PseudoPrimeGenerator - include Enumerable - - def initialize(ubound = nil) - @ubound = ubound - end - - def upper_bound=(ubound) - @ubound = ubound - end - def upper_bound - @ubound - end - - # returns the next pseudo-prime number, and move the internal - # position forward. - # - # +PseudoPrimeGenerator+#succ raises +NotImplementedError+. - def succ - raise NotImplementedError, "need to define `succ'" - end - - # alias of +succ+. - def next - raise NotImplementedError, "need to define `next'" - end - - # Rewinds the internal position for enumeration. - # - # See +Enumerator+#rewind. - def rewind - raise NotImplementedError, "need to define `rewind'" - end - - # Iterates the given block for each prime numbers. - def each(&block) - return self.dup unless block - if @ubound - last_value = nil - loop do - prime = succ - break last_value if prime > @ubound - last_value = block.call(prime) - end - else - loop do - block.call(succ) - end - end - end - - # see +Enumerator+#with_index. - alias with_index each_with_index - - # see +Enumerator+#with_object. - def with_object(obj) - return enum_for(:with_object) unless block_given? - each do |prime| - yield prime, obj - end - end - end - - # An implementation of +PseudoPrimeGenerator+. - # - # Uses +EratosthenesSieve+. - class EratosthenesGenerator < PseudoPrimeGenerator - def initialize - @last_prime = nil - super - end - - def succ - @last_prime = @last_prime ? EratosthenesSieve.instance.next_to(@last_prime) : 2 - end - def rewind - initialize - end - alias next succ - end - - # An implementation of +PseudoPrimeGenerator+ which uses - # a prime table generated by trial division. - class TrialDivisionGenerator<PseudoPrimeGenerator - def initialize - @index = -1 - super - end - - def succ - TrialDivision.instance[@index += 1] - end - def rewind - initialize - end - alias next succ - end - - # Generates all integer which are greater than 2 and - # are not divided by 2 nor 3. - # - # This is a pseudo-prime generator, suitable on - # checking primality of a integer by brute force - # method. - class Generator23<PseudoPrimeGenerator - def initialize - @prime = 1 - @step = nil - super - end - - def succ - loop do - if (@step) - @prime += @step - @step = 6 - @step - else - case @prime - when 1; @prime = 2 - when 2; @prime = 3 - when 3; @prime = 5; @step = 2 - end - end - return @prime - end - end - alias next succ - def rewind - initialize - end - end - - - - - # Internal use. An implementation of prime table by trial division method. - class TrialDivision - include Singleton - - def initialize # :nodoc: - # These are included as class variables to cache them for later uses. If memory - # usage is a problem, they can be put in Prime#initialize as instance variables. - - # There must be no primes between @primes[-1] and @next_to_check. - @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] - # @next_to_check % 6 must be 1. - @next_to_check = 103 # @primes[-1] - @primes[-1] % 6 + 7 - @ulticheck_index = 3 # @primes.index(@primes.reverse.find {|n| - # n < Math.sqrt(@@next_to_check) }) - @ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2 - end - - # Returns the cached prime numbers. - def cache - return @primes - end - alias primes cache - alias primes_so_far cache - - # Returns the +index+th prime number. - # - # +index+ is a 0-based index. - def [](index) - while index >= @primes.length - # Only check for prime factors up to the square root of the potential primes, - # but without the performance hit of an actual square root calculation. - if @next_to_check + 4 > @ulticheck_next_squared - @ulticheck_index += 1 - @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2 - end - # Only check numbers congruent to one and five, modulo six. All others - - # are divisible by two or three. This also allows us to skip checking against - # two and three. - @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? - @next_to_check += 4 - @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? - @next_to_check += 2 - end - return @primes[index] - end - end - - # Internal use. An implementation of eratosthenes's sieve - class EratosthenesSieve - include Singleton - - BITS_PER_ENTRY = 16 # each entry is a set of 16-bits in a Fixnum - NUMS_PER_ENTRY = BITS_PER_ENTRY * 2 # twiced because even numbers are omitted - ENTRIES_PER_TABLE = 8 - NUMS_PER_TABLE = NUMS_PER_ENTRY * ENTRIES_PER_TABLE - FILLED_ENTRY = (1 << NUMS_PER_ENTRY) - 1 - - def initialize # :nodoc: - # bitmap for odd prime numbers less than 256. - # For an arbitrary odd number n, @tables[i][j][k] is - # * 1 if n is prime, - # * 0 if n is composite, - # where i,j,k = indices(n) - @tables = [[0xcb6e, 0x64b4, 0x129a, 0x816d, 0x4c32, 0x864a, 0x820d, 0x2196].freeze] - end - - # returns the least odd prime number which is greater than +n+. - def next_to(n) - n = (n-1).div(2)*2+3 # the next odd number to given n - table_index, integer_index, bit_index = indices(n) - loop do - extend_table until @tables.length > table_index - for j in integer_index...ENTRIES_PER_TABLE - if !@tables[table_index][j].zero? - for k in bit_index...BITS_PER_ENTRY - return NUMS_PER_TABLE*table_index + NUMS_PER_ENTRY*j + 2*k+1 if !@tables[table_index][j][k].zero? - end - end - bit_index = 0 - end - table_index += 1; integer_index = 0 - end - end - - private - # for an odd number +n+, returns (i, j, k) such that @tables[i][j][k] represents primarity of the number - def indices(n) - # binary digits of n: |0|1|2|3|4|5|6|7|8|9|10|11|.... - # indices: |-| k | j | i - # because of NUMS_PER_ENTRY, NUMS_PER_TABLE - - k = (n & 0b00011111) >> 1 - j = (n & 0b11100000) >> 5 - i = n >> 8 - return i, j, k - end - - def extend_table - lbound = NUMS_PER_TABLE * @tables.length - ubound = lbound + NUMS_PER_TABLE - new_table = [FILLED_ENTRY] * ENTRIES_PER_TABLE # which represents primarity in lbound...ubound - (3..Integer(Math.sqrt(ubound))).step(2) do |p| - i, j, k = indices(p) - next if @tables[i][j][k].zero? - - start = (lbound.div(p)+1)*p # least multiple of p which is >= lbound - start += p if start.even? - (start...ubound).step(2*p) do |n| - _, j, k = indices(n) - new_table[j] &= FILLED_ENTRY^(1<<k) - end - end - @tables << new_table.freeze - end - end - - # Provides a +Prime+ object with compatibility to Ruby 1.8 when instantiated via +Prime+.+new+. - module OldCompatibility - # Returns the next prime number and forwards internal pointer. - def succ - @generator.succ - end - alias next succ - - # Overwrites Prime#each. - # - # Iterates the given block over all prime numbers. Note that enumeration starts from - # the current position of internal pointer, not rewound. - def each(&block) - return @generator.dup unless block_given? - loop do - yield succ - end - end - end -end diff --git test/test_prime.rb test/test_prime.rb index e095a29..eba8bcd 100644 --- test/test_prime.rb +++ test/test_prime.rb @@ -95,6 +95,13 @@ class TestPrime < Test::Unit::TestCase assert !Prime.instance.respond_to?(:next) end + def test_prime? + # force use Prime::Generator23 for generator + assert !Prime.prime?(0,Prime::Generator23.new) + assert !Prime.prime?(1,Prime::Generator23.new) + assert Prime.prime?(7,Prime::Generator23.new) + end + class TestInteger < Test::Unit::TestCase def test_prime_division pd = PRIMES.inject(&:*).prime_division ---------------------------------------- http://redmine.ruby-lang.org