Smaller number-theoretic tools: intersections of arithmetic progressions, coprime bases, Pythagorean triples, counting square roots of 1 and -th powers, residues of in a range, Fibonacci numbers, power towers, balanced ternary and NAF, elliptic curves over , and Gaussian integers. Dependencies from other notes: extgcd, invMod (GCD), crtMerge, crt, garner, diophantine, countSolutions (Congruences and CRT), floorSum (Floor Sums), power (Modular Arithmetic, used by PowerTower and twoSquares); the simplest fraction in an interval is simplestBetween in Big Integers and Fractions.

FunctionAnswersCost
apIntersection(a1, d1, a2, d2) () as first + step * t
coprimeBasis(vals)pairwise coprime numbers that generate every value multiplicatively
forEachPythagorean(N, f)all primitive ,
countSquareRootsOfOne(m)
countKthPowers(n, k)
countResiduesInRange(a, b, m, n, lo, hi) by floor sums
pair<ll, ll> apIntersection(ll a1, ll d1, ll a2, ll d2) {   // d1, d2 >= 1; returns {first, step}, first = -1 if empty
    auto [x, l] = crtMerge(((a1 % d1) + d1) % d1, d1, ((a2 % d2) + d2) % d2, d2);
    if (x < 0) return {-1, -1};
    ll lo = max(a1, a2);                                   // first common element >= both starting points
    if (x < lo) x += (lo - x + l - 1) / l * l;
    return {x, l};
}
 
vector<ll> coprimeBasis(const vector<ll>& vals) {          // values >= 1
    vector<ll> basis;
    for (ll v : vals) {
        vector<ll> work{v};
        while (!work.empty()) {
            ll x = work.back(); work.pop_back();
            if (x == 1) continue;
            bool split = false;
            for (int i = 0; i < sz(basis); i++) {
                ll g = gcd(basis[i], x);
                if (g == 1) continue;
                ll b = basis[i]; basis.erase(basis.begin() + i);
                work.push_back(g); work.push_back(b / g); work.push_back(x / g);
                split = true; break;
            }
            if (!split) basis.push_back(x);
        }
    }
    sort(all(basis)); basis.erase(unique(all(basis)), basis.end());
    return basis;
}
 
template<class F> void forEachPythagorean(ll N, F f) {   // primitive triples (a, b, c), c <= N
    for (ll m = 2; m * m + 1 <= N; m++)
        for (ll k = 1 + (m % 2); k < m && m * m + k * k <= N; k += 2)
            if (gcd(m, k) == 1) f(m * m - k * k, 2 * m * k, m * m + k * k);
}
 
ll countSquareRootsOfOne(ll m) {
    ll res = 1;
    for (ll p = 2; p * p <= m; p++) {
        if (m % p) continue;
        int e = 0; while (m % p == 0) m /= p, e++;
        res *= p == 2 ? (e == 1 ? 1 : e == 2 ? 2 : 4) : 2;
    }
    if (m > 1) res *= m == 2 ? 1 : 2;
    return res;
}
 
ll countKthPowers(ll n, ll k) {                            // k >= 1; values of a^k mod n, including 0 and non-units
    auto ipow = [](ll b, ll e) { ll r = 1; while (e-- > 0) r *= b; return r; };
    auto f = [&](auto&& self, ll p, ll e) -> ll {          // count modulo p^e
        if (e <= 0) return 1;                              // only 0
        if (p == 2) {
            if (e == 1) return 2;
            ll u = ipow(2, e - 2) / gcd(k, ipow(2, e - 2));
            if (k % 2) u *= 2;
            return u + self(self, 2, e - k);
        }
        ll phi = ipow(p, e) - ipow(p, e - 1);
        return phi / gcd(k, phi) + self(self, p, e - k);
    };
    ll res = 1, x = n;
    for (ll p = 2; p * p <= x; p++) { if (x % p) continue; int e = 0; while (x % p == 0) x /= p, e++; res *= f(f, p, e); }
    if (x > 1) res *= f(f, x, 1);
    return res;
}
 
