downsample 0.0.4

keep downsampled history of data over long period of time
Documentation
use crate::{
    aggregatable::{Aggregatable, Algorithm},
    fixed_frequency::storage::{Level, LevelMeta, Storage},
};
use alloc::vec::Vec;
use core::{marker::PhantomData, time::Duration};

#[derive(Debug)]
#[allow(dead_code)]
enum TransInfo {
    ///fixed number of
    LevelCount {
        level_count: usize,
        relative_downsample: usize,
        algo: Algorithm,
    },
    ///Duration says like 1s, 1m, 1h and the relative downsample between one
    /// level and the next, e.g. 100 10 20
    TimeRelative(Duration, usize, Algorithm),
    /// Duration says like 1s, 1m, 1h and the absolute downsample between raw
    /// and the respective level, e.g. 100, 1_000, 20_000
    TimeAbsolute(Duration, usize, Algorithm),
}

#[derive(Default)]
pub struct Builder<T> {
    #[allow(dead_code)]
    frequency: usize,
    raw_count: usize,
    level:     Vec<TransInfo>,
    _data:     PhantomData<T>,
}

impl<T> Builder<T> {
    pub fn new(frequency: usize, raw_count: usize) -> Builder<T> {
        Builder {
            frequency,
            raw_count,
            _data: Default::default(),
            level: Vec::new(),
        }
    }

    pub fn build(self) -> Storage<T>
    where
        T: Default,
    {
        let mut level = Vec::new();
        let mut start = 0;
        let mut end;
        let mut last_count = self.raw_count;
        for l in self.level {
            let (count, downsample, algorithm) = match l {
                TransInfo::LevelCount {
                    level_count,
                    relative_downsample,
                    algo,
                } => {
                    let result = (last_count + relative_downsample, relative_downsample, algo);
                    last_count = level_count;
                    result
                },
                _ => unimplemented!(""),
            };
            end = start + count;

            let meta = LevelMeta::new(start..end, downsample, algorithm);
            level.push(Level::build(meta));
            start = end;
        }
        // build last level with downsample of 0
        end = start + last_count;
        let meta = LevelMeta::new(start..end, 0, Algorithm::Average);
        level.push(Level::build(meta));
        let mut data = Vec::with_capacity(end);
        for _ in 0..end {
            data.push(T::default())
        }
        Storage { data, level }
    }

    pub fn level<const A: Algorithm>(mut self, level_count: usize, relative_downsample: usize) -> Self
    where
        T: Aggregatable<{ A }>,
    {
        debug_assert!(relative_downsample != 0);
        self.level.push(TransInfo::LevelCount {
            level_count,
            relative_downsample,
            algo: A,
        });
        self
    }
}

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

    #[test]
    fn construct_simple() {
        // 0s - 1s : 100Hz
        // 1s - 1m : 1Hz
        // 1m - 1h : 1/60 Hz
        // 1h - 1d : 1/3600 Hz
        let s: Storage<f32> = Builder::new(100, 100)
            .level::<{ Algorithm::Average }>(59, 100)
            .level::<{ Algorithm::Average }>(59, 60)
            .level::<{ Algorithm::Average }>(23, 60)
            .build();
        assert_eq!(s.level.len(), 4);
        assert_eq!(s.level[0].0, LevelMeta::new(0..200, 100, Algorithm::Average));
        assert_eq!(s.level[1].0, LevelMeta::new(200..319, 60, Algorithm::Average));
        assert_eq!(s.level[2].0, LevelMeta::new(319..438, 60, Algorithm::Average));
        assert_eq!(s.level[3].0, LevelMeta::new(438..461, 0, Algorithm::Average));
    }

    #[test]
    fn construct_use_simple() {
        // 0s - 1s : 100Hz
        // 1s - 1m : 1Hz
        // 1m - 1h : 1/60 Hz
        // 1h - 1d : 1/3600 Hz
        let mut s: Storage<f32> = Builder::new(100, 100)
            .level::<{ Algorithm::Average }>(59, 100)
            .level::<{ Algorithm::Average }>(59, 60)
            .level::<{ Algorithm::Average }>(23, 60)
            .build();
        for _ in 0..1000 {
            s.push(0.0);
        }
    }
}