macro_rules! impl_running_min_float {
($name:ident, $ty:ty, $init:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
min: $ty,
count: u64,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
min: $init,
count: 0,
}
}
#[inline]
pub fn update(&mut self, sample: $ty) -> Result<$ty, crate::DataError> {
check_finite!(sample);
self.count += 1;
if sample < self.min {
self.min = sample;
}
Ok(self.min)
}
#[inline]
#[must_use]
pub fn min(&self) -> Option<$ty> {
if self.count == 0 {
Option::None
} else {
Option::Some(self.min)
}
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
#[must_use]
pub fn is_primed(&self) -> bool {
self.count > 0
}
#[inline]
pub fn reset(&mut self) {
self.min = $init;
self.count = 0;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
macro_rules! impl_running_min_int {
($name:ident, $ty:ty, $init:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
min: $ty,
count: u64,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
min: $init,
count: 0,
}
}
#[inline]
#[must_use]
pub fn update(&mut self, sample: $ty) -> $ty {
self.count += 1;
if sample < self.min {
self.min = sample;
}
self.min
}
#[inline]
#[must_use]
pub fn min(&self) -> Option<$ty> {
if self.count == 0 {
Option::None
} else {
Option::Some(self.min)
}
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
#[must_use]
pub fn is_primed(&self) -> bool {
self.count > 0
}
#[inline]
pub fn reset(&mut self) {
self.min = $init;
self.count = 0;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
macro_rules! impl_running_max_float {
($name:ident, $ty:ty, $init:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
max: $ty,
count: u64,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
max: $init,
count: 0,
}
}
#[inline]
pub fn update(&mut self, sample: $ty) -> Result<$ty, crate::DataError> {
check_finite!(sample);
self.count += 1;
if sample > self.max {
self.max = sample;
}
Ok(self.max)
}
#[inline]
#[must_use]
pub fn max(&self) -> Option<$ty> {
if self.count == 0 {
Option::None
} else {
Option::Some(self.max)
}
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
#[must_use]
pub fn is_primed(&self) -> bool {
self.count > 0
}
#[inline]
pub fn reset(&mut self) {
self.max = $init;
self.count = 0;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
macro_rules! impl_running_max_int {
($name:ident, $ty:ty, $init:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
max: $ty,
count: u64,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
max: $init,
count: 0,
}
}
#[inline]
#[must_use]
pub fn update(&mut self, sample: $ty) -> $ty {
self.count += 1;
if sample > self.max {
self.max = sample;
}
self.max
}
#[inline]
#[must_use]
pub fn max(&self) -> Option<$ty> {
if self.count == 0 {
Option::None
} else {
Option::Some(self.max)
}
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
#[must_use]
pub fn is_primed(&self) -> bool {
self.count > 0
}
#[inline]
pub fn reset(&mut self) {
self.max = $init;
self.count = 0;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
impl_running_min_float!(RunningMinF64, f64, f64::MAX);
impl_running_min_float!(RunningMinF32, f32, f32::MAX);
impl_running_min_int!(RunningMinI64, i64, i64::MAX);
impl_running_min_int!(RunningMinI32, i32, i32::MAX);
impl_running_min_int!(RunningMinI128, i128, i128::MAX);
impl_running_max_float!(RunningMaxF64, f64, f64::MIN);
impl_running_max_float!(RunningMaxF32, f32, f32::MIN);
impl_running_max_int!(RunningMaxI64, i64, i64::MIN);
impl_running_max_int!(RunningMaxI32, i32, i32::MIN);
impl_running_max_int!(RunningMaxI128, i128, i128::MIN);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn min_empty() {
let rm = RunningMinF64::new();
assert!(rm.min().is_none());
assert!(!rm.is_primed());
}
#[test]
#[allow(clippy::float_cmp)]
fn min_tracks() {
let mut rm = RunningMinF64::new();
assert_eq!(rm.update(50.0).unwrap(), 50.0);
assert_eq!(rm.update(30.0).unwrap(), 30.0);
assert_eq!(rm.update(40.0).unwrap(), 30.0); assert_eq!(rm.update(10.0).unwrap(), 10.0);
}
#[test]
#[allow(clippy::float_cmp)]
fn min_reset() {
let mut rm = RunningMinF64::new();
let _ = rm.update(10.0).unwrap();
rm.reset();
assert!(rm.min().is_none());
assert_eq!(rm.update(50.0).unwrap(), 50.0);
}
#[test]
fn min_i64() {
let mut rm = RunningMinI64::new();
assert_eq!(rm.update(100), 100);
assert_eq!(rm.update(50), 50);
assert_eq!(rm.update(75), 50);
}
#[test]
fn min_default() {
let rm = RunningMinF64::default();
assert_eq!(rm.count(), 0);
}
#[test]
fn max_empty() {
let rm = RunningMaxF64::new();
assert!(rm.max().is_none());
}
#[test]
#[allow(clippy::float_cmp)]
fn max_tracks() {
let mut rm = RunningMaxF64::new();
assert_eq!(rm.update(30.0).unwrap(), 30.0);
assert_eq!(rm.update(50.0).unwrap(), 50.0);
assert_eq!(rm.update(40.0).unwrap(), 50.0); assert_eq!(rm.update(90.0).unwrap(), 90.0);
}
#[test]
fn max_i64() {
let mut rm = RunningMaxI64::new();
assert_eq!(rm.update(50), 50);
assert_eq!(rm.update(100), 100);
assert_eq!(rm.update(75), 100);
}
#[test]
fn max_i32() {
let mut rm = RunningMaxI32::new();
assert_eq!(rm.update(10), 10);
assert_eq!(rm.update(20), 20);
}
#[test]
#[allow(clippy::float_cmp)]
fn min_f32() {
let mut rm = RunningMinF32::new();
assert_eq!(rm.update(50.0).unwrap(), 50.0);
assert_eq!(rm.update(30.0).unwrap(), 30.0);
}
#[test]
#[allow(clippy::float_cmp)]
fn max_f32() {
let mut rm = RunningMaxF32::new();
assert_eq!(rm.update(30.0).unwrap(), 30.0);
assert_eq!(rm.update(50.0).unwrap(), 50.0);
}
#[test]
fn min_i128() {
let mut rm = RunningMinI128::new();
assert_eq!(rm.update(100), 100);
assert_eq!(rm.update(50), 50);
assert_eq!(rm.update(75), 50);
}
#[test]
fn max_i128() {
let mut rm = RunningMaxI128::new();
assert_eq!(rm.update(50), 50);
assert_eq!(rm.update(100), 100);
assert_eq!(rm.update(75), 100);
}
#[test]
fn rejects_nan_and_inf() {
let mut min64 = RunningMinF64::new();
assert!(matches!(
min64.update(f64::NAN),
Err(crate::DataError::NotANumber)
));
assert!(matches!(
min64.update(f64::INFINITY),
Err(crate::DataError::Infinite)
));
assert!(matches!(
min64.update(f64::NEG_INFINITY),
Err(crate::DataError::Infinite)
));
let mut max64 = RunningMaxF64::new();
assert!(matches!(
max64.update(f64::NAN),
Err(crate::DataError::NotANumber)
));
assert!(matches!(
max64.update(f64::INFINITY),
Err(crate::DataError::Infinite)
));
let mut min32 = RunningMinF32::new();
assert!(matches!(
min32.update(f32::NAN),
Err(crate::DataError::NotANumber)
));
let mut max32 = RunningMaxF32::new();
assert!(matches!(
max32.update(f32::NAN),
Err(crate::DataError::NotANumber)
));
}
}