// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // RationalBase.hpp // Base definitions for the multi-precision rational-number class #ifndef SANGI_RATIONAL_BASE_HPP #define SANGI_RATIONAL_BASE_HPP #include #include #include #include #include // C++20 three-way comparison operator namespace sangi { // Forward declaration class Float; /** * @brief Multi-precision rational-number class * * Represents an arbitrary-precision rational number (numerator/denominator). * The denominator is always positive, and numerator and denominator are kept * in reduced form. * Uses the Int class internally. */ class SANGI_API Rational { private: Int numerator_; // numerator Int denominator_; // denominator (always positive, reduced) public: // --- Constructors --- Rational(); explicit Rational(int num, int den = 1); explicit Rational(const Int& num); Rational(const Int& num, const Int& den, bool doReduce = true); Rational(Int&& num, Int&& den, bool doReduce = true); Rational(const Rational& other); Rational(Rational&& other) noexcept; explicit Rational(std::string_view str, int base = 10); explicit Rational(double value); explicit Rational(float value); explicit Rational(const Float& value); ~Rational() = default; // --- Accessors --- [[nodiscard]] const Int& numerator() const { return numerator_; } [[nodiscard]] const Int& denominator() const { return denominator_; } // --- Assignment --- Rational& operator=(const Rational& other); Rational& operator=(Rational&& other) noexcept; Rational& operator=(int value); Rational& operator=(double value); Rational& operator=(float value); Rational& operator=(const Int& value); // --- Comparison operators --- friend std::partial_ordering operator<=>(const Rational& lhs, const Rational& rhs); friend bool operator==(const Rational& lhs, const Rational& rhs); friend std::partial_ordering operator<=>(const Rational& lhs, const Int& rhs); friend bool operator==(const Rational& lhs, const Int& rhs); friend std::partial_ordering operator<=>(const Rational& lhs, int rhs); friend bool operator==(const Rational& lhs, int rhs); // --- Unary operators --- const Rational& operator+() const& { return *this; } Rational&& operator+() && { return std::move(*this); } [[nodiscard]] Rational operator-() const&; [[nodiscard]] Rational operator-() &&; // --- Increment / decrement --- Rational& operator++(); [[nodiscard]] Rational operator++(int); Rational& operator--(); [[nodiscard]] Rational operator--(int); // --- Arithmetic (Rational <-> Rational) --- friend Rational operator+(const Rational& lhs, const Rational& rhs); friend Rational operator-(const Rational& lhs, const Rational& rhs); friend Rational operator*(const Rational& lhs, const Rational& rhs); friend Rational operator/(const Rational& lhs, const Rational& rhs); friend Rational operator%(const Rational& lhs, const Rational& rhs); // --- Arithmetic (Rational <-> Int) --- friend Rational operator+(const Rational& lhs, const Int& rhs); friend Rational operator+(const Int& lhs, const Rational& rhs); friend Rational operator-(const Rational& lhs, const Int& rhs); friend Rational operator-(const Int& lhs, const Rational& rhs); friend Rational operator*(const Rational& lhs, const Int& rhs); friend Rational operator*(const Int& lhs, const Rational& rhs); friend Rational operator/(const Rational& lhs, const Int& rhs); friend Rational operator/(const Int& lhs, const Rational& rhs); // --- Arithmetic (Rational <-> int) --- friend Rational operator+(const Rational& lhs, int rhs); friend Rational operator+(int lhs, const Rational& rhs); friend Rational operator-(const Rational& lhs, int rhs); friend Rational operator-(int lhs, const Rational& rhs); friend Rational operator*(const Rational& lhs, int rhs); friend Rational operator*(int lhs, const Rational& rhs); friend Rational operator/(const Rational& lhs, int rhs); friend Rational operator/(int lhs, const Rational& rhs); // --- Compound assignment --- Rational& operator+=(const Rational& rhs); Rational& operator-=(const Rational& rhs); Rational& operator*=(const Rational& rhs); Rational& operator/=(const Rational& rhs); Rational& operator+=(const Int& rhs); Rational& operator-=(const Int& rhs); Rational& operator*=(const Int& rhs); Rational& operator/=(const Int& rhs); Rational& operator+=(int rhs); Rational& operator-=(int rhs); Rational& operator*=(int rhs); Rational& operator/=(int rhs); // --- Power --- [[nodiscard]] Rational pow(int exponent) const; // --- Shift (multiply/divide by powers of 2) --- [[nodiscard]] Rational operator<<(int n) const; [[nodiscard]] Rational operator>>(int n) const; // --- State predicates --- [[nodiscard]] bool isNegative() const { return numerator_.isNegative(); } [[nodiscard]] bool isNonPositive() const { return numerator_.isNegative() || numerator_.isZero(); } [[nodiscard]] bool isZero() const { return numerator_.isZero() && denominator_.isNormal(); } [[nodiscard]] bool isNonZero() const { return !isZero(); } [[nodiscard]] bool isNonNegative() const { return !numerator_.isNegative(); } [[nodiscard]] bool isPositive() const { return numerator_.isPositive(); } [[nodiscard]] bool isOne() const { return numerator_ == denominator_ && denominator_.isNormal(); } [[nodiscard]] bool isInteger() const { return !isNaN() && denominator_.isOne(); } [[nodiscard]] bool isNaN() const { return numerator_.isNaN() || denominator_.isNaN(); } // --- Convenience functions --- [[nodiscard]] Rational doubled() const; [[nodiscard]] Rational halved() const; /// Return the reciprocal (swap numerator and denominator). O(1). /// The reciprocal of zero is NaN (denominator = 0). [[nodiscard]] Rational reciprocal() const { if (isZero() || isNaN()) return Rational(Int(0), Int(0), false); // Keep the sign on the numerator: flip when the denominator is negative if (numerator_.isNegative()) { return Rational(-denominator_, -numerator_, false); } return Rational(denominator_, numerator_, false); } // --- swap --- friend void swap(Rational& a, Rational& b) noexcept; // --- Reduction --- bool reduce(); // --- Conversions --- explicit operator int() const; explicit operator float() const; explicit operator double() const; [[nodiscard]] double toDouble() const; [[nodiscard]] float toFloat() const; [[nodiscard]] Float toMpFloat(int precision = 0) const; [[nodiscard]] std::string toString(int base = 10) const; [[nodiscard]] std::string toDecimal(int digits = 20) const; // --- Continued fractions --- [[nodiscard]] std::vector toContinuedFraction() const; [[nodiscard]] static Rational fromContinuedFraction(const std::vector& cf); // --- Streams --- friend std::ostream& operator<<(std::ostream& os, const Rational& r); friend std::istream& operator>>(std::istream& is, Rational& r); }; // --- Free functions --- [[nodiscard]] Rational abs(const Rational& x); [[nodiscard]] Int floor(const Rational& x); [[nodiscard]] Int ceil(const Rational& x); [[nodiscard]] Int trunc(const Rational& x); [[nodiscard]] Int round(const Rational& x); [[nodiscard]] Rational square(const Rational& x); [[nodiscard]] Int height(const Rational& x); [[nodiscard]] Rational conj(const Rational& x); [[nodiscard]] Rational gcd(const Rational& x, const Rational& y); // --- Utility functions --- [[nodiscard]] Rational inv(const Rational& x); [[nodiscard]] int sgn(const Rational& x); [[nodiscard]] Rational frac(const Rational& x); [[nodiscard]] Rational mediant(const Rational& a, const Rational& b); // --- Rational reconstruction (FLINT-23) --- // Recover p/q (|p| <= N, 0 < q <= D) from a mod m [[nodiscard]] Rational reconstructRational(const Int& a, const Int& m, const Int& N, const Int& D); // --- Harmonic number (FLINT-24) --- // H_n = 1 + 1/2 + ... + 1/n (exact rational) [[nodiscard]] Rational harmonicNumber(unsigned int n); // --- Dedekind sum (FLINT-25) --- // s(h,k) = sum_{i=1}^{k-1} ((i/k))((hi/k)) [[nodiscard]] Rational dedekindSum(const Int& h, const Int& k); // --- Rational enumeration (FLINT-26) --- // Next positive rational in Stern-Brocot order (ascending height) [[nodiscard]] Rational nextMinimal(const Rational& x); // Next positive rational in the Calkin-Wilf sequence [[nodiscard]] Rational nextCalkinWilf(const Rational& x); // --- Farey neighbors (FLINT-27) --- // Left and right neighbors (left, right) in the Farey sequence F_Q [[nodiscard]] std::pair fareyNeighbors( const Rational& x, const Int& Q); // Rational with smallest height in the open interval (a, b) [[nodiscard]] Rational simplestBetween(const Rational& a, const Rational& b); // --- Rational mod (FLINT-29) --- // (p/q) mod m = p * q^{-1} mod m (q and m must be coprime) [[nodiscard]] Int mod(const Rational& x, const Int& m); // --- Large-exponent power (FLINT-30) --- // Power with an Int (multi-precision) exponent [[nodiscard]] Rational pow(const Rational& x, const Int& exponent); } // namespace sangi #endif // SANGI_RATIONAL_BASE_HPP