use std::fmt::Debug;
#[derive(Copy, Clone, Debug)]
pub(super) struct MinMax {
window: u64,
samples: [MinMaxSample; 3],
}
impl MinMax {
pub(super) fn get(&self) -> u64 {
self.samples[0].value
}
fn fill(&mut self, sample: MinMaxSample) {
self.samples.fill(sample);
}
pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
let sample = MinMaxSample {
time: current_round,
value: measurement,
};
if self.samples[0].value == 0
|| sample.value >= self.samples[0].value
|| sample.time - self.samples[2].time > self.window
{
self.fill(sample);
return;
}
if sample.value >= self.samples[1].value {
self.samples[2] = sample;
self.samples[1] = sample;
} else if sample.value >= self.samples[2].value {
self.samples[2] = sample;
}
self.subwin_update(sample);
}
fn subwin_update(&mut self, sample: MinMaxSample) {
let dt = sample.time - self.samples[0].time;
if dt > self.window {
self.samples[0] = self.samples[1];
self.samples[1] = self.samples[2];
self.samples[2] = sample;
if sample.time - self.samples[0].time > self.window {
self.samples[0] = self.samples[1];
self.samples[1] = self.samples[2];
self.samples[2] = sample;
}
} else if self.samples[1].time == self.samples[0].time && dt > self.window / 4 {
self.samples[2] = sample;
self.samples[1] = sample;
} else if self.samples[2].time == self.samples[1].time && dt > self.window / 2 {
self.samples[2] = sample;
}
}
}
impl Default for MinMax {
fn default() -> Self {
Self {
window: 10,
samples: [Default::default(); 3],
}
}
}
#[derive(Debug, Copy, Clone, Default)]
struct MinMaxSample {
time: u64,
value: u64,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn default_starts_uninitialized() {
let min_max = MinMax::default();
assert_eq!(min_max.window, 10);
assert_eq!(min_max.get(), 0);
assert!(min_max.samples.iter().all(|sample| sample.time == 0));
assert!(min_max.samples.iter().all(|sample| sample.value == 0));
}
#[test]
fn first_sample_initializes_all_slots() {
let mut min_max = MinMax::default();
min_max.update_max(1, 100);
assert_eq!(min_max.get(), 100);
assert!(min_max.samples.iter().all(|sample| sample.time == 1));
assert!(min_max.samples.iter().all(|sample| sample.value == 100));
}
#[test]
fn higher_sample_replaces_all_slots() {
let mut min_max = MinMax::default();
min_max.update_max(1, 100);
min_max.update_max(2, 120);
assert_eq!(min_max.get(), 120);
assert!(min_max.samples.iter().all(|sample| sample.time == 2));
assert!(min_max.samples.iter().all(|sample| sample.value == 120));
}
#[test]
fn lower_samples_do_not_replace_current_max_inside_window() {
let mut min_max = MinMax::default();
min_max.update_max(1, 100);
min_max.update_max(2, 80);
min_max.update_max(3, 90);
assert_eq!(min_max.get(), 100);
assert!(min_max.samples.iter().all(|sample| sample.time == 1));
assert!(min_max.samples.iter().all(|sample| sample.value == 100));
}
#[test]
fn quarter_and_half_window_samples_seed_backups() {
let mut min_max = MinMax::default();
min_max.update_max(0, 100);
min_max.update_max(3, 80);
min_max.update_max(6, 70);
assert_eq!(min_max.get(), 100);
assert_eq!(min_max.samples[1].value, 80);
assert_eq!(min_max.samples[1].time, 3);
assert_eq!(min_max.samples[2].value, 70);
assert_eq!(min_max.samples[2].time, 6);
}
#[test]
fn stale_best_promotes_backup_samples() {
let mut min_max = MinMax::default();
min_max.update_max(0, 100);
min_max.update_max(3, 80);
min_max.update_max(6, 70);
min_max.update_max(11, 60);
assert_eq!(min_max.get(), 80);
assert_eq!(min_max.samples[0].time, 3);
assert_eq!(min_max.samples[1].time, 6);
assert_eq!(min_max.samples[2].time, 11);
}
#[test]
fn expired_third_slot_resets_window() {
let mut min_max = MinMax::default();
min_max.update_max(1, 100);
min_max.update_max(4, 80);
min_max.update_max(7, 70);
min_max.update_max(18, 60);
assert_eq!(min_max.get(), 60);
assert!(min_max.samples.iter().all(|sample| sample.time == 18));
assert!(min_max.samples.iter().all(|sample| sample.value == 60));
}
#[test]
fn equal_sample_counts_as_new_max() {
let mut min_max = MinMax::default();
min_max.update_max(1, 100);
min_max.update_max(5, 80);
min_max.update_max(6, 100);
assert_eq!(min_max.get(), 100);
assert!(min_max.samples.iter().all(|sample| sample.time == 6));
assert!(min_max.samples.iter().all(|sample| sample.value == 100));
}
#[test]
fn original_bbr_sequence_tracks_expected_max() {
let round = 25;
let mut min_max = MinMax::default();
min_max.update_max(round + 1, 100);
assert_eq!(100, min_max.get());
min_max.update_max(round + 3, 120);
assert_eq!(120, min_max.get());
min_max.update_max(round + 5, 160);
assert_eq!(160, min_max.get());
min_max.update_max(round + 7, 100);
assert_eq!(160, min_max.get());
min_max.update_max(round + 10, 100);
assert_eq!(160, min_max.get());
min_max.update_max(round + 14, 100);
assert_eq!(160, min_max.get());
min_max.update_max(round + 16, 100);
assert_eq!(100, min_max.get());
min_max.update_max(round + 18, 130);
assert_eq!(130, min_max.get());
}
}