use quadrature::clenshaw_curtis::integrate;

fn planck(wavelength: f64, temperature: f64) -> f64 {
    let exp_m1 = f64::exp_m1;
    let c: f64 = 299_792_458.0;
    let h = 6.626_068_96e-34;
    let k_b = 1.380e-23;
    2.0 * h * c.powi(2) * 1.0
        / (wavelength.powi(5) * (exp_m1((h * c) / (wavelength * k_b * temperature))))
}

pub fn planck_integral(temperature: f64, wave_min: f64, wave_max: f64) -> f64 {
    let func = |wavelength| planck(wavelength, temperature);
    if wave_min < wave_max {
        integrate(func, wave_min, wave_max, 1e-7).integral
    } else {
        integrate(func, wave_max, wave_min, 1e-7).integral
    }
}

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

    fn is_close(actual: f64, expected: f64) -> bool {
        let precision = 5;
        let pow = 10.0f64.powi(precision + 1);
        let delta = (expected - actual).abs();
        let max_delta = 10.0f64.powi(-precision) / 2.0;
        return (delta * pow).round() / pow <= max_delta;
    }

    #[test]
    fn test_accuracy() {
        assert!(is_close(
            planck_integral(5778.0, 4000e-10, 7000e-10),
            7359875.725388271,
        ));
    }

    #[test]
    fn test_order_proof() {
        assert_eq!(
            planck_integral(5778.0, 4000e-10, 7000e-10),
            planck_integral(5778.0, 7000e-10, 4000e-10)
        );
    }
}
