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

// numeric_state.hpp
// Numeric state-management system
//
// This file defines the state-management system shared by numeric types such as
// multi-precision integers and floating-point numbers.
// It expresses IEEE 754 special values and various states arising in numerical computation.

#ifndef SANGI_NUMERIC_STATE_HPP
#define SANGI_NUMERIC_STATE_HPP

#include <math/core/basic_types.hpp>
#include <string>
#include <type_traits>
#include <concepts>

namespace sangi {

    /**
     * @brief Class describing properties of numeric states.
     *
     * Provides methods that classify numeric states and test their properties.
     */
    class NumericStateTraits {
    public:
        /**
         * @brief Determine whether the state is NaN
         * @param state state to test
         * @return true if NaN
         */
        static constexpr bool isNaN(NumericState state) {
            return state == NumericState::NaN;
        }

        /**
         * @brief Determine whether the state is infinite
         * @param state state to test
         * @return true if infinite
         */
        static constexpr bool isInfinite(NumericState state) {
            return state == NumericState::PositiveInfinity ||
                state == NumericState::NegativeInfinity ||
                state == NumericState::ComplexInfinity;
        }

        /**
         * @brief Determine whether the state failed to converge
         * @param state state to test
         * @return true if not converged
         */
        static constexpr bool isNotConverged(NumericState state) {
            return state == NumericState::NotConverged;
        }

        /**
         * @brief Determine whether the state is divergent
         * @param state state to test
         * @return true if divergent
         */
        static constexpr bool isDivergent(NumericState state) {
            return state == NumericState::Divergent ||
                state == NumericState::NotConverged ||
                state == NumericState::Oscillating ||
                state == NumericState::SlowConvergence;
        }

        /**
         * @brief Determine whether the state is special
         * @param state state to test
         * @return true if a special state (anything other than Normal)
         */
        static constexpr bool isSpecial(NumericState state) {
            return state != NumericState::Normal;
        }

        /**
         * @brief Get the string representation of a state
         * @param state state
         * @return string representation of the state
         */
        static std::string toString(NumericState state) {
            switch (state) {
            case NumericState::Normal: return "Normal";
            case NumericState::NaN: return "NaN";
            case NumericState::PositiveInfinity: return "Infinity";
            case NumericState::NegativeInfinity: return "-Infinity";
            case NumericState::ComplexInfinity: return "ComplexInfinity";
            case NumericState::Overflow: return "Overflow";
            case NumericState::Underflow: return "Underflow";
            case NumericState::Interrupted: return "Interrupted";
            case NumericState::PrecisionLimit: return "PrecisionLimit";
            case NumericState::PositiveZero: return "+Zero";
            case NumericState::NegativeZero: return "-Zero";
            case NumericState::Subnormal: return "Subnormal";
            case NumericState::NotConverged: return "NotConverged";
            case NumericState::Divergent: return "Divergent";
            case NumericState::Oscillating: return "Oscillating";
            case NumericState::SlowConvergence: return "SlowConvergence";
            case NumericState::TruncatedConvergence: return "TruncatedConvergence";
            default: return "Unknown";
            }
        }

        /**
         * @brief Get the string representation of a sign
         * @param sign sign
         * @return string representation of the sign
         */
        static std::string toString(int sign) {
            if (sign < 0) return "Negative";
            else if (sign > 0) return "Positive";
            else return "Zero";
        }

        /**
         * @brief Get the string representation of divergence detail
         * @param detail divergence detail
         * @return string representation of the divergence detail
         */
        static std::string toString(DivergenceDetail detail) {
            switch (detail) {
            case DivergenceDetail::None: return "None";
            case DivergenceDetail::Diverging: return "Diverging";
            case DivergenceDetail::Oscillating: return "Oscillating";
            case DivergenceDetail::Chaotic: return "Chaotic";
            case DivergenceDetail::SlowConvergence: return "SlowConvergence";
            case DivergenceDetail::TruncatedConvergence: return "TruncatedConvergence";
            case DivergenceDetail::ConditionalConvergenceViolation: return "ConditionalConvergenceViolation";
            case DivergenceDetail::AlternatingSeriesDivergence: return "AlternatingSeriesDivergence";
            default: return "Unknown";
            }
        }


