// 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 <math/core/mp/Int.hpp>
#include <string>
#include <iosfwd>
#include <vector>
#include <compare>  // 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<Int> toContinuedFraction() const;
        [[nodiscard]] static Rational fromContinuedFraction(const std::vector<Int>& 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<Rational, Rational> 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
