250 lines
4.5 KiB
Nim
250 lines
4.5 KiB
Nim
import math
|
|
|
|
type
|
|
Factor* = object
|
|
prime*: uint64
|
|
power*: uint64
|
|
|
|
Factors* = object
|
|
numPrimes*: uint64
|
|
factors*: seq[Factor]
|
|
|
|
U64Array* = object
|
|
elems*: seq[uint64]
|
|
|
|
# =========================
|
|
# Utilities
|
|
# =========================
|
|
|
|
proc factorAppend*(f: var Factors, p, k: uint64) =
|
|
f.factors.add Factor(prime: p, power: k)
|
|
inc f.numPrimes
|
|
|
|
# =========================
|
|
# Math helpers
|
|
# =========================
|
|
|
|
proc u64Pow*(a, e: uint64): uint64 =
|
|
var base = a
|
|
var exp = e
|
|
var r: uint64 = 1
|
|
while exp > 0:
|
|
if (exp and 1) == 1:
|
|
r *= base
|
|
base *= base
|
|
exp = exp shr 1
|
|
r
|
|
|
|
proc u64NthRoot*(x, n: uint64): uint64 =
|
|
uint64(pow(float64(x), 1.0 / float64(n)))
|
|
|
|
proc u64Binom*(n, k: uint64): uint64 =
|
|
if k > n: return 0
|
|
var r: uint64 = 1
|
|
for i in 1..k:
|
|
r = r * (n - k + i) div i
|
|
r
|
|
|
|
proc i64Gcd*(a, b: int64): int64 =
|
|
var x = a
|
|
var y = b
|
|
while y != 0:
|
|
let t = x mod y
|
|
x = y
|
|
y = t
|
|
x
|
|
|
|
proc i64Lcm*(a, b: int64): int64 =
|
|
(a div i64Gcd(a, b)) * b
|
|
|
|
proc i64Egcd*(a, b: int64, x, y: var int64): int64 =
|
|
if b == 0:
|
|
x = 1
|
|
y = 0
|
|
return a
|
|
|
|
var x1, y1: int64
|
|
let g = i64Egcd(b, a mod b, x1, y1)
|
|
x = y1
|
|
y = x1 - y1 * (a div b)
|
|
g
|
|
|
|
# =========================
|
|
# Prime estimate helpers
|
|
# =========================
|
|
|
|
proc maxPrimeDivs*(max: uint64): uint64 =
|
|
let primorials = [
|
|
6'u64,30,210,2310,30030,
|
|
510510,9699690,223092870
|
|
]
|
|
|
|
for i, p in primorials:
|
|
if max < p:
|
|
return uint64(i+1)
|
|
|
|
15
|
|
|
|
proc maxPrimesLe*(max: uint64): uint64 =
|
|
uint64(1.25506 * float64(max) / ln(float64(max)))
|
|
|
|
# =========================
|
|
# Omega sieve
|
|
# =========================
|
|
|
|
proc sieveOmega*(max: uint64): seq[uint8] =
|
|
result = newSeq[uint8](max+1)
|
|
|
|
for n in 2..max:
|
|
if result[n] != 0: continue
|
|
var m = n
|
|
while m <= max:
|
|
inc result[m]
|
|
m += n
|
|
|
|
# =========================
|
|
# Largest prime factor sieve
|
|
# =========================
|
|
|
|
proc sieveLargestFactors*(max: uint64): seq[uint64] =
|
|
result = newSeq[uint64](max+1)
|
|
|
|
for p in 2..max:
|
|
if result[p] != 0: continue
|
|
var m = p
|
|
while m <= max:
|
|
result[m] = p
|
|
m += p
|
|
|
|
# =========================
|
|
# Smallest prime factor sieve
|
|
# =========================
|
|
|
|
proc sieveSmallestFactors*(max: uint64): seq[uint32] =
|
|
result = newSeq[uint32](max+1)
|
|
|
|
let r = uint64(sqrt(float64(max)))
|
|
|
|
for p in 2..r:
|
|
if result[p] != 0: continue
|
|
|
|
var m = p*p
|
|
while m <= max:
|
|
if result[m] == 0:
|
|
result[m] = uint32(p)
|
|
m += p
|
|
|
|
for i in 2..max:
|
|
if result[i] == 0:
|
|
result[i] = uint32(i)
|
|
|
|
# =========================
|
|
# Factorization
|
|
# =========================
|
|
|
|
proc fillFactorsFromSmallest*(res: var Factors, n: uint64, spf: seq[uint32]) =
|
|
var x = n
|
|
res.numPrimes = 0
|
|
res.factors.setLen(0)
|
|
|
|
while x > 1:
|
|
let p = uint64(spf[x])
|
|
var k: uint64 = 0
|
|
|
|
while spf[x] == uint32(p):
|
|
x = x div p
|
|
inc k
|
|
|
|
factorAppend(res, p, k)
|
|
# =========================
|
|
# Totient sieve
|
|
# =========================
|
|
|
|
proc sievePhi*(max: uint64): seq[uint64] =
|
|
result = newSeq[uint64](max+1)
|
|
|
|
for i in 1..max:
|
|
result[i] = i
|
|
|
|
for p in 2..max:
|
|
if result[p] != p: continue
|
|
|
|
var m = p
|
|
while m <= max:
|
|
result[m] -= result[m] div p
|
|
m += p
|
|
|
|
# =========================
|
|
# Sigma_0 (divisor count)
|
|
# =========================
|
|
|
|
proc sigma0*(max: uint64): seq[uint64] =
|
|
result = newSeq[uint64](max+1)
|
|
|
|
for i in 1..max:
|
|
var j = i
|
|
while j <= max:
|
|
inc result[j]
|
|
j += i
|
|
|
|
# =========================
|
|
# Mobius sieve
|
|
# =========================
|
|
|
|
proc mobius*(max: uint64): seq[int8] =
|
|
result = newSeq[int8](max+1)
|
|
|
|
for i in 1..max:
|
|
result[i] = 1
|
|
|
|
for p in 2..max:
|
|
if result[p] == 1:
|
|
var m = p
|
|
while m <= max:
|
|
result[m] = -result[m]
|
|
m += p
|
|
|
|
let p2 = p*p
|
|
m = p2
|
|
while m <= max:
|
|
result[m] = 0
|
|
m += p2
|
|
|
|
# =========================
|
|
# Prime sieve
|
|
# =========================
|
|
|
|
proc sievePrimes*(max: uint64): seq[uint64] =
|
|
var isComp = newSeq[bool](max+1)
|
|
|
|
for p in 2..max:
|
|
if isComp[p]: continue
|
|
|
|
result.add p
|
|
|
|
if p*p <= max:
|
|
var m = p*p
|
|
while m <= max:
|
|
isComp[m] = true
|
|
m += p
|
|
|
|
# =========================
|
|
# Main
|
|
# =========================
|
|
|
|
when isMainModule:
|
|
|
|
let max = 10_000_000'u64
|
|
|
|
let primes = sievePrimes(max)
|
|
|
|
echo "Primes up to ", max, ":"
|
|
for p in primes:
|
|
stdout.write($p & " ")
|
|
echo ""
|
|
|
|
let phi = sievePhi(max)
|
|
echo "phi(36) = ", phi[36]
|
|
|
|
let mu = mobius(max)
|
|
echo "mu(30) = ", mu[30]
|