use std::ops::Deref;
use std::time::Duration;
use std::time::Instant;
#[derive(Copy, Clone)]
struct MinmaxSample<T> {
time: Instant,
value: T,
}
pub struct Minmax<T> {
estimate: [MinmaxSample<T>; 3],
}
impl<T> Deref for Minmax<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.estimate[0].value
}
}
impl<T: PartialOrd + Copy> Minmax<T> {
pub fn new(val: T) -> Self {
Minmax {
estimate: [MinmaxSample {
time: Instant::now(),
value: val,
}; 3],
}
}
pub fn reset(&mut self, time: Instant, meas: T) -> T {
let val = MinmaxSample { time, value: meas };
for i in self.estimate.iter_mut() {
*i = val;
}
self.estimate[0].value
}
pub fn running_min(&mut self, win: Duration, time: Instant, meas: T) -> T {
let val = MinmaxSample { time, value: meas };
let delta_time = time.duration_since(self.estimate[2].time);
if val.value <= self.estimate[0].value || delta_time > win {
return self.reset(time, meas);
}
if val.value <= self.estimate[1].value {
self.estimate[2] = val;
self.estimate[1] = val;
} else if val.value <= self.estimate[2].value {
self.estimate[2] = val;
}
self.subwin_update(win, time, meas)
}
pub fn running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
let val = MinmaxSample { time, value: meas };
let delta_time = time.duration_since(self.estimate[2].time);
if val.value >= self.estimate[0].value || delta_time > win {
return self.reset(time, meas);
}
if val.value >= self.estimate[1].value {
self.estimate[2] = val;
self.estimate[1] = val;
} else if val.value >= self.estimate[2].value {
self.estimate[2] = val
}
self.subwin_update(win, time, meas)
}
fn subwin_update(&mut self, win: Duration, time: Instant, meas: T) -> T {
let val = MinmaxSample { time, value: meas };
let delta_time = time.duration_since(self.estimate[0].time);
if delta_time > win {
self.estimate[0] = self.estimate[1];
self.estimate[1] = self.estimate[2];
self.estimate[2] = val;
if time.duration_since(self.estimate[0].time) > win {
self.estimate[0] = self.estimate[1];
self.estimate[1] = self.estimate[2];
self.estimate[2] = val;
}
} else if self.estimate[1].time == self.estimate[0].time &&
delta_time > win.div_f32(4.0)
{
self.estimate[2] = val;
self.estimate[1] = val;
} else if self.estimate[2].time == self.estimate[1].time &&
delta_time > win.div_f32(2.0)
{
self.estimate[2] = val;
}
self.estimate[0].value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reset_filter_rtt() {
let mut f = Minmax::new(Duration::ZERO);
let now = Instant::now();
let rtt = Duration::from_millis(50);
let rtt_min = f.reset(now, rtt);
assert_eq!(rtt_min, rtt);
assert_eq!(f.estimate[0].time, now);
assert_eq!(f.estimate[0].value, rtt);
assert_eq!(f.estimate[1].time, now);
assert_eq!(f.estimate[1].value, rtt);
assert_eq!(f.estimate[2].time, now);
assert_eq!(f.estimate[2].value, rtt);
}
#[test]
fn reset_filter_bandwidth() {
let mut f = Minmax::new(0);
let now = Instant::now();
let bw = 2000;
let bw_min = f.reset(now, bw);
assert_eq!(bw_min, bw);
assert_eq!(f.estimate[0].time, now);
assert_eq!(f.estimate[0].value, bw);
assert_eq!(f.estimate[1].time, now);
assert_eq!(f.estimate[1].value, bw);
assert_eq!(f.estimate[2].time, now);
assert_eq!(f.estimate[2].value, bw);
}
#[test]
fn get_windowed_min_rtt() {
let mut f = Minmax::new(Duration::ZERO);
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let win = Duration::from_millis(500);
let mut time = Instant::now();
let mut rtt_min = f.reset(time, rtt_25);
assert_eq!(rtt_min, rtt_25);
time += Duration::from_millis(250);
rtt_min = f.running_min(win, time, rtt_24);
assert_eq!(rtt_min, rtt_24);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);
time += Duration::from_millis(600);
rtt_min = f.running_min(win, time, rtt_25);
assert_eq!(rtt_min, rtt_25);
assert_eq!(f.estimate[1].value, rtt_25);
assert_eq!(f.estimate[2].value, rtt_25);
}
#[test]
fn get_windowed_min_bandwidth() {
let mut f = Minmax::new(0);
let bw_200 = 200;
let bw_500 = 500;
let win = Duration::from_millis(500);
let mut time = Instant::now();
let mut bw_min = f.reset(time, bw_500);
assert_eq!(bw_min, bw_500);
time += Duration::from_millis(250);
bw_min = f.running_min(win, time, bw_200);
assert_eq!(bw_min, bw_200);
assert_eq!(f.estimate[1].value, bw_200);
assert_eq!(f.estimate[2].value, bw_200);
time += Duration::from_millis(600);
bw_min = f.running_min(win, time, bw_500);
assert_eq!(bw_min, bw_500);
assert_eq!(f.estimate[1].value, bw_500);
assert_eq!(f.estimate[2].value, bw_500);
}
#[test]
fn get_windowed_max_rtt() {
let mut f = Minmax::new(Duration::ZERO);
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let win = Duration::from_millis(500);
let mut time = Instant::now();
let mut rtt_max = f.reset(time, rtt_24);
assert_eq!(rtt_max, rtt_24);
time += Duration::from_millis(250);
rtt_max = f.running_max(win, time, rtt_25);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_25);
assert_eq!(f.estimate[2].value, rtt_25);
time += Duration::from_millis(600);
rtt_max = f.running_max(win, time, rtt_24);
assert_eq!(rtt_max, rtt_24);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);
}
#[test]
fn get_windowed_max_bandwidth() {
let mut f = Minmax::new(0);
let bw_200 = 200;
let bw_500 = 500;
let win = Duration::from_millis(500);
let mut time = Instant::now();
let mut bw_max = f.reset(time, bw_200);
assert_eq!(bw_max, bw_200);
time += Duration::from_millis(5000);
bw_max = f.running_max(win, time, bw_500);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_500);
assert_eq!(f.estimate[2].value, bw_500);
time += Duration::from_millis(600);
bw_max = f.running_max(win, time, bw_200);
assert_eq!(bw_max, bw_200);
assert_eq!(f.estimate[1].value, bw_200);
assert_eq!(f.estimate[2].value, bw_200);
}
#[test]
fn get_windowed_min_estimates_rtt() {
let mut f = Minmax::new(Duration::ZERO);
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let rtt_23 = Duration::from_millis(23);
let rtt_22 = Duration::from_millis(22);
let win = Duration::from_secs(1);
let mut time = Instant::now();
let mut rtt_min = f.reset(time, rtt_23);
assert_eq!(rtt_min, rtt_23);
time += Duration::from_millis(300);
rtt_min = f.running_min(win, time, rtt_24);
assert_eq!(rtt_min, rtt_23);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);
time += Duration::from_millis(300);
rtt_min = f.running_min(win, time, rtt_25);
assert_eq!(rtt_min, rtt_23);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_25);
time += Duration::from_millis(300);
rtt_min = f.running_min(win, time, rtt_22);
assert_eq!(rtt_min, rtt_22);
assert_eq!(f.estimate[1].value, rtt_22);
assert_eq!(f.estimate[2].value, rtt_22);
}
#[test]
fn get_windowed_min_estimates_bandwidth() {
let mut f = Minmax::new(0);
let bw_500 = 500;
let bw_400 = 400;
let bw_300 = 300;
let bw_200 = 200;
let win = Duration::from_secs(1);
let mut time = Instant::now();
let mut bw_min = f.reset(time, bw_300);
assert_eq!(bw_min, bw_300);
time += Duration::from_millis(300);
bw_min = f.running_min(win, time, bw_400);
assert_eq!(bw_min, bw_300);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_400);
time += Duration::from_millis(300);
bw_min = f.running_min(win, time, bw_500);
assert_eq!(bw_min, bw_300);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_500);
time += Duration::from_millis(300);
bw_min = f.running_min(win, time, bw_200);
assert_eq!(bw_min, bw_200);
assert_eq!(f.estimate[1].value, bw_200);
assert_eq!(f.estimate[2].value, bw_200);
}
#[test]
fn get_windowed_max_estimates_rtt() {
let mut f = Minmax::new(Duration::ZERO);
let rtt_25 = Duration::from_millis(25);
let rtt_24 = Duration::from_millis(24);
let rtt_23 = Duration::from_millis(23);
let rtt_26 = Duration::from_millis(26);
let win = Duration::from_secs(1);
let mut time = Instant::now();
let mut rtt_max = f.reset(time, rtt_25);
assert_eq!(rtt_max, rtt_25);
time += Duration::from_millis(300);
rtt_max = f.running_max(win, time, rtt_24);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_24);
time += Duration::from_millis(300);
rtt_max = f.running_max(win, time, rtt_23);
assert_eq!(rtt_max, rtt_25);
assert_eq!(f.estimate[1].value, rtt_24);
assert_eq!(f.estimate[2].value, rtt_23);
time += Duration::from_millis(300);
rtt_max = f.running_max(win, time, rtt_26);
assert_eq!(rtt_max, rtt_26);
assert_eq!(f.estimate[1].value, rtt_26);
assert_eq!(f.estimate[2].value, rtt_26);
}
#[test]
fn get_windowed_max_estimates_bandwidth() {
let mut f = Minmax::new(0);
let bw_500 = 500;
let bw_400 = 400;
let bw_300 = 300;
let bw_600 = 600;
let win = Duration::from_secs(1);
let mut time = Instant::now();
let mut bw_max = f.reset(time, bw_500);
assert_eq!(bw_max, bw_500);
time += Duration::from_millis(300);
bw_max = f.running_max(win, time, bw_400);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_400);
time += Duration::from_millis(300);
bw_max = f.running_max(win, time, bw_300);
assert_eq!(bw_max, bw_500);
assert_eq!(f.estimate[1].value, bw_400);
assert_eq!(f.estimate[2].value, bw_300);
time += Duration::from_millis(300);
bw_max = f.running_max(win, time, bw_600);
assert_eq!(bw_max, bw_600);
assert_eq!(f.estimate[1].value, bw_600);
assert_eq!(f.estimate[2].value, bw_600);
}
}