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

namespace calx {

    // Int型の前方宣言（循環参照を避けるため）
    class Int;

    namespace concepts {
        // 基本コンセプトの前方宣言
        template<typename T>
        concept Field = requires(T a, T b) {
            { a + b } -> std::convertible_to<T>;
            { a - b } -> std::convertible_to<T>;
            { a* b } -> std::convertible_to<T>;
            { a / b } -> std::convertible_to<T>;
            { -a } -> std::convertible_to<T>;
            { T(0) } -> std::convertible_to<T>;
            { T(1) } -> std::convertible_to<T>;
        };

        template<typename T>
        concept OrderedField = Field<T> && requires(T a, T b) {
            { a < b } -> std::convertible_to<bool>;
            { a <= b } -> std::convertible_to<bool>;
            { a > b } -> std::convertible_to<bool>;
            { a >= b } -> std::convertible_to<bool>;
        };

        template<typename V, typename S>
        concept VectorSpace = requires(V v, V w, S s) {
            { v + w } -> std::convertible_to<V>;
            { v - w } -> std::convertible_to<V>;
            { s* v } -> std::convertible_to<V>;
            { v* s } -> std::convertible_to<V>;
            { -v } -> std::convertible_to<V>;
        };

        template<typename M, typename T>
        concept MatrixOf = requires(M m, std::size_t i, std::size_t j) {
            typename M::value_type;
            { m(i, j) } -> std::convertible_to<T>;
            { m.rows() } -> std::convertible_to<std::size_t>;
            { m.cols() } -> std::convertible_to<std::size_t>;
        };

        template<typename V, typename T>
        concept VectorOf = requires(V v, std::size_t i) {
            typename V::value_type;
            { v[i] } -> std::convertible_to<T>;
            { v.size() } -> std::convertible_to<std::size_t>;
        };

        // 数値状態管理のサポートコンセプト
        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>;
        };

        template<typename T>
        concept HasOverflowDetection = requires(T a) {
            { numeric_state_traits<T>::isOverflow(a) } -> std::convertible_to<bool>;
        };