        /**
         * @brief Get the string representation of an error cause
         * @param error error cause
         * @return string representation of the error cause
         */
        static std::string toString(NumericError error) {
            switch (error) {
            case NumericError::None: return "None";
            case NumericError::DivideByZero: return "DivideByZero";
            case NumericError::InfiniteIndeterminate: return "InfiniteIndeterminate";
            case NumericError::ZeroTimesInfinity: return "ZeroTimesInfinity";
            case NumericError::NegativeSqrt: return "NegativeSqrt";
            case NumericError::ConversionError: return "ConversionError";
            case NumericError::ExplicitNaN: return "ExplicitNaN";
            case NumericError::Uninitialized: return "Uninitialized";
            case NumericError::InvalidBitPattern: return "InvalidBitPattern";
            case NumericError::InvalidStringConversion: return "InvalidStringConversion";
            case NumericError::OutOfRangeInput: return "OutOfRangeInput";
            case NumericError::NaNPropagation: return "NaNPropagation";
            case NumericError::IntegerConversionError: return "IntegerConversionError";
            case NumericError::FunctionDomainError: return "FunctionDomainError";
            case NumericError::InvalidBitOperation: return "InvalidBitOperation";
            case NumericError::DivergenceError: return "DivergenceError";
            default: return "Unknown";
            }
        }


        /**
         * @brief Determine the state from an error cause
         * @param error error cause
         * @return corresponding state
         */
        static NumericState errorToState(NumericError error) {
            switch (error) {
            case NumericError::None:
                return NumericState::Normal;
            case NumericError::DivideByZero:
                return NumericState::NaN; // For 0/0
                // Note: for n/0 the result is PositiveInfinity or NegativeInfinity depending on sign
            case NumericError::InfiniteIndeterminate:
            case NumericError::ZeroTimesInfinity:
            case NumericError::NegativeSqrt:
            case NumericError::ExplicitNaN:
            case NumericError::Uninitialized:
            case NumericError::InvalidBitPattern:
            case NumericError::InvalidStringConversion:
            case NumericError::NaNPropagation:
            case NumericError::FunctionDomainError:
            case NumericError::InvalidBitOperation:
                return NumericState::NaN;
            case NumericError::ConversionError:
            case NumericError::OutOfRangeInput:
            case NumericError::IntegerConversionError:
                return NumericState::Overflow;
            case NumericError::DivergenceError:
                return NumericState::Divergent;
            default:
                return NumericState::NaN;
            }
        }


        /**
         * @brief Estimate a default error cause from a state
         * @param state state
         * @return inferred error cause
         * @note This is not a 1-to-1 mapping, so it is not a complete inverse
         */
        static NumericError stateToDefaultError(NumericState state) {
            switch (state) {
            case NumericState::Normal:
            case NumericState::PositiveZero:
            case NumericState::NegativeZero:
                return NumericError::None;
            case NumericState::NaN:
                return NumericError::ExplicitNaN;
            case NumericState::PositiveInfinity:
            case NumericState::NegativeInfinity:
            case NumericState::ComplexInfinity:
                // Default is unknown infinity
                return NumericError::InfiniteIndeterminate;
            case NumericState::Overflow:
                return NumericError::OutOfRangeInput;
            case NumericState::Underflow:
                return NumericError::OutOfRangeInput;
            case NumericState::Interrupted:
                return NumericError::None;
            case NumericState::PrecisionLimit:
                return NumericError::None;
            case NumericState::Subnormal:
                return NumericError::None;
            case NumericState::NotConverged:
            case NumericState::Divergent:
            case NumericState::Oscillating:
            case NumericState::SlowConvergence:
                return NumericError::DivergenceError;
            default:
                return NumericError::None;
            }
        }


