# frozen_string_literal: false
# Prime numbers and factorization library.
# Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.)
# Copyright (c) 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp>
# 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)
# Returns the factorization of +self+.
# See Prime#prime_division for more details.
def prime_division(generator = Prime::Generator23.new)
Prime.prime_division(self, generator)
# Returns true if +self+ is a prime number, else returns false.
# Not recommended for very big integers (> 10**23).
return self >= 2 if self <= 3
if (bases = miller_rabin_bases)
return miller_rabin_test(bases)
return false unless 30.gcd(self) == 1
(7..Integer.sqrt(self)).step(30) do |p|
self%(p) == 0 || self%(p+4) == 0 || self%(p+6) == 0 || self%(p+10) == 0 ||
self%(p+12) == 0 || self%(p+16) == 0 || self%(p+22) == 0 || self%(p+24) == 0
[2,3,5,7,11,13,17,19,23],
[2,3,5,7,11,13,17,19,23,29,31,37],
[2,3,5,7,11,13,17,19,23,29,31,37,41],
private_constant :MILLER_RABIN_BASES
private def miller_rabin_bases
# Miller-Rabin's complexity is O(k log^3n).
# So we can reduce the complexity by reducing the number of bases tested.
# Using values from https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
# For small integers, Miller Rabin can be slower
# There is no mathematical significance to 0xffff
# when self < 2_047 then 0
when self < 1_373_653 then 1
when self < 9_080_191 then 2
when self < 25_326_001 then 3
when self < 3_215_031_751 then 4
when self < 4_759_123_141 then 5
when self < 1_122_004_669_633 then 6
when self < 2_152_302_898_747 then 7
when self < 3_474_749_660_383 then 8
when self < 341_550_071_728_321 then 9
when self < 3_825_123_056_546_413_051 then 10
when self < 318_665_857_834_031_151_167_461 then 11
when self < 3_317_044_064_679_887_385_961_981 then 12
private def miller_rabin_test(bases)
next if x == 1 || x == self_minus_1 || a == self
return false if r.times do
break if x == self_minus_1
# 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)
# The set of all prime numbers.
# Prime.each(100) do |prime|
# p prime #=> 2, 3, 5, 7, 11, ...., 97
# Prime.first 5 # => [2, 3, 5, 7, 11]
# == Retrieving the instance
# For convenience, each instance method of +Prime+.instance can be accessed
# as a class method of +Prime+.
# Prime.instance.prime?(2) #=> true
# Prime.prime?(2) #=> true
# A "generator" provides an implementation of enumerating pseudo-prime
# numbers and it remembers the position of enumeration and upper bound.
# Furthermore, it is an external iterator of prime enumeration which is
# compatible with an Enumerator.
# +Prime+::+PseudoPrimeGenerator+ is the base class for generators.
# There are few implementations of generator.
# [+Prime+::+EratosthenesGenerator+]
# Uses Eratosthenes' sieve.
# [+Prime+::+TrialDivisionGenerator+]
# Uses the trial division method.
# [+Prime+::+Generator23+]
# Generates all positive integers which are not divisible by either 2 or 3.
# This sequence is very bad as a pseudo-prime sequence. But this
# is faster and uses much less memory than the other generators. So,
# it is suitable for factorizing an integer which is not large but
# has many prime factors. e.g. for Prime#prime? .
def method_added(method) # :nodoc:
(class<< self;self;end).def_delegator :instance, method
# Iterates the given block over all prime numbers.
# Optional. An arbitrary positive number.
# The upper bound of enumeration. The method enumerates
# prime numbers infinitely if +ubound+ is nil.
# Optional. An implementation of pseudo-prime generator.
# An evaluated value of the given block at the last time.
# Or an enumerator which is compatible to an +Enumerator+
# Calls +block+ once for each prime number, passing the prime as
# Upper bound of prime numbers. The iterator stops after it
# yields all prime numbers p <= +ubound+.
def each(ubound = nil, generator = EratosthenesGenerator.new, &block)
generator.upper_bound = ubound
# Returns true if +obj+ is an Integer and is prime. Also returns
# true if +obj+ is a Module that is an ancestor of +Prime+.
# Otherwise returns false.
Module.instance_method(:include?).bind(Prime).call(obj)
# Returns true if +value+ is a prime number, else returns false.
# Integer#prime? is much more performant.
# +value+:: an arbitrary integer to be checked.
# +generator+:: optional. A pseudo-prime generator.
def prime?(value, generator = Prime::Generator23.new)
raise ArgumentError, "Expected a prime generator, got #{generator}" unless generator.respond_to? :each
raise ArgumentError, "Expected an integer, got #{value}" unless value.respond_to?(:integer?) && value.integer?
return false if value < 2
# Re-composes a prime factorization and returns the product.
# [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]],
# p_1**e_1 * p_2**e_2 * ... * p_n**e_n.
# +pd+:: Array of pairs of integers.
# Each pair consists of a prime number -- a prime factor --
# and a natural number -- its exponent (multiplicity).
# Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45
def int_from_prime_division(pd)
pd.inject(1){|value, (prime, index)|
# Returns the factorization of +value+.
# For an arbitrary integer:
# p_1**e_1 * p_2**e_2 * ... * p_n**e_n,
# prime_division returns an array of pairs of integers:
# [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]].
# Each pair consists of a prime number -- a prime factor --
# and a natural number -- its exponent (multiplicity).
# +value+:: An arbitrary integer.
# +generator+:: Optional. A pseudo-prime generator.
# +generator+.succ must return the next
# pseudo-prime number in ascending order.
# It must generate all prime numbers,
# but may also generate non-prime numbers, too.
# +ZeroDivisionError+:: when +value+ is zero.
# Prime.prime_division(45) #=> [[3, 2], [5, 1]]
def prime_division(value, generator = Prime::Generator23.new)
raise ZeroDivisionError if value == 0
generator.each do |prime|
while (value1, mod = value.divmod(prime)
# An abstract class for enumerating pseudo-prime numbers.
# Concrete subclasses should override succ, next, rewind.
class PseudoPrimeGenerator
def initialize(ubound = nil)
# returns the next pseudo-prime number, and move the internal
# +PseudoPrimeGenerator+#succ raises +NotImplementedError+.
raise NotImplementedError, "need to define `succ'"
raise NotImplementedError, "need to define `next'"
# Rewinds the internal position for enumeration.
# See +Enumerator+#rewind.
raise NotImplementedError, "need to define `rewind'"
# Iterates the given block for each prime number.
return self.dup unless block_given?
break last_value if prime > @ubound
# see +Enumerator+#with_index.
def with_index(offset = 0, &block)
return enum_for(:with_index, offset) { Float::INFINITY } unless block
return each_with_index(&block) if offset == 0
# see +Enumerator+#with_object.
return enum_for(:with_object, obj) { Float::INFINITY } unless block_given?
# An implementation of +PseudoPrimeGenerator+.
# Uses +EratosthenesSieve+.
class EratosthenesGenerator < PseudoPrimeGenerator
EratosthenesSieve.instance.get_nth_prime(@last_prime_index)
# An implementation of +PseudoPrimeGenerator+ which uses
# a prime table generated by trial division.
class TrialDivisionGenerator < PseudoPrimeGenerator
TrialDivision.instance[@index += 1]
# Generates all integers which are greater than 2 and
# are not divisible by either 2 or 3.
# This is a pseudo-prime generator, suitable on
# checking primality of an integer by brute force
class Generator23 < PseudoPrimeGenerator
when 3; @prime = 5; @step = 2
# Internal use. An implementation of prime table by trial division method.
# 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
# Returns the +index+th prime number.
# +index+ is a 0-based 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