#[derive(Default)]
pub struct P2 {
    quantile: f64,
    heights: Vec<f64>,
    pos: [i64; 5],
    n_pos: [f64; 5],
    dn: [f64; 5],
    filled: bool,
}
impl P2 {
    pub fn new(quantile: f64) -> Self {
        if quantile < 0.0 || quantile > 1.0 {
            panic!("quantile must be in [0, 1]");
        }
        let mut p2 = Self {
            quantile,
            n_pos: [
                0.0,
                2.0 * quantile,
                4.0 * quantile,
                2.0 + 2.0 * quantile,
                4.0,
            ],
            dn: [0.0, quantile / 2.0, quantile, (1.0 + quantile) / 2.0, 1.0],
            ..Default::default()
        };
        for i in 0..p2.pos.len() {
            p2.pos[i] = i as i64;
        }
        p2
    }
    pub fn append(&mut self, data: f64) {
        if self.heights.len() != 5 {
            self.heights.push(data);
            return;
        }
        if !self.filled {
            self.filled = true;
            self.heights.sort_by(|a, b| a.partial_cmp(b).unwrap());
        }
        self.append_data(data);
    }
    fn append_data(&mut self, data: f64) {
        let l = self.heights.len() - 1;
        let mut k: isize = -1;
        if data < self.heights[0] {
            k = 0;
            self.heights[0] = data;
        } else if self.heights[l] <= data {
            k = l as isize - 1;
            self.heights[l] = data;
        } else {
            for i in 1..=l {
                if self.heights[i - 1] <= data && data < self.heights[i] {
                    k = i as isize - 1;
                    break;
                }
            }
        }
        for i in 0..self.pos.len() {
            if i > k as usize {
                self.pos[i] += 1;
            }
            self.n_pos[i] += self.dn[i];
        }
        self.adjust_heights();
    }
    fn adjust_heights(&mut self) {
        for i in 1..self.heights.len() - 1 {
            let n = self.pos[i];
            let np1 = self.pos[i + 1];
            let nm1 = self.pos[i - 1];
            let d = self.n_pos[i] - n as f64;
            if (d >= 1.0 && np1 - n > 1) || (d <= -1.0 && nm1 - n < -1) {
                let d = if d >= 0.0 { 1.0 } else { -1.0 };
                let h = self.heights[i];
                let hp1 = self.heights[i + 1];
                let hm1 = self.heights[i - 1];
                let hi = parabolic(d, hp1, h, hm1, np1 as f64, n as f64, nm1 as f64);
                if hm1 < hi && hi < hp1 {
                    self.heights[i] = hi;
                } else {
                    let hd = self.heights[i + d as usize];
                    let nd: i64 = self.pos[i + d as usize];
                    self.heights[i] = h + d * (hd - h) / (nd - n) as f64;
                }
                self.pos[i] += d as i64;
            }
        }
    }
    pub fn value(&mut self) -> f64 {
        if !self.filled {
            let l = self.heights.len();
            match l {
                0 => return 0.0,
                1 => return self.heights[0],
                _ => self.heights.sort_by(|a, b| a.partial_cmp(b).unwrap()),
            }
            let rank = (self.quantile * l as f64) as usize;
            return self.heights[rank];
        }
        self.heights[2]
    }
}
fn parabolic(d: f64, qp1: f64, q: f64, qm1: f64, np1: f64, n: f64, nm1: f64) -> f64 {
    let a = d / (np1 - nm1);
    let b1 = (n - nm1 + d) * (qp1 - q) / (np1 - n);
    let b2 = (np1 - n - d) * (q - qm1) / (n - nm1);
    q + a * (b1 + b2)
}
#[cfg(test)]
mod test {
    use super::P2;
    #[test]
    fn test_p2() {
        let mut p2 = P2::new(0.3);
        for i in 1..=100 {
            p2.append(i as f64);
        }
        let x = p2.value();
        assert_eq!(x, 30.0);
    }
}