// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// Rational.hpp
// Main header file for the arbitrary-precision rational number library.

#ifndef SANGI_RATIONAL_HPP
#define SANGI_RATIONAL_HPP

#include <math/core/mp/Int.hpp>
#include <math/core/mp/Rational/RationalBase.hpp>
#include <math/core/mp/Rational/RationalTraits.hpp>
#include <math/core/mp/Rational/RationalConstants.hpp>

// Adds Rational math functions + numeric_limits to the std namespace.
// This is a bridge so that Matrix<Rational> works where LinAlg / generic
// algorithms call `std::abs(T)` / `std::numeric_limits<T>`.
//
// Note: Rational only makes sense for operations closed within Q; irrational
// operations such as sqrt/exp/log are not provided. Use Matrix<Float> if needed.
namespace std {
    inline sangi::Rational abs(const sangi::Rational& x) { return sangi::abs(x); }

    // Numeric-state predicates: Rational has no infinity but does have NaN.
    inline bool isnan(const sangi::Rational& x)    { return x.isNaN(); }
    inline bool isinf(const sangi::Rational& /*x*/) { return false; }
    inline bool isfinite(const sangi::Rational& x) { return !x.isNaN(); }

    template<>
    struct numeric_limits<sangi::Rational> {
        static constexpr bool is_specialized = true;
        // Rationals are dense = no smallest unit -> epsilon = 0.
        // ("near-zero" checks in algorithms reduce to exact equality.)
        static sangi::Rational epsilon() { return sangi::Rational(0); }
        static sangi::Rational min() { return sangi::Rational(0); }
        static sangi::Rational max() {
            // Arbitrary precision has no true max, so return a large value as a dummy.
            return sangi::Rational(sangi::Int(1) << 1000);
        }
        static sangi::Rational lowest() { return -max(); }
        static constexpr bool is_signed = true;
        static constexpr bool is_integer = false;
        static constexpr bool is_exact = true;  // <- Rational performs exact computation
        static constexpr bool has_infinity = false;
        static constexpr bool has_quiet_NaN = true;
        static sangi::Rational quiet_NaN() {
            // Construct NaN via Rational(nan_int, 1).
            return sangi::Rational(sangi::Int::nan(), sangi::Int(1), false);
        }
    };
}

// --- std::formatter specialization ---
#if __has_include(<format>)
#include <format>
#if defined(__cpp_lib_format) && __cpp_lib_format >= 202106L

template<>
struct std::formatter<sangi::Rational> {
    int precision_ = -1;
    char type_ = 0;  // 0=default(num/den), 'f'=decimal

    constexpr auto parse(std::format_parse_context& ctx) {
        auto it = ctx.begin();
        auto end = ctx.end();
        if (it != end && *it == '.') {
            ++it;
            precision_ = 0;
            while (it != end && *it >= '0' && *it <= '9') {
                precision_ = precision_ * 10 + (*it - '0');
                ++it;
            }
        }
        if (it != end && *it != '}') {
            if (*it == 'f') {
                type_ = 'f';
                ++it;
            } else {
                throw std::format_error("invalid format spec for Rational");
            }
        }
        return it;
    }

    auto format(const sangi::Rational& val, std::format_context& ctx) const {
        std::string result;
        if (type_ == 'f') {
            result = val.toDecimal(precision_ >= 0 ? precision_ : 20);
        } else {
            result = val.toString();
        }
        return std::format_to(ctx.out(), "{}", result);
    }
};

#endif // __cpp_lib_format
#endif // __has_include(<format>)

#endif // SANGI_RATIONAL_HPP
