//! # dupliter
//!
//! Retro BBS-style terminal simulator for the Dupliter gravitational-temporal
//! anchor hypothesis by Ashlan Chidester.
//!
//! ## Library usage
//!
//! ```rust
//! use dupliter::{relativistic_to_classical_ratio, dupliter_stability_index, verdict};
//!
//! let ratio = relativistic_to_classical_ratio(0.5);
//! let dsi   = dupliter_stability_index(ratio, 2.0, 3.0, 0.0);
//! let (level, message) = verdict(dsi);
//!
//! println!("Ratio : {:.4}", ratio);
//! println!("DSI   : {:.2}  [{}] — {}", dsi, level, message);
//! ```

/// Compute the ratio of relativistic KE to classical KE.
///
/// Formula: `2 * (1/√(1−β²) − 1) / β²`  where β = v/c
///
/// Returns `0.0` for β ≤ 0, `f64::INFINITY` for β ≥ 1.
pub fn relativistic_to_classical_ratio(beta: f64) -> f64 {
    if beta <= 0.0 {
        return 0.0;
    }
    if beta >= 1.0 {
        return f64::INFINITY;
    }
    let gamma = 1.0 / (1.0 - beta * beta).sqrt();
    2.0 * (gamma - 1.0) / (beta * beta)
}

/// Heuristic Dupliter Stability Index (DSI).
///
/// Combines the relativistic/classical KE ratio with toy environmental
/// parameters into a 0–100-ish comparative score.
///
/// `noise` adds a small random perturbation (pass `0.0` for deterministic output).
pub fn dupliter_stability_index(
    ratio: f64,
    dm_factor: f64,
    anchor_strength: f64,
    noise: f64,
) -> f64 {
    use std::f64;
    let r = ratio.max(1e-12);
    let base = (r + 1.0).log10() * 0.6
        + (dm_factor + 1.0).log10() * 0.25
        + (anchor_strength + 1.0).log10() * 0.15;
    let normalised = (base / (1e6_f64 + 1.0).log10()) * 100.0;
    let jitter = if noise > 0.0 {
        // simple deterministic-ish perturbation without pulling in rand
        let seed = (ratio * 1_000_000.0) as u64 ^ (dm_factor * 999.0) as u64;
        let pseudo = ((seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407)) >> 33) as f64
            / u32::MAX as f64;
        1.0 + (pseudo * 2.0 - 1.0) * noise
    } else {
        1.0
    };
    (normalised * jitter).max(0.0)
}

/// Return a `(level, description)` verdict for a DSI score.
pub fn verdict(dsi: f64) -> (&'static str, &'static str) {
    if dsi >= 60.0 {
        ("High",     "Plausible Dupliter regime — strong anchor.")
    } else if dsi >= 30.0 {
        ("Moderate", "Marginal potential — further refinement needed.")
    } else if dsi > 0.0 {
        ("Low",      "Unstable — insufficient parameters.")
    } else {
        ("None",     "No Dupliter potential detected.")
    }
}

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

    #[test]
    fn ratio_zero_beta() { assert_eq!(relativistic_to_classical_ratio(0.0), 0.0); }

    #[test]
    fn ratio_unity_beta() { assert!(relativistic_to_classical_ratio(1.0).is_infinite()); }

    #[test]
    fn ratio_low_speed_near_one() {
        let r = relativistic_to_classical_ratio(0.001);
        assert!((r - 1.0).abs() < 0.001);
    }

    #[test]
    fn ratio_half_c_above_one() {
        assert!(relativistic_to_classical_ratio(0.5) > 1.0);
    }

    #[test]
    fn dsi_non_negative() {
        let r = relativistic_to_classical_ratio(0.9);
        assert!(dupliter_stability_index(r, 1.0, 1.0, 0.0) >= 0.0);
    }

    #[test]
    fn verdict_levels() {
        assert_eq!(verdict(75.0).0, "High");
        assert_eq!(verdict(45.0).0, "Moderate");
        assert_eq!(verdict(10.0).0, "Low");
        assert_eq!(verdict(0.0).0,  "None");
    }
}