        /**
         * @brief Determine whether the state requires an error code
         * @param state state to test
         * @return true if an error code is required
         */
        static bool requiresErrorCode(NumericState state) {
            return state != NumericState::Normal &&
                state != NumericState::PositiveZero &&
                state != NumericState::NegativeZero;
        }

    };

    /**
     * @brief Template class that exposes numeric-state properties
     *
     * Specialized per numeric type to provide that type's state-management facilities.
     * Acts as a layer independent of the algebraic-concept system.
     */
    template<typename T>
    struct numeric_state_traits {
        // Not supported by default
        static constexpr bool is_supported = false;

        // Dummy methods that are not usable as-is
        static bool isNormal(const T&) { return true; }
        static bool isNaN(const T&) { return false; }
        static bool isInfinite(const T&) { return false; }
        static bool isNotConverged(const T&) { return false; }
        static NumericState getState(const T&) { return NumericState::Normal; }
        static NumericError getError(const T&) { return NumericError::None; }
        static int getSign(const T&) { return 0; }
    };

    // Helper functions for fundamental types

    /**
     * @brief Get the sign
     * @param value value
     * @return sign
     */
    template<std::integral T>
    constexpr int getSign(T value) {
        return value < 0 ? -1 :
            value > 0 ? 1 : 0;
    }

    template<std::floating_point T>
    constexpr int getSign(T value) {
        return value < 0 ? -1 :
            value > 0 ? 1 : 0;
    }

    /**
     * @brief Negate a sign
     * @param sign sign
     * @return negated sign
     */
    constexpr int negateSign(int sign) {
        return -sign;
    }

    /**
     * @brief Determine the sign of a multiplication or division result from two signs
     * @param a first sign
     * @param b second sign
     * @return resulting sign
     */
    constexpr int combineSignsMultiply(int a, int b) {
        if (a == 0 || b == 0) return 0;
        return (a == b) ? 1 : -1;
    }

    /**
     * @brief Determine the sign of an addition result from two signs
     * @param a first sign
     * @param b second sign
     * @param absCompare absolute-value comparison (negative: |a|<|b|, 0: |a|=|b|, positive: |a|>|b|)
     * @return resulting sign
     */
    constexpr int combineSignsAdd(int a, int b, int absCompare) {
        // When the signs match, the result has the same sign
        if (a == b) return a;

        // When the signs differ, the result takes the sign of the larger absolute value
        if (absCompare < 0) return b;       // |a| < |b|
        else if (absCompare > 0) return a;  // |a| > |b|
        else return 0;                      // |a| = |b|
    }

    // Concepts for state-managed types

    /**
     * @brief Concept for types that support state management
     */
    template<typename T>
    concept HasNumericState = requires(T a) {
        { numeric_state_traits<T>::isNormal(a) } -> std::convertible_to<bool>;
        { numeric_state_traits<T>::getState(a) } -> std::convertible_to<NumericState>;
        { numeric_state_traits<T>::getError(a) } -> std::convertible_to<NumericError>;
        { numeric_state_traits<T>::getSign(a) } -> std::convertible_to<int>;
    };

    /**
     * @brief Concept for types that support divergence detail
     */
    template<typename T>
    concept HasDivergenceHandling = requires(T a) {
        { numeric_state_traits<T>::isNotConverged(a) } -> std::convertible_to<bool>;
        { numeric_state_traits<T>::getDivergenceDetail(a) } -> std::convertible_to<DivergenceDetail>;
    };

    /**
     * @brief Concept for types that support overflow detection
     */
    template<typename T>
    concept HasOverflowDetection = requires(T a) {
        { numeric_state_traits<T>::isOverflow(a) } -> std::convertible_to<bool>;
    };

} // namespace sangi

#endif // SANGI_NUMERIC_STATE_HPP