use crate::error::{NumRs2Error, Result};
use scirs2_core::ndarray::ArrayView1;
use super::core::ExponentialSmoothing;
use super::types::{ExponentialSmoothingResult, SeasonalComponent, TrendComponent};
pub(super) fn validate_param(value: f64, name: &str) -> Result<()> {
if value <= 0.0 || value >= 1.0 {
return Err(NumRs2Error::ValueError(format!(
"{} must be in (0, 1), got {}",
name, value
)));
}
Ok(())
}
pub(super) fn damped_trend_sum(phi: f64, h: usize) -> f64 {
if (phi - 1.0).abs() < 1e-12 {
return h as f64;
}
if phi.abs() < 1e-12 {
return 0.0;
}
phi * (1.0 - phi.powi(h as i32)) / (1.0 - phi)
}
pub(super) fn build_and_fit(
alpha: f64,
beta: Option<f64>,
gamma: Option<f64>,
phi: Option<f64>,
trend: TrendComponent,
seasonal: SeasonalComponent,
period: Option<usize>,
data: &ArrayView1<f64>,
) -> Result<ExponentialSmoothingResult> {
let model = ExponentialSmoothing::custom(alpha, beta, gamma, phi, trend, seasonal, period)?;
model.fit(data)
}
pub(super) fn quantile_normal(p: f64) -> f64 {
if p <= 0.0 {
return f64::NEG_INFINITY;
}
if p >= 1.0 {
return f64::INFINITY;
}
if p > 0.5 {
return -quantile_normal(1.0 - p);
}
let t = (-2.0 * p.ln()).sqrt();
let c0 = 2.515517;
let c1 = 0.802853;
let c2 = 0.010328;
let d1 = 1.432788;
let d2 = 0.189269;
let d3 = 0.001308;
let numerator = c0 + c1 * t + c2 * t * t;
let denominator = 1.0 + d1 * t + d2 * t * t + d3 * t * t * t;
-(t - numerator / denominator)
}