ll countResiduesInRange(ll a, ll b, ll m, ll n, ll lo, ll hi) {   // 0 <= lo <= hi <= m
    a = ((a % m) + m) % m; b = ((b % m) + m) % m;
    auto below = [&](ll t) -> ll {                         // #i with (a i + b) mod m < t
        if (t <= 0) return 0;
        if (t >= m) return n;
        return n - (floorSum(n, m, a, b + m - t) - floorSum(n, m, a, b));
    };
    return below(hi) - below(lo);
}

Fibonacci numbers

, , ; (); is the last that fits in long long, the last in unsigned long long.

MethodTimeUse
iteration
matrix power
fast doublingsame, about 2 times faster, no matrix struct
Binet inexact beyond
Identity
addition formula
, doubling
, hence strong divisibility: common factors of Fibonacci numbers become gcds of indices
Cassini
Lucas numbers , : ,
Pisano period ( is periodic)
multiplicative structure
, with equality iff (known theorem)
conjectured always (Wall), verified far
: ; : from or
, , so

Zeckendorf: every positive integer is uniquely a sum of non-consecutive Fibonacci numbers (greedy: take the largest that fits); it underlies Wythoff’s game, Fibonacci coding (self-delimiting codes) and “no two adjacent terms” representations.

Where Fibonacci appearsCount
binary strings of length with no two adjacent 1s; subsets of with no two consecutive
domino tilings of ; stairs climbed 1 or 2 at a time
worst case of the Euclidean algorithmconsecutive Fibonacci inputs
golden-section search, minimum AVL tree of height ratio , nodes
tribonacci / -bonacci matrix power, or Bostan–Mori: Linear Recurrences
pair<ll, ll> fibPair(ll n, ll mod) {                       // {F(n) mod m, F(n+1) mod m}, fast doubling
    if (n == 0) return {0, 1 % mod};
    auto [a, b] = fibPair(n >> 1, mod);
    ll c = (ll)((i128)a * ((2 * b - a + mod) % mod) % mod);  // F(2k)
    ll d = (ll)(((i128)a * a + (i128)b * b) % mod);           // F(2k+1)
    return n & 1 ? pair{d, (c + d) % mod} : pair{c, d};
}
 
vector<ll> zeckendorf(ll n) {                              // n >= 1: non-consecutive Fibonacci terms, decreasing
    vector<ll> f{1, 2}; while (f.back() <= n - f[sz(f) - 2]) f.push_back(f.back() + f[sz(f) - 2]);
    vector<ll> res;
    for (int i = sz(f) - 1; i >= 0 && n > 0; i--) if (f[i] <= n) res.push_back(f[i]), n -= f[i];
    return res;
}

Power towers

for any modulus (no coprimality): use whenever , and decide “is the exponent at least ?” from the exact value capped at , which a tower of more than five numbers always exceeds. The modulus chain reaches 1 within levels. Classic: Codeforces 906D Power Tower. Cost: levels, each for (memoized) plus for the power. (The folklore version that stores ” if , else ” can lose the "" information after an exponent has been reduced; carrying the capped exact value avoids that question.)

