use serde::{Deserialize, Serialize};

/// A struct used in profiling.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Tracking {
    sizes: Vec<usize>,
}

impl Tracking {
    pub fn new(sizes: Vec<usize>) -> Self {
        Tracking { sizes }
    }
    pub fn combine(&mut self, old_id_to_new_id: Vec<usize>, new_size: usize) {
        let mut max_other = 0usize;
        let mut other_idx = 0usize;
        for size in self.sizes.iter_mut() {
            loop {
                if other_idx >= *size || other_idx >= old_id_to_new_id.len() {
                    break;
                }
                let new_id = old_id_to_new_id[other_idx];
                if new_id < new_size {
                    max_other = max_other.max(old_id_to_new_id[other_idx] + 1);
                }
                other_idx += 1;
            }
            *size = max_other;
        }
    }
    pub fn get_sizes(&self) -> &[usize] {
        &self.sizes
    }
}

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

    fn new_range(size: usize) -> Tracking {
        Tracking::new((1..=size).collect())
    }
    fn new_range_with_zero(size: usize) -> Tracking {
        Tracking::new((0..=size).collect())
    }
    #[test]
    fn idempotent() {
        let size = 5;
        let mut tracking = new_range(size);
        let old_id_to_new_id = (0..size).collect();
        tracking.combine(old_id_to_new_id, size);
        assert_eq!(tracking, new_range(size));
        let mut tracking = new_range_with_zero(size);
        let old_id_to_new_id = (0..size).collect();
        tracking.combine(old_id_to_new_id, size);
        assert_eq!(tracking, new_range_with_zero(size));
    }
}
