// 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 #include #include #include // Adds Rational math functions + numeric_limits to the std namespace. // This is a bridge so that Matrix works where LinAlg / generic // algorithms call `std::abs(T)` / `std::numeric_limits`. // // Note: Rational only makes sense for operations closed within Q; irrational // operations such as sqrt/exp/log are not provided. Use Matrix 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 { 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() #include #if defined(__cpp_lib_format) && __cpp_lib_format >= 202106L template<> struct std::formatter { 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() #endif // SANGI_RATIONAL_HPP