struct PowerTower {                                        // uses power(a, e, m) from Modular Arithmetic
    static constexpr ll CAP = 1LL << 62;
    vector<ll> a; vector<int> nextOne; map<ll, ll> phiMemo;
    PowerTower(const vector<ll>& a) : a(a), nextOne(sz(a) + 1, sz(a)) {   // a_i >= 1
        for (int i = sz(a) - 1; i >= 0; i--) nextOne[i] = a[i] == 1 ? i : nextOne[i + 1];
    }
    ll phi(ll n) {
        auto it = phiMemo.find(n); if (it != phiMemo.end()) return it->second;
        ll res = n, m = n;
        for (ll p = 2; p * p <= m; p++) if (m % p == 0) { while (m % p == 0) m /= p; res = res / p * (p - 1); }
        if (m > 1) res = res / m * (m - 1);
        return phiMemo[n] = res;
    }
    static ll satPow(ll b, ll e) { ll r = 1; while (e-- > 0 && b > 1) { if (r > CAP / b) return CAP; r *= b; } return r; }
    ll capped(int l, int r) {                              // min(tower a[l..r], CAP)
        int k = min(nextOne[l], r);                        // a 1 at k makes everything above it irrelevant
        if (k - l >= 6) return CAP;                        // at least 6 numbers >= 2: beyond 2^62
        ll v = min(a[k], CAP);
        for (int i = k - 1; i >= l; i--) v = satPow(a[i], min(v, 64LL));
        return v;
    }
    ll tower(int l, int r, ll m) {                         // a[l..r] as a tower, mod m
        if (m == 1) return 0;
        if (l == r) return a[l] % m;
        ll ph = phi(m), ec = capped(l + 1, r);
        ll e = ec < ph ? ec : tower(l + 1, r, ph) + ph;    // exact exponent, or an equivalent one >= phi(m)
        return power(a[l], e, m);
    }
};

Balanced ternary and non-adjacent form

Base 3 with digits : every integer, positive or negative, has a unique representation without a sign (, , ). Conversion: take ; a 2 becomes with a carry. Bachet’s balance: weights , each on the empty pan (), with the object () or unused (), weigh every integer mass up to (Bachet’s Weights). The Soviet Setun computer (1958) used balanced ternary.

