use crate::errors::ArrivalsError;
use atelier_data::temporal::{TimeResolution, from_nanos};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterarrivalResult {
pub deltas_ns: Vec<u64>,
pub deltas_f64: Vec<f64>,
pub resolution: TimeResolution,
pub n_events: usize,
pub total_duration_ns: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArrivalStats {
pub count: usize,
pub mean: f64,
pub variance: f64,
pub std_dev: f64,
pub min: f64,
pub max: f64,
pub skewness: f64,
pub kurtosis: f64,
pub cv: f64,
}
pub fn compute_interarrivals(
timestamps_ns: &[u64],
output_resolution: TimeResolution,
) -> Result<InterarrivalResult, ArrivalsError> {
if timestamps_ns.len() < 2 {
return Err(ArrivalsError::InsufficientData(
"Need at least 2 timestamps to compute interarrivals".into(),
));
}
let n = timestamps_ns.len();
let mut deltas_ns = Vec::with_capacity(n - 1);
for i in 1..n {
deltas_ns.push(timestamps_ns[i].saturating_sub(timestamps_ns[i - 1]));
}
let deltas_f64: Vec<f64> = deltas_ns
.iter()
.map(|&d| from_nanos(d, output_resolution))
.collect();
let total_duration_ns = timestamps_ns[n - 1].saturating_sub(timestamps_ns[0]);
Ok(InterarrivalResult {
deltas_ns,
deltas_f64,
resolution: output_resolution,
n_events: n,
total_duration_ns,
})
}
pub fn descriptive_stats(deltas: &[f64]) -> Option<ArrivalStats> {
let n = deltas.len();
if n == 0 {
return None;
}
let n_f = n as f64;
let sum: f64 = deltas.iter().sum();
let mean = sum / n_f;
let mut min = f64::INFINITY;
let mut max = f64::NEG_INFINITY;
for &d in deltas {
if d < min {
min = d;
}
if d > max {
max = d;
}
}
let mut m2 = 0.0_f64;
let mut m3 = 0.0_f64;
let mut m4 = 0.0_f64;
for &d in deltas {
let diff = d - mean;
let diff2 = diff * diff;
m2 += diff2;
m3 += diff2 * diff;
m4 += diff2 * diff2;
}
let variance = m2 / n_f;
let std_dev = variance.sqrt();
let skewness = if std_dev > 0.0 {
(m3 / n_f) / (std_dev * std_dev * std_dev)
} else {
0.0
};
let kurtosis = if std_dev > 0.0 {
(m4 / n_f) / (variance * variance) - 3.0
} else {
0.0
};
let cv = if mean.abs() > f64::EPSILON {
std_dev / mean
} else {
0.0
};
Some(ArrivalStats {
count: n,
mean,
variance,
std_dev,
min,
max,
skewness,
kurtosis,
cv,
})
}