結果
問題 | No.3127 Multiple of Twin Prime |
ユーザー |
|
提出日時 | 2025-04-25 23:09:16 |
言語 | Ruby (3.4.1) |
結果 |
AC
|
実行時間 | 1,472 ms / 2,500 ms |
コード長 | 58,687 bytes |
コンパイル時間 | 410 ms |
コンパイル使用メモリ | 9,088 KB |
実行使用メモリ | 28,416 KB |
最終ジャッジ日時 | 2025-04-25 23:09:37 |
合計ジャッジ時間 | 20,181 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 1 |
other | AC * 12 |
コンパイルメッセージ
Main.rb:2247: warning: 'frozen_string_literal' is ignored after any tokens Syntax OK
ソースコード
# Kept libraries: # (none) # Expanded libraries: # - faster_prime # - nanacl/bsearch_right # - faster_prime/base # - faster_prime/core_ext # - faster_prime/version # - faster_prime/utils # - faster_prime/primality_test # - faster_prime/prime_factorization # - faster_prime/sieve # Errored libraries: # (none) # Removed libraries: # (none) # # ------------------------------------------------------------------------------ # frozen_string_literal: true # This file is expanded by nanacl. main = -> do # ================================================================= # require "faster_prime" # (expanded: L2222) # require "nanacl/bsearch_right" # (expanded: L2227) in_t = gets.chomp.to_i twin_prime_multiples = [] primes = FasterPrime.each(10**7).to_a primes.each_cons(2) { |p1, p2| twin_prime_multiples << p1 * p2 if p2 - p1 == 2 } in_t.times do in_n = gets.chomp.to_i puts (twin_prime_multiples.bsearch_right { _1 <= in_n } || -1) end end # -------------------------------------------------------------------------- # === dependencies ------------------------------------------------------------- # == faster_prime/utils from faster_prime/primality_test ----------------------- module FasterPrime SMALL_PRIMES = [] SMALL_PRIME_TABLE = [] def self.setup_table(n) n = (n + 59) / 60 * 60 SMALL_PRIMES.clear << 2 n /= 2 SMALL_PRIME_TABLE.replace([true] * n) i = 1 while i < n if SMALL_PRIME_TABLE[i] SMALL_PRIMES << j = i * 2 + 1 k = i + j while k < n SMALL_PRIME_TABLE[k] = false k += j end end i += 1 end end setup_table(100000) module Utils module_function FLOAT_BIGNUM = Float::RADIX ** (Float::MANT_DIG - 1) # Find the largest integer m such that m <= sqrt(n) def integer_square_root(n) if n < FLOAT_BIGNUM Math.sqrt(n).floor else # newton method a, b = n, 1 a, b = a / 2, b * 2 until a <= b a = b + 1 a, b = b, (b + n / b) / 2 until a <= b a end end # Find b such that a * b = 1 (mod n) def mod_inv(a, n) raise ArgumentError, "not coprime for mod_inv" if a == 0 || a.gcd(n) != 1 # Extended GCD Algorithm: # Find $$(x, y)$$ such that $$ax + by = gcd(a, b), x > 0 and y > 0$$ x0, x1 = 1, 0 y0, y1 = 0, 1 b = n while b != 0 q = a / b a, b = b, a % b x0, x1 = x1, x0 - x1 * q y0, y1 = y1, y0 - y1 * q end x0 % n end # Calculate a^b mod n def mod_pow(a, b, n) r = 1 return 1 if b == 0 len = b.bit_length len.times do |i| if b[i] == 1 r = (a * r) % n return r if i == len - 1 end a = (a * a) % n end raise "assert not reached" end # Find x such that x^2 = a (mod prime) # See ``Complexity of finding square roots'' section of wikipedia: # http://en.wikipedia.org/wiki/Quadratic_residue def mod_sqrt(a, prime) return 0 if a == 0 case when prime == 2 a.odd? ? 1 : 0 when prime % 4 == 3 mod_pow(a, (prime + 1) / 4, prime) when prime % 8 == 5 # Let v = (2a)^((prime-5)/8) and r = (2av^2 - 1)av. # # 2^((prime-1)/2) = -1 because (2|prime) = -1. # a^((prime-1)/2) = 1 because (a|prime) = 1. # So, (2a)^((prime-1)/2) = -1. # # r = ((2a)^((prime-1)/4) - 1) * a * (2a)^((prime-5)/8) # r^2 = ((2a)^((prime-1)/2) - 2(2a)^((prime-1)/4) + 1) # * a^2 * (2a)^((prime-5)/4) # = -2(2a)^((prime-1)/4) * a^2 * (2a)^((prime-5)/4) # = -a * (2a)^((prime-1)/2) # = a # # So r is the solution of x^2 = a (mod prime). v = mod_pow(a * 2, (prime - 5) / 8, prime) (2 * a * v * v - 1) * a * v % prime else # prime % 8 == 1 # Use Tonelli–Shanks algorithm, see: # http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm # Calculate (q, s) such that prime - 1 = 2^s * q and q is odd s, q = 3, prime - 1 s += 1 until q[s] == 1 q >>= s # Find quadratic nonresidue z. z = (2...prime).find {|z_| kronecker(z_, prime) != 1 } # The main part of the algorithm c = mod_pow(z, q, prime) x = mod_pow(a, (q - 1) / 2, prime) r = a * x % prime # a^((q+1)/2) t = r * x % prime # a^q until t == 1 i, d = 0, t i, d = i + 1, d * d % prime while d != 1 b = mod_pow(c, 1 << (s - i - 1), prime) c = b * b % prime r = r * b % prime t = t * c % prime s = i end r end end # p-adic valuation: Find the highest exponent v such that p^v divides n def valuation(n, p) e = 0 while n % p == 0 n /= p e += 1 end e end # Enumerate divisors of n, including n itself def each_divisor(n) return enum_for(:each_divisor, n) unless block_given? t = 1 while t * t < n yield t if n % t == 0 t += 1 end yield t if n == t * t while t > 1 t -= 1 yield n / t if n % t == 0 end end # Calculate the number of positive integers less than or equal to n that # are coprime to n def totient(n) n = n.abs raise "n must not be zero" if n == 0 r = n PrimeFactorization.prime_factorization(n) do |prime, exp| r = r * (prime - 1) / prime end r end # Finds the least primitive root modulo n. # Algorithm 1.4.4 (Primitive Root). def primitive_root(n) t = Utils.totient(n) ps = PrimeFactorization.prime_factorization(t).to_a 2.upto(t) do |a| found = true ps.each do |q,| if mod_pow(a, t / q, n) == 1 found = false break end end return a if found end nil end # Calculate Kronecker symbol (a|b) in O((log a)(log b)), see pp. 29--30, # Cohen, Henri (1993), A Course in Computational Algebraic Number Theory def kronecker(a, b) return a.abs != 1 ? 0 : 1 if b == 0 return 0 if a.even? && b.even? k = 1 case a & 7 when 1, 7 then b = b / 2 while b.even? when 3, 5 then b, k = b / 2, -k while b.even? end if b < 0 b = -b k = -k if a < 0 end while a != 0 case b & 7 when 1, 7 then a = a / 2 while a.even? when 3, 5 then a, k = a / 2, -k while a.even? end k = -k if 2 & a & b == 2 a = a.abs a, b = b % a, a a -= b if a > b / 2 end return b > 1 ? 0 : k end end end # == faster_prime/utils from faster_prime/prime_factorization ------------------ module FasterPrime SMALL_PRIMES = [] SMALL_PRIME_TABLE = [] def self.setup_table(n) n = (n + 59) / 60 * 60 SMALL_PRIMES.clear << 2 n /= 2 SMALL_PRIME_TABLE.replace([true] * n) i = 1 while i < n if SMALL_PRIME_TABLE[i] SMALL_PRIMES << j = i * 2 + 1 k = i + j while k < n SMALL_PRIME_TABLE[k] = false k += j end end i += 1 end end setup_table(100000) module Utils module_function FLOAT_BIGNUM = Float::RADIX ** (Float::MANT_DIG - 1) # Find the largest integer m such that m <= sqrt(n) def integer_square_root(n) if n < FLOAT_BIGNUM Math.sqrt(n).floor else # newton method a, b = n, 1 a, b = a / 2, b * 2 until a <= b a = b + 1 a, b = b, (b + n / b) / 2 until a <= b a end end # Find b such that a * b = 1 (mod n) def mod_inv(a, n) raise ArgumentError, "not coprime for mod_inv" if a == 0 || a.gcd(n) != 1 # Extended GCD Algorithm: # Find $$(x, y)$$ such that $$ax + by = gcd(a, b), x > 0 and y > 0$$ x0, x1 = 1, 0 y0, y1 = 0, 1 b = n while b != 0 q = a / b a, b = b, a % b x0, x1 = x1, x0 - x1 * q y0, y1 = y1, y0 - y1 * q end x0 % n end # Calculate a^b mod n def mod_pow(a, b, n) r = 1 return 1 if b == 0 len = b.bit_length len.times do |i| if b[i] == 1 r = (a * r) % n return r if i == len - 1 end a = (a * a) % n end raise "assert not reached" end # Find x such that x^2 = a (mod prime) # See ``Complexity of finding square roots'' section of wikipedia: # http://en.wikipedia.org/wiki/Quadratic_residue def mod_sqrt(a, prime) return 0 if a == 0 case when prime == 2 a.odd? ? 1 : 0 when prime % 4 == 3 mod_pow(a, (prime + 1) / 4, prime) when prime % 8 == 5 # Let v = (2a)^((prime-5)/8) and r = (2av^2 - 1)av. # # 2^((prime-1)/2) = -1 because (2|prime) = -1. # a^((prime-1)/2) = 1 because (a|prime) = 1. # So, (2a)^((prime-1)/2) = -1. # # r = ((2a)^((prime-1)/4) - 1) * a * (2a)^((prime-5)/8) # r^2 = ((2a)^((prime-1)/2) - 2(2a)^((prime-1)/4) + 1) # * a^2 * (2a)^((prime-5)/4) # = -2(2a)^((prime-1)/4) * a^2 * (2a)^((prime-5)/4) # = -a * (2a)^((prime-1)/2) # = a # # So r is the solution of x^2 = a (mod prime). v = mod_pow(a * 2, (prime - 5) / 8, prime) (2 * a * v * v - 1) * a * v % prime else # prime % 8 == 1 # Use Tonelli–Shanks algorithm, see: # http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm # Calculate (q, s) such that prime - 1 = 2^s * q and q is odd s, q = 3, prime - 1 s += 1 until q[s] == 1 q >>= s # Find quadratic nonresidue z. z = (2...prime).find {|z_| kronecker(z_, prime) != 1 } # The main part of the algorithm c = mod_pow(z, q, prime) x = mod_pow(a, (q - 1) / 2, prime) r = a * x % prime # a^((q+1)/2) t = r * x % prime # a^q until t == 1 i, d = 0, t i, d = i + 1, d * d % prime while d != 1 b = mod_pow(c, 1 << (s - i - 1), prime) c = b * b % prime r = r * b % prime t = t * c % prime s = i end r end end # p-adic valuation: Find the highest exponent v such that p^v divides n def valuation(n, p) e = 0 while n % p == 0 n /= p e += 1 end e end # Enumerate divisors of n, including n itself def each_divisor(n) return enum_for(:each_divisor, n) unless block_given? t = 1 while t * t < n yield t if n % t == 0 t += 1 end yield t if n == t * t while t > 1 t -= 1 yield n / t if n % t == 0 end end # Calculate the number of positive integers less than or equal to n that # are coprime to n def totient(n) n = n.abs raise "n must not be zero" if n == 0 r = n PrimeFactorization.prime_factorization(n) do |prime, exp| r = r * (prime - 1) / prime end r end # Finds the least primitive root modulo n. # Algorithm 1.4.4 (Primitive Root). def primitive_root(n) t = Utils.totient(n) ps = PrimeFactorization.prime_factorization(t).to_a 2.upto(t) do |a| found = true ps.each do |q,| if mod_pow(a, t / q, n) == 1 found = false break end end return a if found end nil end # Calculate Kronecker symbol (a|b) in O((log a)(log b)), see pp. 29--30, # Cohen, Henri (1993), A Course in Computational Algebraic Number Theory def kronecker(a, b) return a.abs != 1 ? 0 : 1 if b == 0 return 0 if a.even? && b.even? k = 1 case a & 7 when 1, 7 then b = b / 2 while b.even? when 3, 5 then b, k = b / 2, -k while b.even? end if b < 0 b = -b k = -k if a < 0 end while a != 0 case b & 7 when 1, 7 then a = a / 2 while a.even? when 3, 5 then a, k = a / 2, -k while a.even? end k = -k if 2 & a & b == 2 a = a.abs a, b = b % a, a a -= b if a > b / 2 end return b > 1 ? 0 : k end end end # == faster_prime/utils from faster_prime/sieve -------------------------------- module FasterPrime SMALL_PRIMES = [] SMALL_PRIME_TABLE = [] def self.setup_table(n) n = (n + 59) / 60 * 60 SMALL_PRIMES.clear << 2 n /= 2 SMALL_PRIME_TABLE.replace([true] * n) i = 1 while i < n if SMALL_PRIME_TABLE[i] SMALL_PRIMES << j = i * 2 + 1 k = i + j while k < n SMALL_PRIME_TABLE[k] = false k += j end end i += 1 end end setup_table(100000) module Utils module_function FLOAT_BIGNUM = Float::RADIX ** (Float::MANT_DIG - 1) # Find the largest integer m such that m <= sqrt(n) def integer_square_root(n) if n < FLOAT_BIGNUM Math.sqrt(n).floor else # newton method a, b = n, 1 a, b = a / 2, b * 2 until a <= b a = b + 1 a, b = b, (b + n / b) / 2 until a <= b a end end # Find b such that a * b = 1 (mod n) def mod_inv(a, n) raise ArgumentError, "not coprime for mod_inv" if a == 0 || a.gcd(n) != 1 # Extended GCD Algorithm: # Find $$(x, y)$$ such that $$ax + by = gcd(a, b), x > 0 and y > 0$$ x0, x1 = 1, 0 y0, y1 = 0, 1 b = n while b != 0 q = a / b a, b = b, a % b x0, x1 = x1, x0 - x1 * q y0, y1 = y1, y0 - y1 * q end x0 % n end # Calculate a^b mod n def mod_pow(a, b, n) r = 1 return 1 if b == 0 len = b.bit_length len.times do |i| if b[i] == 1 r = (a * r) % n return r if i == len - 1 end a = (a * a) % n end raise "assert not reached" end # Find x such that x^2 = a (mod prime) # See ``Complexity of finding square roots'' section of wikipedia: # http://en.wikipedia.org/wiki/Quadratic_residue def mod_sqrt(a, prime) return 0 if a == 0 case when prime == 2 a.odd? ? 1 : 0 when prime % 4 == 3 mod_pow(a, (prime + 1) / 4, prime) when prime % 8 == 5 # Let v = (2a)^((prime-5)/8) and r = (2av^2 - 1)av. # # 2^((prime-1)/2) = -1 because (2|prime) = -1. # a^((prime-1)/2) = 1 because (a|prime) = 1. # So, (2a)^((prime-1)/2) = -1. # # r = ((2a)^((prime-1)/4) - 1) * a * (2a)^((prime-5)/8) # r^2 = ((2a)^((prime-1)/2) - 2(2a)^((prime-1)/4) + 1) # * a^2 * (2a)^((prime-5)/4) # = -2(2a)^((prime-1)/4) * a^2 * (2a)^((prime-5)/4) # = -a * (2a)^((prime-1)/2) # = a # # So r is the solution of x^2 = a (mod prime). v = mod_pow(a * 2, (prime - 5) / 8, prime) (2 * a * v * v - 1) * a * v % prime else # prime % 8 == 1 # Use Tonelli–Shanks algorithm, see: # http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm # Calculate (q, s) such that prime - 1 = 2^s * q and q is odd s, q = 3, prime - 1 s += 1 until q[s] == 1 q >>= s # Find quadratic nonresidue z. z = (2...prime).find {|z_| kronecker(z_, prime) != 1 } # The main part of the algorithm c = mod_pow(z, q, prime) x = mod_pow(a, (q - 1) / 2, prime) r = a * x % prime # a^((q+1)/2) t = r * x % prime # a^q until t == 1 i, d = 0, t i, d = i + 1, d * d % prime while d != 1 b = mod_pow(c, 1 << (s - i - 1), prime) c = b * b % prime r = r * b % prime t = t * c % prime s = i end r end end # p-adic valuation: Find the highest exponent v such that p^v divides n def valuation(n, p) e = 0 while n % p == 0 n /= p e += 1 end e end # Enumerate divisors of n, including n itself def each_divisor(n) return enum_for(:each_divisor, n) unless block_given? t = 1 while t * t < n yield t if n % t == 0 t += 1 end yield t if n == t * t while t > 1 t -= 1 yield n / t if n % t == 0 end end # Calculate the number of positive integers less than or equal to n that # are coprime to n def totient(n) n = n.abs raise "n must not be zero" if n == 0 r = n PrimeFactorization.prime_factorization(n) do |prime, exp| r = r * (prime - 1) / prime end r end # Finds the least primitive root modulo n. # Algorithm 1.4.4 (Primitive Root). def primitive_root(n) t = Utils.totient(n) ps = PrimeFactorization.prime_factorization(t).to_a 2.upto(t) do |a| found = true ps.each do |q,| if mod_pow(a, t / q, n) == 1 found = false break end end return a if found end nil end # Calculate Kronecker symbol (a|b) in O((log a)(log b)), see pp. 29--30, # Cohen, Henri (1993), A Course in Computational Algebraic Number Theory def kronecker(a, b) return a.abs != 1 ? 0 : 1 if b == 0 return 0 if a.even? && b.even? k = 1 case a & 7 when 1, 7 then b = b / 2 while b.even? when 3, 5 then b, k = b / 2, -k while b.even? end if b < 0 b = -b k = -k if a < 0 end while a != 0 case b & 7 when 1, 7 then a = a / 2 while a.even? when 3, 5 then a, k = a / 2, -k while a.even? end k = -k if 2 & a & b == 2 a = a.abs a, b = b % a, a a -= b if a > b / 2 end return b > 1 ? 0 : k end end end # == faster_prime/version from faster_prime/base ------------------------------- module FasterPrime VERSION = "1.0.1" end # == faster_prime/utils from faster_prime/base --------------------------------- module FasterPrime SMALL_PRIMES = [] SMALL_PRIME_TABLE = [] def self.setup_table(n) n = (n + 59) / 60 * 60 SMALL_PRIMES.clear << 2 n /= 2 SMALL_PRIME_TABLE.replace([true] * n) i = 1 while i < n if SMALL_PRIME_TABLE[i] SMALL_PRIMES << j = i * 2 + 1 k = i + j while k < n SMALL_PRIME_TABLE[k] = false k += j end end i += 1 end end setup_table(100000) module Utils module_function FLOAT_BIGNUM = Float::RADIX ** (Float::MANT_DIG - 1) # Find the largest integer m such that m <= sqrt(n) def integer_square_root(n) if n < FLOAT_BIGNUM Math.sqrt(n).floor else # newton method a, b = n, 1 a, b = a / 2, b * 2 until a <= b a = b + 1 a, b = b, (b + n / b) / 2 until a <= b a end end # Find b such that a * b = 1 (mod n) def mod_inv(a, n) raise ArgumentError, "not coprime for mod_inv" if a == 0 || a.gcd(n) != 1 # Extended GCD Algorithm: # Find $$(x, y)$$ such that $$ax + by = gcd(a, b), x > 0 and y > 0$$ x0, x1 = 1, 0 y0, y1 = 0, 1 b = n while b != 0 q = a / b a, b = b, a % b x0, x1 = x1, x0 - x1 * q y0, y1 = y1, y0 - y1 * q end x0 % n end # Calculate a^b mod n def mod_pow(a, b, n) r = 1 return 1 if b == 0 len = b.bit_length len.times do |i| if b[i] == 1 r = (a * r) % n return r if i == len - 1 end a = (a * a) % n end raise "assert not reached" end # Find x such that x^2 = a (mod prime) # See ``Complexity of finding square roots'' section of wikipedia: # http://en.wikipedia.org/wiki/Quadratic_residue def mod_sqrt(a, prime) return 0 if a == 0 case when prime == 2 a.odd? ? 1 : 0 when prime % 4 == 3 mod_pow(a, (prime + 1) / 4, prime) when prime % 8 == 5 # Let v = (2a)^((prime-5)/8) and r = (2av^2 - 1)av. # # 2^((prime-1)/2) = -1 because (2|prime) = -1. # a^((prime-1)/2) = 1 because (a|prime) = 1. # So, (2a)^((prime-1)/2) = -1. # # r = ((2a)^((prime-1)/4) - 1) * a * (2a)^((prime-5)/8) # r^2 = ((2a)^((prime-1)/2) - 2(2a)^((prime-1)/4) + 1) # * a^2 * (2a)^((prime-5)/4) # = -2(2a)^((prime-1)/4) * a^2 * (2a)^((prime-5)/4) # = -a * (2a)^((prime-1)/2) # = a # # So r is the solution of x^2 = a (mod prime). v = mod_pow(a * 2, (prime - 5) / 8, prime) (2 * a * v * v - 1) * a * v % prime else # prime % 8 == 1 # Use Tonelli–Shanks algorithm, see: # http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm # Calculate (q, s) such that prime - 1 = 2^s * q and q is odd s, q = 3, prime - 1 s += 1 until q[s] == 1 q >>= s # Find quadratic nonresidue z. z = (2...prime).find {|z_| kronecker(z_, prime) != 1 } # The main part of the algorithm c = mod_pow(z, q, prime) x = mod_pow(a, (q - 1) / 2, prime) r = a * x % prime # a^((q+1)/2) t = r * x % prime # a^q until t == 1 i, d = 0, t i, d = i + 1, d * d % prime while d != 1 b = mod_pow(c, 1 << (s - i - 1), prime) c = b * b % prime r = r * b % prime t = t * c % prime s = i end r end end # p-adic valuation: Find the highest exponent v such that p^v divides n def valuation(n, p) e = 0 while n % p == 0 n /= p e += 1 end e end # Enumerate divisors of n, including n itself def each_divisor(n) return enum_for(:each_divisor, n) unless block_given? t = 1 while t * t < n yield t if n % t == 0 t += 1 end yield t if n == t * t while t > 1 t -= 1 yield n / t if n % t == 0 end end # Calculate the number of positive integers less than or equal to n that # are coprime to n def totient(n) n = n.abs raise "n must not be zero" if n == 0 r = n PrimeFactorization.prime_factorization(n) do |prime, exp| r = r * (prime - 1) / prime end r end # Finds the least primitive root modulo n. # Algorithm 1.4.4 (Primitive Root). def primitive_root(n) t = Utils.totient(n) ps = PrimeFactorization.prime_factorization(t).to_a 2.upto(t) do |a| found = true ps.each do |q,| if mod_pow(a, t / q, n) == 1 found = false break end end return a if found end nil end # Calculate Kronecker symbol (a|b) in O((log a)(log b)), see pp. 29--30, # Cohen, Henri (1993), A Course in Computational Algebraic Number Theory def kronecker(a, b) return a.abs != 1 ? 0 : 1 if b == 0 return 0 if a.even? && b.even? k = 1 case a & 7 when 1, 7 then b = b / 2 while b.even? when 3, 5 then b, k = b / 2, -k while b.even? end if b < 0 b = -b k = -k if a < 0 end while a != 0 case b & 7 when 1, 7 then a = a / 2 while a.even? when 3, 5 then a, k = a / 2, -k while a.even? end k = -k if 2 & a & b == 2 a = a.abs a, b = b % a, a a -= b if a > b / 2 end return b > 1 ? 0 : k end end end # == faster_prime/primality_test from faster_prime/base ------------------------ # require "faster_prime/utils" # (expanded: L22) module FasterPrime module PrimalityTest module_function DEFAULT_RANDOM = Random.new def prime?(n, random = DEFAULT_RANDOM) r = probable_prime?(n, random) return r if r != nil return APRCL.prime?(n) end # Miller-Rabin primality test # Returns true for a prime, false for a composite, nil if unknown. def probable_prime?(n, random = Random.new) return n >= 2 if n <= 3 return true if n == 2 || n == 3 return true if n == 5 return false unless 30.gcd(n) == 1 return SMALL_PRIME_TABLE[n / 2] if n / 2 < SMALL_PRIME_TABLE.size m = n % 6 return false if m != 1 && m != 5 list = DETERMINISTIC_MR_TEST_TABLE.find {|t,| n < t } if list ret = true list = list.last else ret = nil list = (1..20).map { random.rand(n - 2) + 1 } end d, s = n - 1, 0 while d.even? d >>= 1 s += 1 end list.each do |a| y = Utils.mod_pow(a, d, n) if y != 1 f = s.times do break if y == n - 1 y = (y * y) % n end return false if f end end ret end # These numbers are taken from: # http://code.google.com/p/sympy/ # http://primes.utm.edu/prove/prove2_3.html DETERMINISTIC_MR_TEST_TABLE = [ [ 1_373_653, [2, 3]], [ 170_584_961, [350, 3_958_281_543]], [ 4_759_123_141, [2, 7, 61]], [ 75_792_980_677, [2, 379_215, 457_083_754]], [ 1_000_000_000_000, [2, 13, 23, 1_662_803]], [ 2_152_302_898_747, [2, 3, 5, 7, 11]], [ 3_474_749_660_383, [2, 3, 5, 7, 11, 13]], [341_550_071_728_321, [2, 3, 5, 7, 11, 13, 17]], ] # APRCL Primarity Test # # Henry Cohen. A course in computational algebraic number theory. # 9.1 The Jacobi Sum Test class APRCL # usage: # # (1) simplest # # APRCL.prime?(N) #=> true or false # # (2) reuse-table # # aprcl = APRCL.new(B) # aprcl.prime?(N1) #=> true or false # aprcl.prime?(N2) #=> true or false # aprcl.bound #=> upper bound (>= B) # # (3) manual-t (for test or debug) # # aprcl = APRCP.new(B) # aprcl.set_t(t) # aprcl.prime?(N) #=> true or false # An internal exception for terminating the algorithm because n is composite class Composite < StandardError end # An exception representing that the test has failed (highly improbable) class Failed < StandardError end # Returns true if n is prime, false otherwise. def self.prime?(n) new(n).prime? end # Creates an APRCL instance to test an integer that is less than bound def initialize(n, jacobi_sum_table: DefaultJacobiSumTable, t: nil) # an integer in question @n = n # precompute n-1 and (n-1)/2 because n is normally big @n1 = n - 1 @n1_2 = @n1 / 2 # @eta[pk]: a set of p^k-th roots of unity @eta = {} # a cache of Jacobi sums @js = jacobi_sum_table # compute e(t) if t raise "t must be even" if t.odd? @t = t @et = compute_e(@t) raise "t is too small to test n" if @n >= @et ** 2 else @t = find_t(@n) @et = compute_e(@t) end end # Algorithm 9.1.28 (Jacobi Sum Primality test) def prime? return false if @n <= 1 # 1. [Check GCD] g = @n.gcd(@t * @et) return g == @n && PrimalityTest.prime?(g) if g != 1 # 2. [Initialize] lp = {} PrimeFactorization.prime_factorization(@t) do |p, | lp[p] = false if p == 2 || Utils.mod_pow(@n, p - 1, p ** 2) == 1 end # 3. [Loop on characters] Utils.each_divisor(@t) do |d| q = d + 1 next unless PrimalityTest.prime?(q) PrimeFactorization.prime_factorization(d) do |p, k| # 4. [Check (*beta)] lp.delete(p) if step4(q, p, k) end end # 5. [Check conditions Lp] lp.keys.each {|p| step5(p) } # 6. [Final trial division] r = 1 1.upto(@t - 1) do r = r * @n % @et return false if @n % r == 0 && 1 < r && r < @n end return true rescue Composite return false end private def step4(q, p, k) case when p >= 3 then step4a(q, p, k) when k >= 3 then step4b(q, k) when k == 2 then step4c(q) when k == 1 then step4d(q) end end # p is odd def step4a(q, p, k) pk = p ** k # s1 = J(p,q)^theta, s3 = J(p,q)^alpha s1 = s3 = ZZeta.one(p, k) r = @n % pk jpq = @js.get_j(p, q) 1.upto(pk - 1) do |x| next if x % p == 0 j = jpq.substitute(x).reduce_mod!(@n) s1 = s1 * j.mod_pow(x, @n) s3 = s3 * j.mod_pow(r * x / pk, @n) end s2 = s1.mod_pow(@n / pk, @n) # S(p,q) = s2 J(p,q)^alpha s = (s2 * s3).reduce_mod!(@n) i = check_root_of_unity(p, k, pk, s) i % p != 0 # it is a primitive root of unity end # p = 2, k >= 3 def step4b(q, k) pk = 2 ** k # s1 = J_3(q)^theta, s3 = J_3(q)^alpha s1 = s3 = ZZeta.one(2, k) r = @n % pk j3, j2 = @js.get_j3_j2(q) diff = 1 0.step(pk - 1, 4) do |x| x, diff = x + diff, -diff # x = 8m+1 or 8m+3, i.e., 1, 3, 9, 11, ... j = j3.substitute(x).reduce_mod!(@n) s1 = (s1 * j.mod_pow(x , @n)).reduce_mod!(@n) s3 = (s3 * j.mod_pow(r * x / pk, @n)).reduce_mod!(@n) end # S(2,q) = s2 J_3(q)^alpha if r = 8m+1 or 8m+3 # s2 J_3(q)^alpha J_2(q) if r = 8m+5 or 8m+7 s = s3 * s1.mod_pow(@n / pk, @n) s = s * j2 if r[2] == 1 # r = 8m+5 or 8m+7 s.reduce_mod!(@n) i = check_root_of_unity(2, k, pk, s) i[0] == 1 && Utils.mod_pow(q, @n1_2, @n) == @n1 end # p = 2, k = 2 def step4c(q) s0 = @js.get_j(2, q).sqr s1 = s0.scalar(q) s2 = s1.mod_pow(@n / 4, @n) # S(2,q) = s2 if n = 4m+1 # s2 J(2,q)^2 if n = 4m+3 s = (@n[1] == 0 ? s2 : s2 * s0).reduce_mod!(@n) i = check_root_of_unity(2, 2, 4, s) i[0] != 1 && Utils.mod_pow(q, @n1_2, @n) == @n1 end # p = 2, k = 1 def step4d(q) # S(2,q) = (-q)^((n-1)/2) mod N s = Utils.mod_pow(-q, @n1_2, @n) raise Composite if s != 1 && s != @n1 s == @n && @n[1] == 0 end # check whether there exists a p^k-th root of unity e such that s = e mod n def check_root_of_unity(p, k, pk, s) unless @eta[pk] table = {} pk.times do |i| table[ZZeta.monomial(p, k, i).reduce_mod!(@n)] = i end @eta[pk] = table end @eta[pk][s] || raise(Composite) end TRIAL = 2000 def step5(p) bound_k = 4 # Find q and k by brute force. # We first try to find a small k because step4 might take long time # when k is big (>= 4). (An example case: prime?(19541) with TRIAL = 128) # If we cannot do so, we use any k. 2.times do q = p + 1 TRIAL.times do if @et % q != 0 && PrimalityTest.prime?(q) if @n % q == 0 return if @n == q raise Composite end k = Utils.valuation(q - 1, p) return if k <= bound_k && step4(q, p, k) end q += p end bound_k = Float::INFINITY end raise Failed, "APRCL primality test failed (highly improbable)" end # The pairs of t candidate and floor(log_2(e^2(t))). # generated by tool/build-aprcl-t-table.rb T_TABLE = [ [12, 31], [24, 33], [30, 34], [36, 54], [60, 65], [72, 68], [108, 70], [120, 78], [180, 102], [240, 104], [360, 127], [420, 136], [540, 153], [840, 165], [1008, 169], [1080, 178], [1200, 192], [1260, 206], [1620, 211], [1680, 222], [2016, 225], [2160, 244], [2520, 270], [3360, 279], [3780, 293], [5040, 346], [6480, 348], [7560, 383], [8400, 396], [10080, 426], [12600, 458], [15120, 527], [25200, 595], [30240, 636], [42840, 672], [45360, 684], [55440, 708], [60480, 771], [75600, 775], [85680, 859], [100800, 893], [110880, 912], [128520, 966], [131040, 1009], [166320, 1042], [196560, 1124], [257040, 1251], [332640, 1375], [393120, 1431], [514080, 1483], [655200, 1546], [665280, 1585], [786240, 1661], [831600, 1667], [917280, 1677], [982800, 1728], [1081080, 1747], [1179360, 1773], [1285200, 1810], [1310400, 1924], [1441440, 2001], [1663200, 2096], [1965600, 2166], [2162160, 2321], [2751840, 2368], [2827440, 2377], [3326400, 2514], [3341520, 2588], [3603600, 2636], [3931200, 2667], [4324320, 3028], [5654880, 3045], [6652800, 3080], [6683040, 3121], [7207200, 3283], [8648640, 3514], [10810800, 3725], [12972960, 3817], [14414400, 3976], [18378360, 3980], [21621600, 4761], [36756720, 5067], [43243200, 5657], [64864800, 5959], [73513440, 6423], [86486400, 6497], ] # Find t such that e(t) > n^(1/2). def find_t(n) n_log2 = n.bit_length # e^2(t) >= 2^(et2_log2) >= 2^n_log2 > n t, = T_TABLE.find {|_t, et2_log2| et2_log2 >= n_log2 } raise "too large" unless t t end # Compute e(t) (the definition comes from Theorem 9.1.12) def compute_e(t) r = 2 Utils.each_divisor(t) do |d| q = d + 1 r *= q ** (Utils.valuation(t, q) + 1) if PrimalityTest.prime?(q) end r end class JacobiSumTableClass # Algorithm 9.1.27 (Precomputations) # Note that this computes the table lazily, i.e., on demand def initialize @f = {} # a table of the function f(x) in 2. (1) @j = {} # J(p,q) @j3_j2 = {} # J3(p,q) and J2(p,q) end def clear @f.clear @j.clear @j3_j2.clear end # compute a table of the function f(x) such that 1 - g^x = g^f(x), # where g is a primitive root modulo q def calc_f(q) return @f[q] if @f[q] g = Utils.primitive_root(q) f, h = [], {} 1.upto(q - 2) {|x| h[(1 - Utils.mod_pow(g, x, q)) % q] = x } 1.upto(q - 2) {|fx| f[h[Utils.mod_pow(g, fx, q)]] = fx } @f[q] = f end # compute J(p,q) def get_j(p, q) # assumes that p >= 3 or (p == 2 && k >= 2) return @j[[p, q]] if @j[[p, q]] f = calc_f(q) k = Utils.valuation(q - 1, p) pk = p ** k # J(p,q) = \Sum_{1<=x<=q-2} (zeta_{p^k}^{x+f(x)}) coefs = [0] * pk 1.upto(q - 2) {|x| coefs[(x + f[x]) % pk] += 1 } @j[[p, q]] = ZZeta.new(coefs, p, k).reduce! end # compute J_3(q) and J_2(q) def get_j3_j2(q) # assumes that p == 2 && k >= 3 return @j3_j2[q] if @j3_j2[q] f = calc_f(q) k = Utils.valuation(q - 1, 2) pk = 2 ** k # coefs3: \Sum_{1<=x<=q-2} (zeta_{2^k}^{2x+f(x)}) # coefs2: \Sum_{1<=x<=q-2} (zeta_{2^3}^{3x+f(x)}) coefs3 = [0] * pk coefs2 = [0] * pk 1.upto(q - 2) do |x| coefs3[(2 * x + f[x]) % pk] += 1 coefs2[(3 * x + f[x]) % 8 * (pk / 8)] += 1 end # J_3(q) = J(2,q) coefs3, J_2(q) = ( coefs2 )^2 j3 = (get_j(2, q) * ZZeta.new(coefs3, 2, k)).reduce! j2 = ZZeta.new(coefs2, 2, k).reduce!.sqr.reduce! @j3_j2[q] = [j3, j2] end end DefaultJacobiSumTable = JacobiSumTableClass.new # A group algebra Z[zeta_{p^k}] # (linear sum of p^k-th roots of unity) class ZZeta def initialize(coefs, p, k) @coefs = coefs @p, @k = p, k @pk1 = p ** (k - 1) # p^(k-1) @pk = @pk1 * p # p^k @p1pk1 = @pk - @pk1 # (p-1) p^(k-1) if @pk != @coefs.size raise "coefs.size (#{ coefs.size }) must be equal to #{ @p }**#{ @k }" end end attr_reader :coefs protected :coefs # generates 1 def self.one(p, k) @@one[p ** k] ||= monomial(p, k, 0) end @@one = {} # generates zeta_{p^k}^i def self.monomial(p, k, i) coefs = [0] * (p ** k) coefs[i] = 1 ZZeta.new(coefs, p, k) end # for hashing def ==(other); @coefs == other.coefs; end def hash; @coefs.hash; end alias eql? == # scalar multiplication def scalar(n) ZZeta.new(@coefs.map {|x| x * n }, @p, @k) end # polynomial multiplication def *(other) coefs = [0] * @pk other.coefs.each_with_index do |oc, j| next if oc == 0 @pk.times do |i| coefs[(i + j) % @pk] += @coefs[i] * oc end end ZZeta.new(coefs, @p, @k) end # polynomial power modulo n def mod_pow(exp, n) return ZZeta.one(@p, @k) if exp == 0 r = nil z = reduce_mod!(n) len = exp.bit_length len.times do |i| if exp[i] == 1 r = r ? (z * r).reduce_mod!(n) : z return r if i == len - 1 end z = z.sqr.reduce_mod!(n) end raise "assert not reached" end # polynomial square (self * self) # assumes self is already reduced def sqr coefs = [0] * @pk @p1pk1.times do |j| oc = @coefs[j] next if oc == 0 @p1pk1.times do |i| coefs[(i + j) % @pk] += @coefs[i] * oc end end ZZeta.new(coefs, @p, @k) end # generate a reminder divided by CyclotomicPolynomial(p^k) def reduce! # CyclotomicPolynomial(n) = \Prod_{d|n} (x^{n/d} - 1)^\moeb{d} # # CyclotomicPolynomial(p^k) = # (x^{p^ k } - 1)^\moeb{p^0} (case d = p^0) # * (x^{p^{k-1}} - 1)^\moeb{p^1} (case d = p^1) # ... # * (x^{p^ 0 } - 1)^\moeb{p^k} (case d = p^k) # = (x^{p^k} - 1) / (x^{p^{k-1}} - 1) # = \Sum_{0 <= i < p} (x^{i p^{k-1}}) @p1pk1.times do |i| @coefs[i] -= @coefs[@p1pk1 + (i % @pk1)] end @pk1.times do |i| @coefs[@p1pk1 + i] = 0 end self end # reduce modulo n def reduce_mod!(n) @pk.times do |i| # I would use centermod if it was built-in... @coefs[i] = (@coefs[i] - @coefs[@p1pk1 + (i % @pk1)]) % n end self end # replace zeta with zeta^x def substitute(x) coefs = [] @pk.times {|i| coefs << @coefs[(x * i) % @pk] } ZZeta.new(coefs, @p, @k) end def inspect s = [] (@pk - 1).downto(0) do |i| c = @coefs[i] next if c == 0 s << (c > 0 ? ?+ : ?-) + case when i == 0 then c.abs.to_s when c.abs == 1 then i == 1 ? "z" : "z^#{ i }" else "#{ c.abs }#{ i == 1 ? "z" : "z^#{ i }" }" end end return "0" if s.empty? s.first[0, 1] = "" if s.first[0] == ?+ s.join end end end end end # == faster_prime/prime_factorization from faster_prime/base ------------------- # require "faster_prime/utils" # (expanded: L22) module FasterPrime module PrimeFactorization module_function # Factorize an integer def prime_factorization(n) return enum_for(:prime_factorization, n) unless block_given? return if n == 0 if n < 0 yield [-1, 1] n = -n end SMALL_PRIMES.each do |prime| if n % prime == 0 c = 0 begin n /= prime c += 1 end while n % prime == 0 yield [prime, c] end if prime * prime > n yield [n, 1] if n > 1 return end return if n == 1 end if PrimalityTest.prime?(n) yield [n, 1] else d = nil until d [PollardRho, MPQS].each do |algo| begin d = algo.try_find_factor(n) rescue Failed else break end end end pe = Hash.new(0) prime_factorization(n / d) {|p, e| pe[p] += e } prime_factorization(d) {|p, e| pe[p] += e } pe.keys.sort.each do |p| yield [p, pe[p]] end end end end class Failed < StandardError end # An implementation of Pollard's rho algorithm (Brent's variant) # # https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm#Variants module PollardRho module_function def self.try_find_factor(n) x0 = rand(n) y = x0 m = Math.log(n, 2).ceil c = 2 r = q = 1 while true x = y r.times { y = (y * y + c) % n } k = 0 while true ys = y [m, r - k].min.times do y = (y * y + c) % n q = (q * (x - y)) % n end g = q.gcd(n) k += m break if k >= r || g > 1 end r *= 2 break if g > 1 end if g == n while true ys = (ys * ys + c) % n g = (x - ys).gcd(n) break if g > 1 end end if g == n raise Failed, "failed to find a factor: #{ n }" else g end end end # An implementation of MPQS factoring algorithm. # # R. D. Silverman, # "The Multiple Polynomial Quadratic Sieve." (1987) # http://www.ams.org/journals/mcom/1987-48-177/S0025-5718-1987-0866119-8/S0025-5718-1987-0866119-8.pdf class MPQS # MPQS parameters # (N digits, factor base size, sieve interval, and large prime tolerance) PARAMETERS = [ [24, 100, 250, 1.5 ], [30, 200, 1_250, 1.5 ], [36, 400, 1_250, 1.75], [42, 900, 2_500, 2.0 ], [48, 1200, 7_000, 2.0 ], [54, 2000, 12_500, 2.2 ], [60, 3000, 17_500, 2.4 ], [66, 4500, 25_000, 2.6 ], ] SMALL_PRIMES = [2, 3, 5, 7, 11] # Generates a table for finding k such that kN = 1 mod 4 and that kN is # rich in small quadratic residues, i.e., (kN | p) = 1 for all p in # SMALL_PRIMES. def self.k_table return @@k_table if defined?(@@k_table) k_table = { 1 => {}, 3 => {} } Sieve.each do |p| next if p <= SMALL_PRIMES.last state = SMALL_PRIMES.map {|q| Utils.kronecker(p, q) } k_table[p % 4][state] ||= p break if k_table.all? {|r, t| t.size == 2 ** SMALL_PRIMES.size } end @@k_table = k_table end def self.try_find_factor(n) new(n).try_find_factor end S = 65536 def initialize(n) @n = n if @n <= 1 || PrimalityTest.prime?(@n) @trivial_factor = 1 return end SMALL_PRIMES.each do |p| if @n % p == 0 @trivial_factor = p return end end m = Utils.integer_square_root(n) if m * m == n @trivial_factor = m return end # select parameters # @k: a multiplier @k = MPQS.k_table[@n % 4][SMALL_PRIMES.map {|q| Utils.kronecker(@n, q) }] @kn = @k * @n # @f: the size of the factor base # @m: the length of the sieve interval (2M+1) # @t: the large prime tolerance _digit, @f, @m, @t = PARAMETERS.find {|d,| Math.log(@n, 10) < d } || PARAMETERS.last # compute factor base # @ps: the factor base # @log_ps: log of each factor base # @sqrts: sqrt(kN) mod p @ps, @log_ps, @sqrts = [], [], [] Sieve.each do |p| if @n % p == 0 @trivial_factor = p return end if p >= 3 && Utils.kronecker(@kn, p) == 1 @ps << p @log_ps << (Math.log(p) * S).floor @sqrts << Utils.mod_sqrt(@kn, p) break if @ps.size >= @f end end @pmax = @ps.last # max factor base # for enumerating coefficients @base_d = Math.sqrt(Math.sqrt(@kn) / (Math.sqrt(2) * @m)).floor | 3 @diff_d = 0 # 0, 4, -4, 8, -8, 12, -12, ... # sieve table @ws = [0] * (2 * @m + 1) # a test value for sieve @test_value = (Math.log(@m * Math.sqrt(@kn) / @pmax ** @t) * S).round # relations found @incomplete_relations = {} # for gaussian elimination @elim = GaussianElimination.new(@ps.size + 2) end # try to find any factor (not necessarily a prime) def try_find_factor return @trivial_factor if defined?(@trivial_factor) catch(:factor_found) do while true p :foo select_q solve_roots sieve @ws.each_with_index do |w, i| check_relation(i - @m) if w > @test_value end end end end # Selects Q(x) = Ax^2 + Bx + C for quadratic sieve def select_q # find a prime D such that (kN | D) = 1 and D = 3 mod 4 begin @d = @base_d + @diff_d @diff_d = @diff_d > 0 ? -@diff_d : 4 - @diff_d end until Utils.kronecker(@kn, @d) == 1 && PrimalityTest.probable_prime?(@d) != false && !@ps.include?(@d) # select coefficients (see the paper) @a = @d * @d h0 = Utils.mod_pow(@kn, @d / 4, @d) h1 = (@kn * h0) % @d # assert: h1*h1 = kN mod D # assert: h0*h1 = 1 mod D h2 = (Utils.mod_inv(2, @d) * h0 * ((@kn - h1 * h1) / @d)) % @d # assert: kN - h1^2 = 0 mod D @b = (h1 + h2 * @d) % @a @b -= @a if @b[0] == 0 # assert: B^2 = kN mod 4A #@c = (@b * @b - @kn) / (4 * @a) # not needed # compute real roots of Q(x) = 0 s = Utils.integer_square_root(@kn) @real_root_1 = (-@b - s) / (2 * @a) @real_root_2 = (-@b + s) / (2 * @a) # x is between real roots if @real_root_1 <= x && x <= @real_root_2 end # Computes the roots of Q(x) = 0 mod p (p in the factor base) def solve_roots @roots = [] @ps.zip(@sqrts) do |p, s| a_inv = Utils.mod_inv(2 * @a, p) x1 = (-@b + s) * a_inv % p x2 = (-@b - s) * a_inv % p @roots << [x1, x2] end end # Performs the quadratic sieve def sieve @ws.fill(0) @ps.zip(@log_ps, @roots) do |p, lp, (s1, s2)| ((@m + s1) % p).step(2 * @m, p) {|i| @ws[i] += lp } ((@m + s2) % p).step(2 * @m, p) {|j| @ws[j] += lp } end end # Tests whether Q(x) is actually a product of factor bases def check_relation(x) h, qx = compute_q(x) return if qx == 0 es, l = exponent_bitvector(qx) # discard this x if the residue L is too big return if l > @pmax ** @t if l == 1 # complete relation found: # Q(x) = p0^e0 * p1^e1 * ... * pk^ek (pi in the factor base) qx_vec = Hash.new(0) PrimeFactorization.prime_factorization(qx) {|p, e| qx_vec[p] += e } collect_relation(es, h, qx_vec) elsif @incomplete_relations[l] # large prime procedure: # make a complete relation by multiplying two incomplete relations es2, h2, qx2 = @incomplete_relations[l] # XXX: use FactoredInteger qx_vec = Hash.new(0) PrimeFactorization.prime_factorization(qx) {|p, e| qx_vec[p] += e } PrimeFactorization.prime_factorization(qx2) {|p, e| qx_vec[p] += e } collect_relation(es ^ es2, h * h2 % @kn, qx_vec) else @incomplete_relations[l] = [es, h, qx] end end # Adds a complete relation found to gaussian elimination list def collect_relation(es, h, q) #p "%0#{ @ps.size + 1 }b" % es @elim[es] = [h, q] if @elim.size > 0.9 * @f && @elim.size % [1, @f / 100].max == 0 @elim.eliminate do |relations| # factor candidate found check_factor_candidate(relations) end end if @elim.size > @f * 1.5 raise Failed, "failed to find a factor: #{ @n }" end end # Computes and checks a factor candidate def check_factor_candidate(relations) # computes the factor candidate p1 = 1 p2_vec = Hash.new(0) # XXX: use FactoredInteger relations.each do |h, qx_vec| p1 = p1 * h % @kn qx_vec.each {|p, e| p2_vec[p] += e } end p2 = 1 p2_vec.each {|p, e| p2 = (p2 * Utils.mod_pow(p, e / 2, @kn)) % @kn } return if p1 == p2 || (p1 + p2) % @kn == 0 factor = (p1 + p2).gcd(@n) # check the factor candidate if factor != 1 && factor != @n throw :factor_found, factor end end # Computes Q(x) def compute_q(x) h = (2 * @a * x + @b) * Utils.mod_inv(2 * @d, @kn) % @kn q = h * h % @kn q -= @kn if @real_root_1 <= x && x <= @real_root_2 # assert: q == @a*x*x + @b*x + @c return h, q end # Factorizes n in factor base and returns an exponent bitvector def exponent_bitvector(n) vec = 0 if n < 0 vec = 1 n = -n end bit = 2 ([2] + @ps).each do |p| e = false while n % p == 0 n /= p e = !e end vec |= bit if e bit <<= 1 end return vec, n end # Incremental gaussian elimination. # # A. K. Lenstra, and M. S. Manasse, # "Compact incremental Gaussian Elimination over Z/2Z." (1988) # http://dl.acm.org/citation.cfm?id=896266 class GaussianElimination def initialize(m) @n = 0 @m = m @d = [] @e = [] @u = [] @v = [] end def []=(es, val) @d << es @e << nil @v << val end def size @d.size end def eliminate n2 = @d.size @n.times do |i| if @u[i] @e[@u[i]] = i @n.upto(n2 - 1) do |j| @d[j] ^= @d[i] if @d[j][@u[i]] == 1 end end end @n.upto(n2 - 1) do |j| found = false @m.times do |i| if @d[j][i] == 1 && !@e[i] @u[j] = i @e[@u[j]] = j @d[j] &= ~(1 << @u[j]) (j + 1).upto(n2 - 1) do |k| @d[k] ^= @d[j] if @d[k][@u[j]] == 1 end found = true break end end if !found # linear dependency found @u[j] = nil c = {} c[j] = true @m.times do |k| c[@e[k]] = true if @d[j][k] == 1 end yield c.keys.map {|k| @v[k] } end end @n = n2 end end end end # == faster_prime/sieve from faster_prime/base --------------------------------- # Sieve of Atkin # http://cr.yp.to/papers/primesieves.pdf # require "faster_prime/utils" # (expanded: L22) module FasterPrime module Sieve module_function TBL = [] [ [:mark_type_1, ->(f, g) { 4*f*f + g*g }, 15, 30, 1, 4], [:mark_type_2, ->(f, g) { 3*f*f + g*g }, 10, 30, 7, 12], [:mark_type_3, ->(f, g) { 3*f*f - g*g }, 10, 30, 11, 12], ].each do |type, expr, fmax, gmax, n, mod| gmax.times do |g| fmax.times do |f| d = expr[f, g] % 60 TBL << [type, f, g, d] if d % mod == n && d.gcd(60) == 1 end end end MODULO60 = [1, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 53, 59] def each(ubound = nil, &blk) if ubound && SMALL_PRIMES.last >= ubound SMALL_PRIMES.each(&blk) return end u = SMALL_PRIMES.last / 60 * 60 SMALL_PRIMES.pop until SMALL_PRIMES.last < u SMALL_PRIMES.each(&blk) b = Utils.integer_square_root(ubound * 3000) if ubound b = 100000 if !b || b > 100000 l = u / 60 # Algorithm 3.1, 3.2, 3.3 flags = [0] * b while true # step flags.fill(0) # sieve TBL.each do |name, f, g, d| send(name, flags, f, g, d, l, b) end # squarefree squarefree(flags, l, b) # enumerate primes if ubound && ubound < 60 * (l + b) enumerate(flags, l, b) do |p| return if p >= ubound yield p end else enumerate(flags, l, b, &blk) end l += b end end def enumerate(flags, l, b) b.times do |x| base = 60 * (l + x) flag = flags[x] MODULO60.each do |d| next if flag[d] == 0 p = base + d SMALL_PRIMES << p yield p end end end def squarefree(flags, l, b) SMALL_PRIMES.each do |q| q2 = q * q break if q2 > 60 * (l + b) next if q < 7 minus_inv_60 = -Utils.mod_inv(60, q2) MODULO60.each do |d| # 60(l+x)+d == 0 (mod q2) # x = -d * 60^{-1} - l (mod q2) x = (d * minus_inv_60 - l) % q2 m = ~(1 << d) while x < b flags[x] &= m x += q2 end end end end # Algorithm 4.1 def mark_type_1(flags, f, g, d, l, b) x, y0 = f, g k0 = (4 * f * f + g * g - d) / 60 - l m = 1 << d while k0 < l + b k0 += 2*x + 15 x += 15 end while true x -= 15 k0 -= 2*x + 15 return if x <= 0 while k0 < 0 k0 += y0 + 15 y0 += 30 end k, y = k0, y0 while k < b flags[k] ^= m # mark k += y + 15 y += 30 end end end # Algorithm 4.2 def mark_type_2(flags, f, g, d, l, b) x, y0 = f, g k0 = (3 * f * f + g * g - d) / 60 - l m = 1 << d while k0 < b k0 += x + 5 x += 10 end while true x -= 10 k0 -= x + 5 return if x <= 0 while k0 < 0 k0 += y0 + 15 y0 += 30 end k, y = k0, y0 while k < b flags[k] ^= m # mark k += y + 15 y += 30 end end end # Algorithm 4.3 def mark_type_3(flags, f, g, d, l, b) x, y0 = f, g k0 = (3 * f * f - g * g - d) / 60 - l m = 1 << d while true while k0 >= b return if x <= y0 k0 -= y0 + 15 y0 += 30 end k, y = k0, y0 while k >= 0 && y < x flags[k] ^= m # mark k -= y + 15 y += 30 end k0 += x + 5 x += 10 end end end end # == faster_prime/core_ext from faster_prime/base ------------------------------ class Integer def Integer.from_prime_division(pd) FasterPrime.int_from_prime_division(pd) end def prime_division(generator = nil) FasterPrime.prime_division(self) end def prime? FasterPrime.prime?(self) end def Integer.each_prime(ubound, &block) # :yields: prime Faster.Prime.each(ubound, &block) end end # == faster_prime/base from faster_prime --------------------------------------- # require "faster_prime/version" # (expanded: L705) # require "faster_prime/utils" # (expanded: L22) # require "faster_prime/primality_test" # (expanded: L939) # require "faster_prime/prime_factorization" # (expanded: L1499) # require "faster_prime/sieve" # (expanded: L1960) # require "faster_prime/core_ext" # (expanded: L2137) module FasterPrime module Core include Enumerable class SuccEnumerator < Enumerator alias succ next end def each(ubound = nil, &block) return SuccEnumerator.new(Float::INFINITY) do |y| Sieve.each(ubound) {|p| y << p } end unless block_given? Sieve.each(ubound, &block) end def prime?(n) raise ArgumentError, "Expected an integer, got #{ n }" unless n.respond_to?(:integer?) && n.integer? PrimalityTest.prime?(n) end def prime_division(n) PrimeFactorization.prime_factorization(n).to_a end def int_from_prime_division(pd) pd.inject(1) {|v, (p, e)| v * (p ** e) } end end def self.instance self end include Core extend Core end # == faster_prime/core_ext from faster_prime ----------------------------------- class Integer def Integer.from_prime_division(pd) FasterPrime.int_from_prime_division(pd) end def prime_division(generator = nil) FasterPrime.prime_division(self) end def prime? FasterPrime.prime?(self) end def Integer.each_prime(ubound, &block) # :yields: prime Faster.Prime.each(ubound, &block) end end # == faster_prime from main ---------------------------------------------------- # require "faster_prime/base" # (expanded: L2156) # require "faster_prime/core_ext" # (expanded: L2137) # == nanacl/bsearch_right from main -------------------------------------------- # frozen_string_literal: true class Array def bsearch_right(&) index = bsearch_index_right(&) index && self[index] end def bsearch_index_right(&block) right = bsearch_index { |elem| !block.call(elem) } if right.nil? size - 1 elsif right == 0 nil else right - 1 end end end class Range def bsearch_right(&block) right = bsearch { |elem| !block.call(elem) } if right.nil? last elsif right == first nil else right - 1 end end end # ============================================================================== main.call