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

/**
 * @file convergence_criteria.hpp
 * @brief 数値アルゴリズムの収束判定基準の定義
 * @author MKL Algebra Library
 */

#ifndef CALX_CONVERGENCE_CRITERIA_HPP
#define CALX_CONVERGENCE_CRITERIA_HPP

#include <cstddef>
#include <limits>
#include <type_traits>
#include <concepts>
#include "../concepts/algebraic_concepts.hpp"

namespace calx {

    /**
     * @brief 数値アルゴリズムの収束判定基準クラス
     *
     * 反復法による数値解法の収束判定基準を定義します。
     * 絶対誤差、相対誤差、最大反復回数による判定が可能です。
     *
     * @tparam T 値の型（通常は浮動小数点型）
     */
    template<typename T>
        requires concepts::OrderedField<T>
    class ConvergenceCriteria {
    private:
        T abs_tol_;      ///< 絶対許容誤差
        T rel_tol_;      ///< 相対許容誤差
        std::size_t max_iter_; ///< 最大反復回数

    public:
        /**
         * @brief デフォルトコンストラクタ
         *
         * デフォルト値を設定します：
         * - 絶対許容誤差: 1e-8 (倍精度の場合)
         * - 相対許容誤差: 1e-6 (倍精度の場合)
         * - 最大反復回数: 1000
         */
        ConvergenceCriteria()
            : abs_tol_(std::is_same_v<T, float> ? 1e-6f : 1e-8)
            , rel_tol_(std::is_same_v<T, float> ? 1e-4f : 1e-6)
            , max_iter_(1000)
        {
        }

        /**
         * @brief パラメータ指定のコンストラクタ
         *
         * @param abs_tol 絶対許容誤差
         * @param rel_tol 相対許容誤差
         * @param max_iter 最大反復回数
         */
        ConvergenceCriteria(T abs_tol, T rel_tol, std::size_t max_iter)
            : abs_tol_(abs_tol)
            , rel_tol_(rel_tol)
            , max_iter_(max_iter)
        {
            validate_parameters();
        }

        /**
         * @brief 絶対許容誤差の取得
         *
         * @return 絶対許容誤差
         */
        T absolute_tolerance() const {
            return abs_tol_;
        }

        /**
         * @brief 絶対許容誤差の設定
         *
         * @param tol 新しい絶対許容誤差
         */
        void set_absolute_tolerance(T tol) {
            abs_tol_ = tol;
            validate_parameters();
        }

        /**
         * @brief 相対許容誤差の取得
         *
         * @return 相対許容誤差
         */
        T relative_tolerance() const {
            return rel_tol_;
        }

        /**
         * @brief 相対許容誤差の設定
         *
         * @param tol 新しい相対許容誤差
         */
        void set_relative_tolerance(T tol) {
            rel_tol_ = tol;
            validate_parameters();
        }

        /**
         * @brief 最大反復回数の取得
         *
         * @return 最大反復回数
         */
        std::size_t max_iterations() const {
            return max_iter_;
        }

        /**
         * @brief 最大反復回数の設定
         *
         * @param iter 新しい最大反復回数
         */
        void set_max_iterations(std::size_t iter) {
            max_iter_ = iter;
        }

        /**
         * @brief 指定したノルム値が収束基準を満たすかをチェック
         *
         * 絶対誤差または相対誤差のいずれかが基準を満たすかを確認します。
         *
         * @param norm 現在のノルム値
         * @param initial_norm 初期ノルム値（相対誤差計算用）
         * @return 収束基準を満たす場合はtrue
         */
        bool converged(T norm, T initial_norm) const {
            return norm <= abs_tol_ || norm <= rel_tol_ * initial_norm;
        }

        /**
         * @brief 指定した誤差値が絶対誤差基準を満たすかをチェック
         *
         * @param error 現在の誤差値
         * @return 絶対誤差基準を満たす場合はtrue
         */
        bool absolute_converged(T error) const {
            return error <= abs_tol_;
        }

        /**
         * @brief 指定した誤差値が相対誤差基準を満たすかをチェック
         *
         * @param error 現在の誤差値
         * @param reference 基準値（通常は初期値や目標値のノルム）
         * @return 相対誤差基準を満たす場合はtrue
         */
        bool relative_converged(T error, T reference) const {
            return error <= rel_tol_ * reference;
        }

        /**
         * @brief 指定した反復回数が最大回数を超えているかをチェック
         *
         * @param iter 現在の反復回数
         * @return 反復回数が最大値を超えている場合はtrue
         */
        bool max_iterations_exceeded(std::size_t iter) const {
            return iter >= max_iter_;
        }

        /**
         * @brief 厳格な収束判定モードを設定
         *
         * 絶対誤差と相対誤差の両方を満たす必要がある厳格モードに設定します。
         *
         * @param strict 厳格モードを有効にする場合はtrue
         */
        void set_strict_mode(bool strict = true) {
            // このシンプルな実装では厳格モードは提供しませんが、
            // 将来的な拡張性のために関数を定義しています。
        }

        /**
         * @brief 収束基準の詳細を文字列として取得
         *
         * @return 収束基準の詳細を表す文字列
         */
        std::string to_string() const {
            std::ostringstream oss;
            oss << "ConvergenceCriteria:\n";
            oss << "  Absolute tolerance: " << abs_tol_ << "\n";
            oss << "  Relative tolerance: " << rel_tol_ << "\n";
            oss << "  Maximum iterations: " << max_iter_;
            return oss.str();
        }

        /**
         * @brief 収束状態から数値状態への変換
         *
         * @param converged 収束したかどうか
         * @param max_iter_exceeded 最大反復回数を超えたかどうか
         * @param error エラー情報を格納する変数
         * @return 対応する数値状態
         */
        NumericState to_numeric_state(bool converged, bool max_iter_exceeded, NumericError& error) const {
            if (converged) {
                error = NumericError::None;
                return NumericState::Normal;
            }

            if (max_iter_exceeded) {
                error = NumericError::ConditionalConvergenceViolation;
                return NumericState::TruncatedConvergence;
            }

            error = NumericError::DivergenceError;
            return NumericState::NotConverged;
        }

    private:
        /**
         * @brief パラメータの妥当性を検証
         *
         * 無効なパラメータが設定されていた場合、例外をスローします。
         */
        void validate_parameters() {
            if (abs_tol_ < 0) {
                throw std::invalid_argument("Absolute tolerance must be non-negative");
            }
            if (rel_tol_ < 0) {
                throw std::invalid_argument("Relative tolerance must be non-negative");
            }
            // マシンイプシロンより小さい値を設定した場合は警告を発するか、
            // 最小値で調整することも考えられます
            const T eps = std::numeric_limits<T>::epsilon();
            if (abs_tol_ < eps) {
                abs_tol_ = eps;
            }
            if (rel_tol_ < eps) {
                rel_tol_ = eps;
            }
        }
    };

    // 一般的なケースに対するエイリアステンプレート
    /**
     * @brief 単精度浮動小数点用の収束判定基準
     */
    using FloatConvergenceCriteria = ConvergenceCriteria<float>;

    /**
     * @brief 倍精度浮動小数点用の収束判定基準
     */
    using DoubleConvergenceCriteria = ConvergenceCriteria<double>;

    // MKL対応の特殊化（必要に応じて実装）
#ifdef CALX_USE_MKL
// MKL用の特殊化や拡張をここに実装
#endif

} // namespace calx

#endif // CALX_CONVERGENCE_CRITERIA_HPP