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

// convergence.hpp
// 収束性評価拡張インターフェース
//
// このファイルでは、数値計算における収束性の評価と制御のための
// インターフェースを提供します。反復計算や数列の収束判定に使用されます。
//
// 主な機能:
// - 収束閾値の設定と取得
// - 最大反復回数の設定と取得
// - 収束判定関数
// - 発散検出メカニズム

#ifndef CALX_CONVERGENCE_HPP
#define CALX_CONVERGENCE_HPP

#include <math/core/numeric_state.hpp>
#include <cmath>
#include <cstddef>
#include <limits>
#include <utility>  // C++23 std::unreachable

namespace calx {

    /**
     * @brief 収束状態を表すenum
     */
    enum class ConvergenceState {
        Converged,            ///< 収束した
        NotConverged,         ///< 収束していない
        MaxIterationsReached, ///< 最大反復回数に達した
        Oscillating,          ///< 振動している
        Diverging             ///< 発散している
    };

    /**
     * @brief 収束性評価クラス
     * 
     * このクラスは、反復計算の収束性を評価し、
     * 収束閾値や最大反復回数を設定するためのインターフェースです。
     */
    class ConvergenceCriteria {
    private:
        static inline double threshold_ = 1.0e-8;
        static inline size_t max_iterations_ = 1000;
        static inline double prev_diff_ = 0.0;

    public:
        /**
         * @brief 収束閾値を設定
         * @param threshold 収束判定に使用する閾値
         */
        static void setThreshold(double threshold) {
            threshold_ = threshold;
        }

        /**
         * @brief 現在の収束閾値を取得
         * @return 設定されている収束閾値
         */
        static double getThreshold() {
            return threshold_;
        }

        /**
         * @brief 最大反復回数を設定
         * @param maxIterations 最大反復回数
         */
        static void setMaxIterations(size_t maxIterations) {
            max_iterations_ = maxIterations;
        }

        /**
         * @brief 現在の最大反復回数を取得
         * @return 設定されている最大反復回数
         */
        static size_t getMaxIterations() {
            return max_iterations_;
        }

        /**
         * @brief 収束判定（実数版）
         * @param current 現在の値
         * @param previous 前回の値
         * @return 収束しているならtrue
         */
        static bool isConverged(double current, double previous) {
            return std::abs(current - previous) < threshold_;
        }

        /**
         * @brief 収束判定（相対誤差版）
         * @param current 現在の値
         * @param previous 前回の値
         * @return 収束しているならtrue
         */
        static bool isConvergedRelative(double current, double previous) {
            if (previous == 0.0) {
                return std::abs(current) < threshold_;
            }
            return std::abs((current - previous) / previous) < threshold_;
        }

        /**
         * @brief 反復計算のステータスを評価
         * @param current 現在の値
         * @param previous 前回の値
         * @param iteration 現在の反復回数
         * @return 収束状態
         */
        static ConvergenceState evaluateStatus(double current, double previous, size_t iteration) {
            if (iteration >= max_iterations_) {
                return ConvergenceState::MaxIterationsReached;
            }
            
            if (isConverged(current, previous)) {
                return ConvergenceState::Converged;
            }
            
            // 発散の検出
            if (std::isnan(current) || std::isinf(current)) {
                return ConvergenceState::Diverging;
            }
            
            // 振動の検出（単純な実装例）
            double current_diff = current - previous;

            if (prev_diff_ * current_diff < 0 &&
                std::abs(current_diff) > threshold_) {
                prev_diff_ = current_diff;
                return ConvergenceState::Oscillating;
            }

            prev_diff_ = current_diff;
            return ConvergenceState::NotConverged;
        }

        /**
         * @brief 収束状態からNumericStateに変換
         * @param state 収束状態
         * @return 対応するNumericState
         */
        static NumericState toNumericState(ConvergenceState state) {
            switch (state) {
                case ConvergenceState::Converged:
                    return NumericState::Normal;
                case ConvergenceState::NotConverged:
                case ConvergenceState::MaxIterationsReached:
                case ConvergenceState::Oscillating:
                case ConvergenceState::Diverging:
                    return NumericState::NotConverged;
                default:
                    std::unreachable();
            }
        }

        /**
         * @brief 収束状態からDivergenceDetailに変換
         * @param state 収束状態
         * @return 対応するDivergenceDetail
         */
        static DivergenceDetail toDivergenceDetail(ConvergenceState state) {
            switch (state) {
                case ConvergenceState::Converged:
                    return DivergenceDetail::None;
                case ConvergenceState::NotConverged:
                    return DivergenceDetail::None;
                case ConvergenceState::MaxIterationsReached:
                    return DivergenceDetail::TruncatedConvergence;
                case ConvergenceState::Oscillating:
                    return DivergenceDetail::Oscillating;
                case ConvergenceState::Diverging:
                    return DivergenceDetail::Diverging;
                default:
                    std::unreachable();
            }
        }
    };

    // Int型専用の収束関連関数
    /**
     * @brief Int型用の収束閾値を設定
     * @param threshold 収束判定に使用する閾値
     */
    void setConvergenceThreshold(const Int& threshold);

    /**
     * @brief Int型用の最大反復回数を設定
     * @param maxIterations 最大反復回数
     */
    void setMaxIterations(size_t maxIterations);

} // namespace calx

#endif // CALX_CONVERGENCE_HPP