// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // FactoredInteger.hpp // Factored representation class -- stores an integer as a product of prime factors // // Design: // - Stores pairs of factor and exponent // - Performs multiplication and division at the factor level (without expanding) // - GCD/LCM computable in O(number of prime factors) // - expand() returns the value with all factors multiplied together // - Also supports an additive form #pragma once #include #include #include #include #include #include namespace sangi { // ================================================================ // FactoredInteger class // ================================================================ template class FactoredInteger { private: std::vector factors_; // Factors std::vector exponents_; // Exponents bool additive_; // true: additive form (sum f_i^e_i), false: product form (prod f_i^e_i) public: // ============================================================ // Constructors // ============================================================ /// Default: empty (product form = 1, additive form = 0) FactoredInteger() : additive_(false) {} /// Reserve space for the given number of factors explicit FactoredInteger(size_t n, bool additive = false) : factors_(n), exponents_(n, 1), additive_(additive) { T identity = additive ? T(0) : T(1); int defaultExp = additive ? 1 : 0; std::fill(factors_.begin(), factors_.end(), identity); std::fill(exponents_.begin(), exponents_.end(), defaultExp); } /// Construct from a list of (factor, exponent) pairs (product form) FactoredInteger(std::initializer_list> pairs) : additive_(false) { for (const auto& [f, e] : pairs) { factors_.push_back(f); exponents_.push_back(e); } } // ============================================================ // Accessors // ============================================================ /// Number of factors [[nodiscard]] size_t size() const { return factors_.size(); } /// Whether the container is empty [[nodiscard]] bool empty() const { return factors_.empty(); } /// Whether the form is additive [[nodiscard]] bool isAdditive() const { return additive_; } /// The i-th factor [[nodiscard]] const T& factor(size_t i) const { return factors_[i]; } [[nodiscard]] T& factor(size_t i) { return factors_[i]; } /// The i-th exponent [[nodiscard]] int exponent(size_t i) const { return exponents_[i]; } [[nodiscard]] int& exponent(size_t i) { return exponents_[i]; } /// Access a factor via operator[] [[nodiscard]] const T& operator[](size_t i) const { return factors_[i]; } [[nodiscard]] T& operator[](size_t i) { return factors_[i]; } // ============================================================ // Adding / removing factors // ============================================================ /// Add factor f with exponent e void addFactor(const T& f, int e = 1) { // Merge with an existing factor if it matches for (size_t i = 0; i < factors_.size(); ++i) { if (factors_[i] == f) { exponents_[i] += e; return; } } factors_.push_back(f); exponents_.push_back(e); } /// Remove factors with zero exponent void removeZeroExponents() { size_t j = 0; for (size_t i = 0; i < factors_.size(); ++i) { if (exponents_[i] != 0) { if (j != i) { factors_[j] = std::move(factors_[i]); exponents_[j] = exponents_[i]; } ++j; } } factors_.resize(j); exponents_.resize(j); } /// Change the number of elements void resize(size_t n) { size_t old = factors_.size(); factors_.resize(n); exponents_.resize(n); if (n > old) { T identity = additive_ ? T(0) : T(1); int defaultExp = additive_ ? 1 : 0; for (size_t i = old; i < n; ++i) { factors_[i] = identity; exponents_[i] = defaultExp; } } } // ============================================================ // Expansion (compute the value) // ============================================================ /// Return the value of multiplying (or summing) all factors together [[nodiscard]] T expand() const { if (additive_) { T result = T(0); for (size_t i = 0; i < factors_.size(); ++i) { T term = factors_[i]; for (int j = 1; j < exponents_[i]; ++j) term *= factors_[i]; result += term; } return result; } else { T result = T(1); for (size_t i = 0; i < factors_.size(); ++i) { for (int j = 0; j < exponents_[i]; ++j) result *= factors_[i]; } return result; } } // ============================================================ // Multiplication and division (factor level) // ============================================================ /// Multiplication between product-form values: merge factors [[nodiscard]] FactoredInteger operator*(const FactoredInteger& rhs) const { assert(!additive_ && !rhs.additive_); FactoredInteger result = *this; for (size_t i = 0; i < rhs.factors_.size(); ++i) { result.addFactor(rhs.factors_[i], rhs.exponents_[i]); } return result; } /// Division between product-form values: subtract exponents [[nodiscard]] FactoredInteger operator/(const FactoredInteger& rhs) const { assert(!additive_ && !rhs.additive_); FactoredInteger result = *this; for (size_t i = 0; i < rhs.factors_.size(); ++i) { result.addFactor(rhs.factors_[i], -rhs.exponents_[i]); } return result; } FactoredInteger& operator*=(const FactoredInteger& rhs) { return *this = *this * rhs; } FactoredInteger& operator/=(const FactoredInteger& rhs) { return *this = *this / rhs; } // ============================================================ // GCD / LCM (factor level -- O(number of factors)) // ============================================================ /// GCD between factored values: min of exponents over common factors [[nodiscard]] friend FactoredInteger gcd(const FactoredInteger& a, const FactoredInteger& b) { assert(!a.additive_ && !b.additive_); FactoredInteger result; for (size_t i = 0; i < a.factors_.size(); ++i) { for (size_t j = 0; j < b.factors_.size(); ++j) { if (a.factors_[i] == b.factors_[j]) { int minExp = std::min(a.exponents_[i], b.exponents_[j]); if (minExp > 0) result.addFactor(a.factors_[i], minExp); break; } } } return result; } /// LCM between factored values: max of exponents over all factors [[nodiscard]] friend FactoredInteger lcm(const FactoredInteger& a, const FactoredInteger& b) { assert(!a.additive_ && !b.additive_); FactoredInteger result = a; for (size_t j = 0; j < b.factors_.size(); ++j) { bool found = false; for (size_t i = 0; i < result.factors_.size(); ++i) { if (result.factors_[i] == b.factors_[j]) { result.exponents_[i] = std::max(result.exponents_[i], b.exponents_[j]); found = true; break; } } if (!found) { result.addFactor(b.factors_[j], b.exponents_[j]); } } return result; } // ============================================================ // Exponentiation // ============================================================ /// Multiply every exponent by n [[nodiscard]] FactoredInteger pow(int n) const { FactoredInteger result = *this; for (auto& e : result.exponents_) e *= n; return result; } // ============================================================ // Comparison // ============================================================ [[nodiscard]] bool operator==(const FactoredInteger& rhs) const { if (additive_ != rhs.additive_) return false; if (factors_.size() != rhs.factors_.size()) return false; // Check whether the factor sets are equal (order-independent) std::vector matched(rhs.factors_.size(), false); for (size_t i = 0; i < factors_.size(); ++i) { bool found = false; for (size_t j = 0; j < rhs.factors_.size(); ++j) { if (!matched[j] && factors_[i] == rhs.factors_[j] && exponents_[i] == rhs.exponents_[j]) { matched[j] = true; found = true; break; } } if (!found) return false; } return true; } [[nodiscard]] bool operator!=(const FactoredInteger& rhs) const { return !(*this == rhs); } // ============================================================ // String conversion // ============================================================ [[nodiscard]] std::string toString() const { if (factors_.empty()) { return additive_ ? "0" : "1"; } std::ostringstream oss; for (size_t i = 0; i < factors_.size(); ++i) { if (i > 0) { oss << (additive_ ? " + " : " * "); } oss << "(" << factors_[i] << ")"; if (exponents_[i] != 1) { oss << "^" << exponents_[i]; } } return oss.str(); } friend std::ostream& operator<<(std::ostream& os, const FactoredInteger& fi) { return os << fi.toString(); } }; // ================================================================ // Type traits // ================================================================ template struct is_factored_integer : std::false_type {}; template struct is_factored_integer> : std::true_type {}; template inline constexpr bool is_factored_integer_v = is_factored_integer::value; } // namespace sangi