#[derive(Debug, Clone)]
pub struct LevelCrossingF64 {
threshold: f64,
was_above: bool,
crossings: u64,
initialized: bool,
}
impl LevelCrossingF64 {
#[inline]
#[must_use]
pub fn new(threshold: f64) -> Self {
Self {
threshold,
was_above: false,
crossings: 0,
initialized: false,
}
}
#[inline]
pub fn update(&mut self, sample: f64) -> Result<bool, crate::DataError> {
check_finite!(sample);
let is_above = sample >= self.threshold;
if !self.initialized {
self.was_above = is_above;
self.initialized = true;
return Ok(false);
}
if is_above == self.was_above {
Ok(false)
} else {
self.was_above = is_above;
self.crossings += 1;
Ok(true)
}
}
#[inline]
#[must_use]
pub fn crossing_count(&self) -> u64 {
self.crossings
}
#[inline]
pub fn reset(&mut self) {
self.was_above = false;
self.crossings = 0;
self.initialized = false;
}
}
#[derive(Debug, Clone)]
pub struct LevelCrossingI64 {
threshold: i64,
was_above: bool,
crossings: u64,
initialized: bool,
}
impl LevelCrossingI64 {
#[inline]
#[must_use]
pub fn new(threshold: i64) -> Self {
Self {
threshold,
was_above: false,
crossings: 0,
initialized: false,
}
}
#[inline]
#[must_use]
pub fn update(&mut self, sample: i64) -> bool {
let is_above = sample >= self.threshold;
if !self.initialized {
self.was_above = is_above;
self.initialized = true;
return false;
}
if is_above == self.was_above {
false
} else {
self.was_above = is_above;
self.crossings += 1;
true
}
}
#[inline]
#[must_use]
pub fn crossing_count(&self) -> u64 {
self.crossings
}
#[inline]
pub fn reset(&mut self) {
self.was_above = false;
self.crossings = 0;
self.initialized = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_sample_no_crossing() {
let mut lc = LevelCrossingF64::new(50.0);
assert!(!lc.update(30.0).unwrap());
}
#[test]
fn upward_crossing() {
let mut lc = LevelCrossingF64::new(50.0);
assert!(!lc.update(30.0).unwrap());
assert!(lc.update(60.0).unwrap()); assert_eq!(lc.crossing_count(), 1);
}
#[test]
fn downward_crossing() {
let mut lc = LevelCrossingF64::new(50.0);
assert!(!lc.update(60.0).unwrap());
assert!(lc.update(40.0).unwrap()); assert_eq!(lc.crossing_count(), 1);
}
#[test]
fn multiple_crossings() {
let mut lc = LevelCrossingF64::new(50.0);
let _ = lc.update(30.0).unwrap();
let _ = lc.update(60.0).unwrap(); let _ = lc.update(40.0).unwrap(); let _ = lc.update(70.0).unwrap(); assert_eq!(lc.crossing_count(), 3);
}
#[test]
fn no_crossing_same_side() {
let mut lc = LevelCrossingF64::new(50.0);
let _ = lc.update(30.0).unwrap();
assert!(!lc.update(40.0).unwrap()); assert!(!lc.update(20.0).unwrap()); }
#[test]
fn i64_basic() {
let mut lc = LevelCrossingI64::new(100);
assert!(!lc.update(50));
assert!(lc.update(150)); }
#[test]
fn reset() {
let mut lc = LevelCrossingF64::new(50.0);
let _ = lc.update(30.0).unwrap();
let _ = lc.update(60.0).unwrap();
lc.reset();
assert_eq!(lc.crossing_count(), 0);
}
#[test]
fn rejects_nan_and_inf() {
let mut lc = LevelCrossingF64::new(50.0);
assert_eq!(lc.update(f64::NAN), Err(crate::DataError::NotANumber));
assert_eq!(lc.update(f64::INFINITY), Err(crate::DataError::Infinite));
assert_eq!(
lc.update(f64::NEG_INFINITY),
Err(crate::DataError::Infinite)
);
}
}