TaskMethodCost
one trial division by and
one deterministic Miller–Rabin (7 fixed bases) multiplications per base
factor one Pollard–Brent rho + Miller–Rabinexpected
factor many smallest-prime-factor sieve (Sieves) each
all primes in a range(segmented) sieve
Mersenne numbers Lucas–Lehmer
cryptographic sizes, need a certificateECPPheuristic
theoretical interest onlyAKS, no unproven hypotheses needed

Smaller base sets suffice for smaller bounds

is deterministic for ; for ; for . Use a shorter set when the input is bounded — fewer modpows per test.

Three recurring bugs

  1. is not prime, and must be handled before anything else.
  2. i * i <= n overflows when is near — use i <= n / i or __int128.
  3. Fermat’s test alone is wrong: Carmichael numbers (561, 1105, 1729, …) satisfy for every coprime . Korselt: is Carmichael iff it is squarefree, composite and for every prime . Miller–Rabin’s square-root check is exactly what fixes this.
using u64 = unsigned long long; using u128 = __uint128_t;
u64 mulm(u64 a, u64 b, u64 m) { return (u128)a * b % m; }
u64 powm(u64 a, u64 e, u64 m) { u64 r = 1 % m; for (a %= m; e; e >>= 1, a = mulm(a, a, m)) if (e & 1) r = mulm(r, a, m); return r; }
bool isPrime(u64 n) {                                     // deterministic for all 64-bit n
    if (n < 2) return false;
    for (u64 p : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}) if (n % p == 0) return n == p;
    int s = __builtin_ctzll(n - 1); u64 d = (n - 1) >> s;
    for (u64 a : {2ULL, 325ULL, 9375ULL, 28178ULL, 450775ULL, 9780504ULL, 1795265022ULL}) {
        if (a % n == 0) continue;
        u64 x = powm(a, d, n);
        if (x == 1 || x == n - 1) continue;
        bool comp = true;
        for (int i = 1; i < s && comp; i++) { x = mulm(x, x, n); if (x == n - 1) comp = false; }
        if (comp) return false;                            // a witnesses that n is composite
    }
    return true;
}
u64 pollard(u64 n) {                                      // a nontrivial factor of an odd composite n (Brent, batched gcd)
    static mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
    if (n % 2 == 0) return 2;
    for (;;) {
        u64 c = rng() % (n - 1) + 1, y = rng() % n, x = y, ys = y, g = 1, q = 1;
        auto f = [&](u64 v) { return (mulm(v, v, n) + c) % n; };
        for (u64 r = 1; g == 1; r <<= 1) {
            x = y; for (u64 i = 0; i < r; i++) y = f(y);
            for (u64 k = 0; k < r && g == 1; k += 128) {
                ys = y;
                for (u64 i = 0; i < min<u64>(128, r - k); i++) y = f(y), q = mulm(q, x > y ? x - y : y - x, n);
                g = std::gcd(q, n);
            }
        }
        if (g == n) do ys = f(ys), g = std::gcd(x > ys ? x - ys : ys - x, n); while (g == 1);   // replay the last block
        if (g != n) return g;                              // otherwise the walk closed without splitting: new c
    }
}
void factorRec(u64 n, vector<u64>& out) {
    if (n == 1) return;
    if (isPrime(n)) { out.push_back(n); return; }
    u64 d = pollard(n); factorRec(d, out); factorRec(n / d, out);
}
vector<pair<u64, int>> factorize(u64 n) {                 // sorted (prime, exponent)
    vector<u64> ps;
    for (u64 p : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}) while (n % p == 0) ps.push_back(p), n /= p;
    factorRec(n, ps); sort(all(ps));
    vector<pair<u64, int>> res; for (u64 p : ps) { if (sz(res) && res.back().first == p) res.back().second++; else res.push_back({p, 1}); }
    return res;
}
vector<u64> divisorsFrom(const vector<pair<u64, int>>& f) {   // all divisors (unsorted), d(n) of them
    vector<u64> ds{1};
    for (auto [p, e] : f) { int m = sz(ds); u64 pk = 1; for (int k = 1; k <= e; k++) { pk *= p; for (int i = 0; i < m; i++) ds.push_back(ds[i] * pk); } }
    return ds;
}
From Value
number of divisors
sum of divisors
Euler
Carmichael (exponent of ), except for
perfect square / -th powerall even / divisible by
primitive root exists (Primitive Roots)

Beyond Pollard rho: Baillie–PSW (one Miller–Rabin base-2 test plus a Lucas test, no known counterexample) is a fast primality heuristic; Pollard’s Kangaroo adapts the same random-walk idea to discrete logarithms; Lenstra’s ECM factors numbers with a medium-sized factor (up to ~60 digits) faster than rho; the quadratic sieve / GNFS are what’s needed for genuinely large semiprimes, where rho is hopeless — none of these are typically hand-implemented in a contest, but recognise the names.

Wilson’s theorem and factorials modulo

iff is prime; for composite , , and . Too slow as a test, but it evaluates products like , and in factorial-mod- problems (Factorials mod p).

Useful consequences: (drop the factor from Wilson), and more generally a factorial near converts to a small one via — handy when a formula needs for small . holds only for the three known Wilson primes 5, 13, 563 (none other known below ). Defining as the product of integers not divisible by , Wilson gives — the engine behind the generalised Lucas theorem (Factorials mod p).

Special forms worth checking before general factorization

FormApproach
(perfect power)check each from 2 to , taking an integer -th root
(difference of squares)Fermat’s method: fast when ‘s two factors are close to , useless otherwise
strip powers of two with __builtin_ctzll first
factorials and binomialsnever factor the number itself — use Legendre’s formula for the exponent of in