Property
representationunique for every integer
negationflip every digit; no two’s-complement asymmetry
signcarried by the leading digit
roundingtruncation gives the nearest value
range of digits, symmetric
digits neededabout $\log_3(2
ProblemWhy balanced ternary
weigh any mass with weights the defining example
as with distinct exponentsexactly the representation (always possible, uniquely)
games where each move is state = digits
minimum number of signed powers of 3 summing to the number of non-zero digits
ternary search trees, three-way partitionthe three-outcome analogy

Non-adjacent form (NAF): base 2 with digits and no two adjacent non-zero digits; unique, of minimal weight, with on average one third of the digits non-zero (versus one half in binary): exponentiation and elliptic-curve scalar multiplication get about 11% faster (fewer multiplications; subtraction of a point is as cheap as addition). Related systems: binary, Zeckendorf (Fibonacci base, no two adjacent 1s), factorial base (digit in ; permutation ranking via the Lehmer code), Gray code (consecutive values differ in one bit), negabinary (base , negatives without a sign): Modular Arithmetic has negative bases and fromBalancedTernary.

vector<int> toBalancedTernary(ll n) {                      // least significant digit first, digits in {-1, 0, 1}
    vector<int> d;
    while (n) {
        int r = int(((n % 3) + 3) % 3);
        if (r == 2) { d.push_back(-1); n = (n + 1) / 3; }   // exact division
        else { d.push_back(r); n = (n - r) / 3; }
    }
    return d;
}
 
vector<int> naf(ll n) {                                    // n >= 0, least significant first, no two adjacent non-zeros
    vector<int> d;
    while (n) {
        if (n & 1) { int z = 2 - int(n % 4); d.push_back(z); n -= z; }   // z = 1 if n = 1 mod 4, -1 if n = 3 mod 4
        else d.push_back(0);
        n >>= 1;
    }
    return d;
}

Elliptic curves over

Points of ( prime, ) plus the point at infinity form an abelian group under the chord-and-tangent law: slope , or when doubling; . Hasse: . Scalar multiplication by double-and-add is the one-way function of elliptic-curve cryptography (the discrete logarithm is hard), and Lenstra’s factorization runs the same arithmetic modulo a composite , succeeding when an inversion fails (Theoretical Algorithms). Cost: per addition (one inverse), per multiplication, point counting by Euler’s criterion.

struct ECPoint {
    ll x = 0, y = 0; bool inf = false;
    static ECPoint infinity() { return {0, 0, true}; }
    bool operator==(const ECPoint& o) const { return inf == o.inf && (inf || (x == o.x && y == o.y)); }
};
 
struct EllipticCurve {
    ll a, b, p;
    EllipticCurve(ll a_, ll b_, ll p_) : a((a_ % p_ + p_) % p_), b((b_ % p_ + p_) % p_), p(p_) {}
    ll mul(ll x, ll y) const { return (ll)((i128)x * y % p); }
    ll pw(ll x, ll e) const { ll r = 1; for (x %= p; e; e >>= 1, x = mul(x, x)) if (e & 1) r = mul(r, x); return r; }
    ll inv(ll x) const { return pw(x, p - 2); }
    bool contains(const ECPoint& P) const { return P.inf || mul(P.y, P.y) == (mul(mul(P.x, P.x), P.x) + mul(a, P.x) + b) % p; }
    ECPoint add(const ECPoint& P, const ECPoint& Q) const {
        if (P.inf) return Q;
        if (Q.inf) return P;
        ll s;
        if (P.x == Q.x) {
            if ((P.y + Q.y) % p == 0) return ECPoint::infinity();
            s = mul((3 * mul(P.x, P.x) + a) % p, inv(2 * P.y % p));
        } else s = mul(((Q.y - P.y) % p + p) % p, inv(((Q.x - P.x) % p + p) % p));
        ll x = ((mul(s, s) - P.x - Q.x) % p + 2 * p) % p;
        ll y = ((mul(s, (P.x - x + p) % p) - P.y) % p + p) % p;
        return {x, y, false};
    }
    ECPoint negate(const ECPoint& P) const { return P.inf ? P : ECPoint{P.x, (p - P.y) % p, false}; }
    ECPoint multiply(ECPoint P, ll k) const { ECPoint R = ECPoint::infinity(); for (; k > 0; k >>= 1, P = add(P, P)) if (k & 1) R = add(R, P); return R; }
    ll countPoints() const {                               // including infinity
        ll cnt = 1;
        for (ll x = 0; x < p; x++) { ll rhs = (mul(mul(x, x), x) + mul(a, x) + b) % p; cnt += rhs == 0 ? 1 : pw(rhs, (p - 1) / 2) == 1 ? 2 : 0; }
        return cnt;
    }
};

Gaussian integers

with norm is a Euclidean domain (rounding each coordinate of leaves a remainder of norm ), so Euclid’s algorithm computes gcds. Units: . Primes: ; rational primes ; and the two factors of each . This gives an two-square decomposition once is known, and the representation count (with for ).

struct GI { ll a, b; };                                     // a + b i (keep norms below about 4e18)
GI operator-(GI x, GI y) { return {x.a - y.a, x.b - y.b}; }
GI operator*(GI x, GI y) { return {x.a * y.a - x.b * y.b, x.a * y.b + x.b * y.a}; }
ll roundDiv(i128 n, i128 d) { return (ll)(n >= 0 ? (2 * n + d) / (2 * d) : -((-2 * n + d) / (2 * d))); }   // d > 0
GI gquot(GI x, GI y) {                                      // nearest Gaussian integer to x / y
    i128 nn = (i128)y.a * y.a + (i128)y.b * y.b;
    i128 re = (i128)x.a * y.a + (i128)x.b * y.b, im = (i128)x.b * y.a - (i128)x.a * y.b;
    return {roundDiv(re, nn), roundDiv(im, nn)};
}
GI ggcd(GI x, GI y) { while (y.a || y.b) { GI r = x - gquot(x, y) * y; x = y; y = r; } return x; }
pair<ll, ll> twoSquares(ll p) {                             // prime p = 1 (mod 4) -> (a, b) with a^2 + b^2 = p
    ll t = 0;
    for (ll c = 2;; c++) if (power(c, (p - 1) / 2, p) == p - 1) { t = power(c, (p - 1) / 4, p); break; }   // t^2 = -1
    GI g = ggcd({p, 0}, {t, 1});
    return {llabs(g.a), llabs(g.b)};
}