use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PELT {
penalty: f64,
min_dist: usize,
}
impl PELT {
pub fn new(penalty: f64, min_dist: usize) -> Self {
Self { penalty, min_dist }
}
fn cost(&self, data: &[f64], start: usize, end: usize) -> f64 {
if end <= start { return 0.0; }
let n = (end - start) as f64;
let mut sum = 0.0;
let mut sum_sq = 0.0;
for i in start..end {
sum += data[i];
sum_sq += data[i] * data[i];
}
let mean = sum / n;
let var = (sum_sq / n) - (mean * mean);
n * var.max(0.0)
}
pub fn detect(&self, data: &[f64]) -> Vec<usize> {
let n = data.len();
if n < self.min_dist * 2 { return vec![]; }
let mut f = vec![0.0; n + 1];
let mut cp = vec![0; n + 1];
let mut r = vec![0];
f[0] = -self.penalty;
for t in 1..=n {
let mut min_val = f64::MAX;
let mut best_tau = 0;
for &tau in &r {
if t - tau < self.min_dist { continue; }
let val = f[tau] + self.cost(data, tau, t) + self.penalty;
if val < min_val {
min_val = val;
best_tau = tau;
}
}
f[t] = min_val;
cp[t] = best_tau;
let mut next_r = vec![0];
for &tau in &r {
if f[tau] + self.cost(data, tau, t) <= f[t] {
next_r.push(tau);
}
}
next_r.push(t);
r = next_r;
}
let mut changepoints = Vec::new();
let mut curr = cp[n];
while curr > 0 {
changepoints.push(curr);
curr = cp[curr];
}
changepoints.sort();
changepoints
}
}