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

/**
 * @file convergence_criteria.hpp
 * @brief Definitions of convergence criteria for numerical algorithms
 * @author MKL Algebra Library
 */

#ifndef SANGI_CONVERGENCE_CRITERIA_HPP
#define SANGI_CONVERGENCE_CRITERIA_HPP

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

namespace sangi {

    /**
     * @brief Convergence criteria class for numerical algorithms
     *
     * Defines convergence criteria for iterative numerical solvers.
     * Supports judgment by absolute error, relative error, and maximum iteration count.
     *
     * @tparam T value type (typically a floating-point type)
     */
    template<typename T>
        requires concepts::OrderedField<T>
    class ConvergenceCriteria {
    private:
        T abs_tol_;      ///< Absolute tolerance
        T rel_tol_;      ///< Relative tolerance
        std::size_t max_iter_; ///< Maximum number of iterations

    public:
        /**
         * @brief Default constructor
         *
         * Sets default values:
         * - Absolute tolerance: 1e-8 (for double precision)
         * - Relative tolerance: 1e-6 (for double precision)
         * - Maximum iteration count: 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 Constructor with explicit parameters
         *
         * @param abs_tol absolute tolerance
         * @param rel_tol relative tolerance
         * @param max_iter maximum number of iterations
         */
        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 Get the absolute tolerance
         *
         * @return absolute tolerance
         */
        T absolute_tolerance() const {
            return abs_tol_;
        }

        /**
         * @brief Set the absolute tolerance
         *
         * @param tol new absolute tolerance
         */
        void set_absolute_tolerance(T tol) {
            abs_tol_ = tol;
            validate_parameters();
        }

        /**
         * @brief Get the relative tolerance
         *
         * @return relative tolerance
         */
        T relative_tolerance() const {
            return rel_tol_;
        }

        /**
         * @brief Set the relative tolerance
         *
         * @param tol new relative tolerance
         */
        void set_relative_tolerance(T tol) {
            rel_tol_ = tol;
            validate_parameters();
        }

        /**
         * @brief Get the maximum number of iterations
         *
         * @return maximum number of iterations
         */
        std::size_t max_iterations() const {
            return max_iter_;
        }

        /**
         * @brief Set the maximum number of iterations
         *
         * @param iter new maximum number of iterations
         */
        void set_max_iterations(std::size_t iter) {
            max_iter_ = iter;
        }

        /**
         * @brief Check whether the given norm value satisfies the convergence criterion
         *
         * Returns true if either the absolute or the relative criterion is satisfied.
         *
         * @param norm current norm value
         * @param initial_norm initial norm value (for relative error calculation)
         * @return true if the convergence criterion is satisfied
         */
        bool converged(T norm, T initial_norm) const {
            return norm <= abs_tol_ || norm <= rel_tol_ * initial_norm;
        }

        /**
         * @brief Check whether the given error value satisfies the absolute-error criterion
         *
         * @param error current error value
         * @return true if the absolute-error criterion is satisfied
         */
        bool absolute_converged(T error) const {
            return error <= abs_tol_;
        }

        /**
         * @brief Check whether the given error value satisfies the relative-error criterion
         *
         * @param error current error value
         * @param reference reference value (typically the norm of the initial or target value)
         * @return true if the relative-error criterion is satisfied
         */
        bool relative_converged(T error, T reference) const {
            return error <= rel_tol_ * reference;
        }

        /**
         * @brief Check whether the given iteration count exceeds the maximum
         *
         * @param iter current iteration count
         * @return true if the iteration count exceeds the maximum
         */
        bool max_iterations_exceeded(std::size_t iter) const {
            return iter >= max_iter_;
        }

        /**
         * @brief Set strict convergence judgment mode
         *
         * Strict mode requires both the absolute and the relative criteria to be satisfied.
         *
         * @param strict true to enable strict mode
         */
        void set_strict_mode(bool strict = true) {
            // This simple implementation does not provide strict mode,
            // but the function is defined for future extensibility.
        }

        /**
         * @brief Get the details of the convergence criteria as a string
         *
         * @return string describing the convergence criteria
         */
        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 Convert convergence status to a numeric state
         *
         * @param converged whether convergence was achieved
         * @param max_iter_exceeded whether the maximum iteration count was exceeded
         * @param error variable into which error information is stored
         * @return the corresponding numeric state
         */
        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 Validate parameters
         *
         * Throws an exception if invalid parameters are set.
         */
        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");
            }
            // If a value smaller than machine epsilon is set, we could issue a
            // warning or clamp it to the minimum value.
            const T eps = std::numeric_limits<T>::epsilon();
            if (abs_tol_ < eps) {
                abs_tol_ = eps;
            }
            if (rel_tol_ < eps) {
                rel_tol_ = eps;
            }
        }
    };

    // Alias templates for common cases
    /**
     * @brief Convergence criteria for single-precision floating point
     */
    using FloatConvergenceCriteria = ConvergenceCriteria<float>;

    /**
     * @brief Convergence criteria for double-precision floating point
     */
    using DoubleConvergenceCriteria = ConvergenceCriteria<double>;

    // MKL-specific specializations (implement as needed)
#ifdef SANGI_USE_MKL
// Place MKL-specific specializations and extensions here
#endif

} // namespace sangi

#endif // SANGI_CONVERGENCE_CRITERIA_HPP