// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later namespace sangi { // Forward declaration of the Int type (to avoid circular references) class Int; namespace concepts { // Forward declaration of basic concepts template concept Field = requires(T a, T b) { { a + b } -> std::convertible_to; { a - b } -> std::convertible_to; { a* b } -> std::convertible_to; { a / b } -> std::convertible_to; { -a } -> std::convertible_to; { T(0) } -> std::convertible_to; { T(1) } -> std::convertible_to; }; template concept OrderedField = Field && requires(T a, T b) { { a < b } -> std::convertible_to; { a <= b } -> std::convertible_to; { a > b } -> std::convertible_to; { a >= b } -> std::convertible_to; }; template concept VectorSpace = requires(V v, V w, S s) { { v + w } -> std::convertible_to; { v - w } -> std::convertible_to; { s* v } -> std::convertible_to; { v* s } -> std::convertible_to; { -v } -> std::convertible_to; }; template concept MatrixOf = requires(M m, std::size_t i, std::size_t j) { typename M::value_type; { m(i, j) } -> std::convertible_to; { m.rows() } -> std::convertible_to; { m.cols() } -> std::convertible_to; }; template concept VectorOf = requires(V v, std::size_t i) { typename V::value_type; { v[i] } -> std::convertible_to; { v.size() } -> std::convertible_to; }; // Support concept for numeric-state management template concept HasDivergenceHandling = requires(T a) { { numeric_state_traits::isNotConverged(a) } -> std::convertible_to; { numeric_state_traits::getDivergenceDetail(a) } -> std::convertible_to; }; template concept HasOverflowDetection = requires(T a) { { numeric_state_traits::isOverflow(a) } -> std::convertible_to; }; // Types where numeric-state management can be detected and handled template concept HasNumericState = requires(T a) { { a.isNormal() } -> std::convertible_to; { a.isNaN() } -> std::convertible_to; { a.isInfinite() } -> std::convertible_to; { a.getState() } -> std::convertible_to; }; } // Numeric-type traits // Identification of numeric-type categories struct numeric_category_tag {}; struct real_tag : numeric_category_tag {}; struct complex_tag : numeric_category_tag {}; struct integer_tag : real_tag {}; struct floating_point_tag : real_tag {}; // Base template for numeric-type traits template struct numeric_traits { // Undefined by default static constexpr bool is_supported = false; static constexpr T abs(T x) { return x < T(0) ? -x : x; } /// Pivot selection: is a a better pivot than b? /// Default: |a| > |b| (largest magnitude is stable) /// For exact types such as Rational, override to minimize height static bool pivotBetter(const T& a, const T& b) { return abs(a) > abs(b); } }; // Specialization: floating-point types template<> struct numeric_traits { using value_type = float; using category = floating_point_tag; static constexpr bool is_supported = true; static constexpr bool is_complex = false; static constexpr bool is_integer = false; static constexpr bool is_floating_point = true; static constexpr float zero() { return 0.0f; } static constexpr float one() { return 1.0f; } static constexpr float epsilon() { return std::numeric_limits::epsilon(); } static constexpr float infinity() { return std::numeric_limits::infinity(); } static constexpr float quiet_NaN() { return std::numeric_limits::quiet_NaN(); } static constexpr float abs(float value) { return value < 0.0f ? -value : value; } static constexpr float conj(float value) { return value; } static constexpr float norm(float value) { return value * value; } static bool pivotBetter(float a, float b) { return abs(a) > abs(b); } // Numeric-state-related utilities - constexpr removed static bool isNaN(float value) { return std::isnan(value); } static bool isInfinite(float value) { return std::isinf(value); } static bool isNormal(float value) { return std::isnormal(value); } static bool isFinite(float value) { return std::isfinite(value); } static NumericState getState(float value) { if (std::isnan(value)) return NumericState::NaN; if (std::isinf(value)) { return value > 0 ? NumericState::PositiveInfinity : NumericState::NegativeInfinity; } if (std::isnormal(value) || value == 0.0f) return NumericState::Normal; return NumericState::Subnormal; } static NumericError getError(float value) { if (std::isnan(value)) return NumericError::ExplicitNaN; return NumericError::None; } }; template<> struct numeric_traits { using value_type = double; using category = floating_point_tag; static constexpr bool is_supported = true; static constexpr bool is_complex = false; static constexpr bool is_integer = false; static constexpr bool is_floating_point = true; static constexpr double zero() { return 0.0; } static constexpr double one() { return 1.0; } static constexpr double epsilon() { return std::numeric_limits::epsilon(); } static constexpr double infinity() { return std::numeric_limits::infinity(); } static constexpr double quiet_NaN() { return std::numeric_limits::quiet_NaN(); } static constexpr double abs(double value) { return value < 0.0 ? -value : value; } static constexpr double conj(double value) { return value; } static constexpr double norm(double value) { return value * value; } static bool pivotBetter(double a, double b) { return abs(a) > abs(b); } // Numeric-state-related utilities - constexpr removed static bool isNaN(double value) { return std::isnan(value); } static bool isInfinite(double value) { return std::isinf(value); } static bool isNormal(double value) { return std::isnormal(value); } static bool isFinite(double value) { return std::isfinite(value); } static NumericState getState(double value) { if (std::isnan(value)) return NumericState::NaN; if (std::isinf(value)) { return value > 0 ? NumericState::PositiveInfinity : NumericState::NegativeInfinity; } if (std::isnormal(value) || value == 0.0) return NumericState::Normal; return NumericState::Subnormal; } static NumericError getError(double value) { if (std::isnan(value)) return NumericError::ExplicitNaN; return NumericError::None; } }; // Extended-precision floating-point type (long double) template<> struct numeric_traits { using value_type = long double; using category = floating_point_tag; static constexpr bool is_supported = true; static constexpr bool is_complex = false; static constexpr bool is_integer = false; static constexpr bool is_floating_point = true; static constexpr long double zero() { return 0.0L; } static constexpr long double one() { return 1.0L; } static constexpr long double epsilon() { return std::numeric_limits::epsilon(); } static constexpr long double infinity() { return std::numeric_limits::infinity(); } static constexpr long double quiet_NaN() { return std::numeric_limits::quiet_NaN(); } static constexpr long double abs(long double value) { return value < 0.0L ? -value : value; } static constexpr long double conj(long double value) { return value; } static constexpr long double norm(long double value) { return value * value; } static bool pivotBetter(long double a, long double b) { return abs(a) > abs(b); } // Numeric-state-related utilities - constexpr removed static bool isNaN(long double value) { return std::isnan(value); } static bool isInfinite(long double value) { return std::isinf(value); } static bool isNormal(long double value) { return std::isnormal(value); } static bool isFinite(long double value) { return std::isfinite(value); } static NumericState getState(long double value) { if (std::isnan(value)) return NumericState::NaN; if (std::isinf(value)) { return value > 0 ? NumericState::PositiveInfinity : NumericState::NegativeInfinity; } if (std::isnormal(value) || value == 0.0L) return NumericState::Normal; return NumericState::Subnormal; } static NumericError getError(long double value) { if (std::isnan(value)) return NumericError::ExplicitNaN; return NumericError::None; } }; // Integer-type specialization (for int) template<> struct numeric_traits { using value_type = int; using category = integer_tag; static constexpr bool is_supported = true; static constexpr bool is_complex = false; static constexpr bool is_integer = true; static constexpr bool is_floating_point = false; static constexpr int zero() { return 0; } static constexpr int one() { return 1; } static constexpr int epsilon() { return 1; } // The smallest unit of an integer is 1 // Integer types cannot represent "infinity"; return the maximum value static constexpr int infinity() { return std::numeric_limits::max(); } // Integer types cannot represent NaN (return a special value as an error state) static constexpr int quiet_NaN() { return std::numeric_limits::min(); } static constexpr int abs(int value) { return value < 0 ? (value == std::numeric_limits::min() ? std::numeric_limits::max() : -value) : value; } static constexpr int conj(int value) { return value; } // The conjugate of an integer is itself static constexpr int norm(int value) { return value * value; } // The norm of an integer is its square static bool pivotBetter(int a, int b) { return abs(a) > abs(b); } static NumericState getState(int) { return NumericState::Normal; // Integer types are always Normal } static NumericError getError(int) { return NumericError::None; // Default error for integer types is none } static int getSign(int value) { return value < 0 ? -1 : (value > 0 ? 1 : 0); } }; // Specializations for other integer types template<> struct numeric_traits : numeric_traits { using value_type = long; static constexpr long zero() { return 0L; } static constexpr long one() { return 1L; } static constexpr long abs(long value) { return value < 0L ? -value : value; } static NumericState getState(long) { return NumericState::Normal; } static NumericError getError(long) { return NumericError::None; } static int getSign(long value) { return value < 0L ? -1 : value > 0L ? 1 : 0; } }; template<> struct numeric_traits : numeric_traits { using value_type = long long; static constexpr long long zero() { return 0LL; } static constexpr long long one() { return 1LL; } static constexpr long long abs(long long value) { return value < 0LL ? -value : value; } static NumericState getState(long long) { return NumericState::Normal; } static NumericError getError(long long) { return NumericError::None; } static int getSign(long long value) { return value < 0LL ? -1 : value > 0LL ? 1 : 0; } }; // Forward definition of the Int class and the traits framework // The actual implementation lives in IntBase.hpp // A provisional implementation is provided here to avoid compile errors template<> struct numeric_traits; // Forward definition of the numeric-state traits specialization for the Int type template<> struct numeric_state_traits; // Specialization: complex-number types template struct numeric_traits> { using value_type = std::complex; using real_type = T; using category = complex_tag; static constexpr bool is_supported = numeric_traits::is_supported; static constexpr bool is_complex = true; static constexpr bool is_integer = false; static constexpr bool is_floating_point = numeric_traits::is_floating_point; static std::complex zero() { return std::complex(0, 0); } static std::complex one() { return std::complex(1, 0); } static T epsilon() { return numeric_traits::epsilon(); } // constexpr removed (absolute value is the square root of the sum of squares of real and imaginary parts) static T abs(const std::complex& value) { const T real = value.real(); const T imag = value.imag(); return std::sqrt(real * real + imag * imag); } static std::complex conj(const std::complex& value) { return std::conj(value); } static T norm(const std::complex& value) { return std::norm(value); } static bool pivotBetter(const std::complex& a, const std::complex& b) { return abs(a) > abs(b); } // Numeric-state utilities for complex types - constexpr removed static bool isNaN(const std::complex& value) { return numeric_traits::isNaN(value.real()) || numeric_traits::isNaN(value.imag()); } static bool isInfinite(const std::complex& value) { return numeric_traits::isInfinite(value.real()) || numeric_traits::isInfinite(value.imag()); } static bool isNormal(const std::complex& value) { return !isNaN(value) && !isInfinite(value) && (value.real() != 0 || value.imag() != 0); } static bool isFinite(const std::complex& value) { return numeric_traits::isFinite(value.real()) && numeric_traits::isFinite(value.imag()); } static NumericState getState(const std::complex& value) { if (isNaN(value)) return NumericState::NaN; bool real_inf = numeric_traits::isInfinite(value.real()); bool imag_inf = numeric_traits::isInfinite(value.imag()); if (real_inf || imag_inf) { // Complex infinity has no sign; return a special state based on the sign of real/imaginary parts if (real_inf && value.real() > 0) return NumericState::PositiveInfinity; if (real_inf && value.real() < 0) return NumericState::NegativeInfinity; if (imag_inf) return NumericState::ComplexInfinity; return NumericState::ComplexInfinity; } return NumericState::Normal; } static NumericError getError(const std::complex& value) { // For NaN, clarify the error cause if (isNaN(value)) { return NumericError::ExplicitNaN; } // Even for infinity, clarify the error cause if (isInfinite(value)) { return NumericError::OutOfRangeInput; } return NumericError::None; } static int getSign(const std::complex& value) { // The sign of a complex number prefers the sign of the real part if (value.real() != 0) { return value.real() < 0 ? -1 : value.real() > 0 ? 1 : 0; } // If the real part is zero, use the sign of the imaginary part if (value.imag() != 0) { return value.imag() < 0 ? -1 : 1; } // If both are zero, the sign is zero return 0; } }; } // namespace sangi