//! Convergence configuration and reporting.

use std::time::Duration;

use smallvec::SmallVec;

#[derive(Clone, Copy, Debug)]
pub struct ConvergenceOptions {
    pub max_iter: usize,
    pub epsilon: f64,
    /// EP damping factor in natural-parameter space: each per-factor
    /// update inside a single game writes `α·new + (1−α)·old`. `1.0` is
    /// undamped (default); `< 1.0` stabilises oscillating fixed-point
    /// loops at the cost of more iterations. Must be in `(0.0, 1.0]`.
    ///
    /// Applies only to the within-game EP loop (`run_chain`). The outer
    /// `History::converge` cross-history sweep is undamped regardless of
    /// this value — cross-slice damping is a different concept and not
    /// in scope.
    pub alpha: f64,
}

impl Default for ConvergenceOptions {
    fn default() -> Self {
        Self {
            max_iter: crate::ITERATIONS,
            epsilon: crate::EPSILON,
            alpha: 1.0,
        }
    }
}

/// Post-hoc summary of a `History::converge` call.
#[derive(Clone, Debug)]
pub struct ConvergenceReport {
    pub iterations: usize,
    pub final_step: (f64, f64),
    pub log_evidence: f64,
    pub converged: bool,
    pub per_iteration_time: SmallVec<[Duration; 32]>,
    pub slices_skipped: usize,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_alpha_is_one_for_undamped_behavior() {
        let opts = ConvergenceOptions::default();
        assert_eq!(opts.alpha, 1.0);
    }
}
