use std::{
collections::{HashMap, HashSet},
ops::{BitXor, BitXorAssign, Deref},
};
use serde::{Deserialize, Serialize};
use super::classification::{IntervalIdx, SubIntervalIdx};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Default, Deserialize, Serialize)]
#[repr(transparent)]
pub struct Fingerprint(u64);
impl Deref for Fingerprint {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl BitXor for Fingerprint {
type Output = Fingerprint;
fn bitxor(self, rhs: Self) -> Self::Output {
(self.0 ^ rhs.0).into()
}
}
impl BitXorAssign for Fingerprint {
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0
}
}
impl From<u64> for Fingerprint {
fn from(value: u64) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Digest {
pub(crate) configuration_fingerprint: Fingerprint,
pub(crate) cold_era_fingerprint: Fingerprint,
pub(crate) warm_era_fingerprints: HashMap<IntervalIdx, Fingerprint>,
pub(crate) hot_era_fingerprints: HashMap<IntervalIdx, HashMap<SubIntervalIdx, Fingerprint>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct DigestDiff {
pub(crate) cold_eras_differ: bool,
pub(crate) warm_eras_differences: HashSet<IntervalIdx>,
pub(crate) hot_eras_differences: HashMap<IntervalIdx, HashSet<SubIntervalIdx>>,
}
impl Digest {
pub(crate) fn diff(&self, mut other: Digest) -> Option<DigestDiff> {
if self.configuration_fingerprint != other.configuration_fingerprint {
return None;
}
for (interval_idx, sub_intervals_fingerprints) in &self.hot_era_fingerprints {
if let Some(other_sub_intervals_fingerprints) =
other.hot_era_fingerprints.get_mut(interval_idx)
{
other_sub_intervals_fingerprints.retain(|other_idx, other_fingerprint| {
match sub_intervals_fingerprints.get(other_idx) {
Some(fingerprint) => other_fingerprint != fingerprint,
None => true,
}
});
}
}
other
.hot_era_fingerprints
.retain(|_, sub_intervals| !sub_intervals.is_empty());
other
.warm_era_fingerprints
.retain(|other_idx, other_fingerprint| {
match self.warm_era_fingerprints.get(other_idx) {
Some(fingerprint) => other_fingerprint != fingerprint,
None => true,
}
});
if !other.hot_era_fingerprints.is_empty() || !other.warm_era_fingerprints.is_empty() {
return Some(DigestDiff {
cold_eras_differ: self.cold_era_fingerprint != other.cold_era_fingerprint,
warm_eras_differences: other.warm_era_fingerprints.into_keys().collect(),
hot_eras_differences: other
.hot_era_fingerprints
.into_iter()
.map(|(interval_idx, sub_intervals)| {
(interval_idx, sub_intervals.into_keys().collect())
})
.collect(),
});
}
if self.cold_era_fingerprint != other.cold_era_fingerprint {
return Some(DigestDiff {
cold_eras_differ: true,
warm_eras_differences: HashSet::default(),
hot_eras_differences: HashMap::default(),
});
}
None
}
}
#[cfg(test)]
#[path = "tests/digest.test.rs"]
mod tests;