use crate::Mergeable;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ExtremaF64 {
min_bits: u64,
max_bits: u64,
nan: bool,
seen: bool,
count: u64,
}
impl Default for ExtremaF64 {
fn default() -> Self {
Self::new()
}
}
impl ExtremaF64 {
pub const BYTES: usize = 8 + 8 + 1 + 8;
pub const fn new() -> Self {
Self {
min_bits: 0,
max_bits: 0,
nan: false,
seen: false,
count: 0,
}
}
pub fn add(&mut self, x: f64) {
self.count = self.count.saturating_add(1);
if x.is_nan() {
self.nan = true;
return;
}
if !self.seen {
self.min_bits = x.to_bits();
self.max_bits = x.to_bits();
self.seen = true;
return;
}
if x.total_cmp(&f64::from_bits(self.min_bits)).is_lt() {
self.min_bits = x.to_bits();
}
if x.total_cmp(&f64::from_bits(self.max_bits)).is_gt() {
self.max_bits = x.to_bits();
}
}
pub fn min(&self) -> Option<f64> {
if self.nan || !self.seen {
return None;
}
Some(f64::from_bits(self.min_bits))
}
pub fn max(&self) -> Option<f64> {
if self.nan || !self.seen {
return None;
}
Some(f64::from_bits(self.max_bits))
}
pub fn range(&self) -> Option<f64> {
Some(self.max()? - self.min()?)
}
pub const fn count(&self) -> u64 {
self.count
}
pub fn to_bytes(&self) -> [u8; Self::BYTES] {
let mut out = [0u8; Self::BYTES];
out[..8].copy_from_slice(&self.min_bits.to_le_bytes());
out[8..16].copy_from_slice(&self.max_bits.to_le_bytes());
out[16] = (self.nan as u8) | ((self.seen as u8) << 1);
out[17..].copy_from_slice(&self.count.to_le_bytes());
out
}
pub fn from_bytes(b: &[u8; Self::BYTES]) -> Option<Self> {
if b[16] & !0b11 != 0 {
return None;
}
let mut w = [0u8; 8];
w.copy_from_slice(&b[..8]);
let min_bits = u64::from_le_bytes(w);
w.copy_from_slice(&b[8..16]);
let max_bits = u64::from_le_bytes(w);
if b[16] & 2 == 0 && (min_bits != 0 || max_bits != 0) {
return None; }
w.copy_from_slice(&b[17..]);
Some(Self {
min_bits,
max_bits,
nan: b[16] & 1 != 0,
seen: b[16] & 2 != 0,
count: u64::from_le_bytes(w),
})
}
}
impl Mergeable for ExtremaF64 {
fn merge(&mut self, other: &Self) {
self.count = self.count.saturating_add(other.count);
self.nan |= other.nan;
if !other.seen {
return;
}
if !self.seen {
self.min_bits = other.min_bits;
self.max_bits = other.max_bits;
self.seen = true;
return;
}
let (omin, omax) = (
f64::from_bits(other.min_bits),
f64::from_bits(other.max_bits),
);
if omin.total_cmp(&f64::from_bits(self.min_bits)).is_lt() {
self.min_bits = other.min_bits;
}
if omax.total_cmp(&f64::from_bits(self.max_bits)).is_gt() {
self.max_bits = other.max_bits;
}
}
fn count(&self) -> u64 {
self.count
}
#[cfg(feature = "std")]
fn encode(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
#[cfg(feature = "std")]
fn decode(bytes: &[u8]) -> Option<Self> {
let arr: &[u8; Self::BYTES] = bytes.try_into().ok()?;
Self::from_bytes(arr)
}
}