// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // IntFactorTable.hpp // Prime factor table (Sieve of Eratosthenes, recording the largest prime factor) #ifndef SANGI_INT_FACTOR_TABLE_HPP #define SANGI_INT_FACTOR_TABLE_HPP #include "IntBase.hpp" #include #include namespace sangi { /** * @brief Prime factor table (largest-prime-factor version) * * Uses the Sieve of Eratosthenes to build a table recording the largest * prime factor of each integer. Recording the largest prime factor (LPF) * instead of the smallest prime factor (SPF) has the following advantages: * * 1. **Faster sieve construction**: unconditional overwrite, no branching * 2. **Faster factorization**: dividing by larger primes first shrinks * the digit count more quickly * 3. **Simpler code**: no checks needed * * Algorithm: * - For prime p, unconditionally overwrite table[m] = p for every multiple m * - Processed in increasing prime order, so the largest prime factor remains * at the end * * Usage example: * IntFactorTable table(10000); // Build the factor table up to 10000 * int lpf = table[360]; // 360 = 2^3 * 3^2 * 5 -> lpf = 5 * auto factors = table.factorize(Int(360)); // {{2,3}, {3,2}, {5,1}} */ class IntFactorTable { private: std::vector table_; // Stores odd numbers only (even numbers are always prime factor 2) int max_value_; // Maximum value of the table public: /** * @brief Constructor (builds the table) * * @param max_value Maximum value of the table (default: 1000000 = 1M) * * Builds the prime factor table up to max_value. * Construction takes O(n log log n) time. * * Memory usage: * - max_value = 1000: about 2KB * - max_value = 1000000: about 2MB * - max_value = 100000000: about 200MB */ explicit IntFactorTable(int max_value = 1000000); /** * @brief Get the maximum value of the table */ int maxValue() const { return max_value_; } /** * @brief Get the largest prime factor of the given integer * * @param n Integer (0 <= n <= max_value) * @return Largest prime factor (0 when n is prime; a special value for n = 0,1) * * Return value: * - n = 0 or n = 1: 0 * - n is prime: 0 * - n is composite: largest prime factor * - n is even (n > 2): 2, or the largest odd prime factor * * Note: returns 0 when n > max_value (out of range) */ int operator[](int n) const; /** * @brief Factorize the given integer into primes * * @param n Integer to factorize * @return Vector of (prime, exponent) pairs {{p1, e1}, {p2, e2}, ...} * ordered by p1 < p2 < ... (ascending) * * Examples: * factorize(Int(360)) -> {{2,3}, {3,2}, {5,1}} (360 = 2^3 * 3^2 * 5) * factorize(Int(17)) -> {{17,1}} (prime) * factorize(Int(1)) -> {} (empty) * factorize(Int(0)) -> {{0,1}} (special case) * * Special cases: * - n = 0: {{0, 1}} * - n = 1: {} (empty vector) * - n < 0: returns the factorization of |n| * - n > max_value: parts beyond the table are factorized by trial division * - n.isNaN(): {} (empty) * - n.isInfinite(): {} (empty) */ std::vector> factorize(const Int& n) const; /** * @brief Save the table to a file * * @param filename File name (default: "FactorTable.dat") * @return true on success, false on failure * * Saved in binary format: * 1. max_value (4 bytes) * 2. table size (4 bytes) * 3. table contents (uint32_t x size) */ bool saveToFile(const std::string& filename = "FactorTable.dat") const; /** * @brief Load the table from a file * * @param filename File name (default: "FactorTable.dat") * @return true on success, false on failure * * Fails if the file does not exist or its size does not match. */ bool loadFromFile(const std::string& filename = "FactorTable.dat"); private: /** * @brief Build the table using the Sieve of Eratosthenes (LPF version) * * Algorithm: * for p = 3 to sqrt(max_value) step 2: * if table[p] == 0: // p is prime * for m = p^2 to max_value step 2*p: * table[m] = p // unconditional overwrite (no condition check) * * Difference from the smallest-prime-factor (SPF) version: * - SPF: if (table[m] == 0) table[m] = p; // requires a check * - LPF: table[m] = p; // no check needed */ void buildTable(); /** * @brief Convert an odd-number index to the index in the internal array * * @param n Odd number * @return Index in the internal array * * Because only odd numbers are stored, use the index obtained by * dividing n by 2. * Example: n=3 -> index=1, n=5 -> index=2, n=7 -> index=3 */ size_t oddIndex(int n) const { return static_cast(n >> 1); } }; } // namespace sangi #endif // SANGI_INT_FACTOR_TABLE_HPP