        // 数値状態管理の検出と処理が可能な型
        template<typename T>
        concept HasNumericState = requires(T a) {
            { a.isNormal() } -> std::convertible_to<bool>;
            { a.isNaN() } -> std::convertible_to<bool>;
            { a.isInfinite() } -> std::convertible_to<bool>;
            { a.getState() } -> std::convertible_to<typename T::state_type>;
        };
    }

    // 数値型特性

    // 数値型カテゴリの識別
    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 {};

    // 数値型特性の基本テンプレート
    template<typename T>
    struct numeric_traits {
        // デフォルトは未定義
        static constexpr bool is_supported = false;

        static constexpr T abs(T x) { return x < T(0) ? -x : x; }

        /// ピボット選択: a は b より良いピボットか？
        /// 既定: |a| > |b| (絶対値最大が安定)
        /// Rational 等の厳密型では高さ最小にオーバーライドする
        static bool pivotBetter(const T& a, const T& b) {
            return abs(a) > abs(b);
        }
    };

    // 特殊化: 浮動小数点型
    template<>
    struct numeric_traits<float> {
        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<float>::epsilon(); }
        static constexpr float infinity() { return std::numeric_limits<float>::infinity(); }
        static constexpr float quiet_NaN() { return std::numeric_limits<float>::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); }

        // 数値状態関連のユーティリティ - constexprを削除
        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<double> {
        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<double>::epsilon(); }
        static constexpr double infinity() { return std::numeric_limits<double>::infinity(); }
        static constexpr double quiet_NaN() { return std::numeric_limits<double>::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); }

        // 数値状態関連のユーティリティ - constexprを削除
        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;
        }
    };

    // 長倍精度浮動小数点型（long double）
    template<>
    struct numeric_traits<long double> {
        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<long double>::epsilon(); }
        static constexpr long double infinity() { return std::numeric_limits<long double>::infinity(); }
        static constexpr long double quiet_NaN() { return std::numeric_limits<long double>::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); }

        // 数値状態関連のユーティリティ - constexprを削除
        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;
        }
    };

    // 整数型の特殊化（int用）
    template<>
    struct numeric_traits<int> {
        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; } // 整数の最小単位は1

        // 整数型では"infinity"は表現できないが、最大値を返す
        static constexpr int infinity() { return std::numeric_limits<int>::max(); }

        // 整数型ではNaNは表現できない（特別な値をエラー状態として返す）
        static constexpr int quiet_NaN() { return std::numeric_limits<int>::min(); }

        static constexpr int abs(int value) {
            return value < 0 ? (value == std::numeric_limits<int>::min() ?
                std::numeric_limits<int>::max() : -value) : value;
        }

        static constexpr int conj(int value) { return value; } // 整数は共役複素数が自分自身
        static constexpr int norm(int value) { return value * value; } // 整数のノルムは二乗
        static bool pivotBetter(int a, int b) { return abs(a) > abs(b); }

        static NumericState getState(int) {
            return NumericState::Normal; // 整数型は常にNormal
        }

        static NumericError getError(int) {
            return NumericError::None; // 整数型のデフォルトエラーはなし
        }

        static int getSign(int value) {
            return value < 0 ? -1 : (value > 0 ? 1 : 0);
        }
    };

    // 他の整数型の特殊化
    template<>
    struct numeric_traits<long> : numeric_traits<int> {
        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<long long> : numeric_traits<int> {
        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;
        }
    };

    // Intクラスの前方定義とトレイトの枠組み
    // 実際の実装はIntBase.hppで行う
    // ここではコンパイルエラーを避けるための仮実装を提供
    template<>
    struct numeric_traits<Int>;

    // Int型の数値状態特性特殊化の前方定義
    template<>
    struct numeric_state_traits<Int>;

    // 特殊化: 複素数型
    template<typename T>
    struct numeric_traits<std::complex<T>> {
        using value_type = std::complex<T>;
        using real_type = T;
        using category = complex_tag;

        static constexpr bool is_supported = numeric_traits<T>::is_supported;
        static constexpr bool is_complex = true;
        static constexpr bool is_integer = false;
        static constexpr bool is_floating_point = numeric_traits<T>::is_floating_point;

        static std::complex<T> zero() { return std::complex<T>(0, 0); }
        static std::complex<T> one() { return std::complex<T>(1, 0); }
        static T epsilon() { return numeric_traits<T>::epsilon(); }

        // constexprを削除（絶対値は実部と虚部の二乗和の平方根）
        static T abs(const std::complex<T>& value) {
            const T real = value.real();
            const T imag = value.imag();
            return std::sqrt(real * real + imag * imag);
        }

        static std::complex<T> conj(const std::complex<T>& value) { return std::conj(value); }
        static T norm(const std::complex<T>& value) { return std::norm(value); }
        static bool pivotBetter(const std::complex<T>& a, const std::complex<T>& b) {
            return abs(a) > abs(b);
        }

        // 複素数型の数値状態関連のユーティリティ - constexprを削除
        static bool isNaN(const std::complex<T>& value) {
            return numeric_traits<T>::isNaN(value.real()) || numeric_traits<T>::isNaN(value.imag());
        }

        static bool isInfinite(const std::complex<T>& value) {
            return numeric_traits<T>::isInfinite(value.real()) || numeric_traits<T>::isInfinite(value.imag());
        }

        static bool isNormal(const std::complex<T>& value) {
            return !isNaN(value) && !isInfinite(value) && (value.real() != 0 || value.imag() != 0);
        }

        static bool isFinite(const std::complex<T>& value) {
            return numeric_traits<T>::isFinite(value.real()) && numeric_traits<T>::isFinite(value.imag());
        }

        static NumericState getState(const std::complex<T>& value) {
            if (isNaN(value)) return NumericState::NaN;

            bool real_inf = numeric_traits<T>::isInfinite(value.real());
            bool imag_inf = numeric_traits<T>::isInfinite(value.imag());

            if (real_inf || imag_inf) {
                // 複素無限大は符号を持たないが、実部/虚部の符号に基づいて特殊状態を返す
                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<T>& value) {
            // NaNの場合はエラー原因を明確にする
            if (isNaN(value)) {
                return NumericError::ExplicitNaN;
            }
            // 無限大の場合でもエラー原因を明確にする
            if (isInfinite(value)) {
                return NumericError::OutOfRangeInput;
            }
            return NumericError::None;
        }

        static int getSign(const std::complex<T>& value) {
            // 複素数の符号は実部の符号を優先
            if (value.real() != 0) {
                return value.real() < 0 ? -1 :
                    value.real() > 0 ? 1 : 0;
            }

            // 実部がゼロなら虚部の符号を使用
            if (value.imag() != 0) {
                return value.imag() < 0 ? -1 : 1;
            }

            // 両方ゼロならゼロ
            return 0;
        }
    };

} // namespace calx
