use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VectorClockOrder {
HappensBefore,
HappensAfter,
Concurrent,
Equal,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct VectorClock {
clocks: HashMap<u32, u64>,
}
impl VectorClock {
pub fn new() -> Self {
Self {
clocks: HashMap::new(),
}
}
pub fn get(&self, node_id: u32) -> u64 {
*self.clocks.get(&node_id).unwrap_or(&0)
}
pub fn tick(&mut self, node_id: u32) {
let entry = self.clocks.entry(node_id).or_insert(0);
*entry += 1;
}
pub fn merge_and_tick(&mut self, other: &VectorClock, self_node_id: u32) {
for (&node_id, &clock) in &other.clocks {
let entry = self.clocks.entry(node_id).or_insert(0);
*entry = (*entry).max(clock);
}
self.tick(self_node_id);
}
pub fn merge(&mut self, other: &VectorClock) {
for (&node_id, &clock) in &other.clocks {
let entry = self.clocks.entry(node_id).or_insert(0);
*entry = (*entry).max(clock);
}
}
pub fn compare(&self, other: &VectorClock) -> VectorClockOrder {
let mut self_gt = false; let mut other_gt = false;
let all_nodes: std::collections::HashSet<u32> = self
.clocks
.keys()
.chain(other.clocks.keys())
.copied()
.collect();
for node in all_nodes {
let lhs = self.get(node);
let rhs = other.get(node);
if lhs > rhs {
self_gt = true;
}
if rhs > lhs {
other_gt = true;
}
}
match (self_gt, other_gt) {
(false, false) => VectorClockOrder::Equal,
(true, false) => VectorClockOrder::HappensAfter,
(false, true) => VectorClockOrder::HappensBefore,
(true, true) => VectorClockOrder::Concurrent,
}
}
pub fn happens_before(&self, other: &VectorClock) -> bool {
self.compare(other) == VectorClockOrder::HappensBefore
}
pub fn is_concurrent(&self, other: &VectorClock) -> bool {
self.compare(other) == VectorClockOrder::Concurrent
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tick_and_get() {
let mut vc = VectorClock::new();
vc.tick(1);
vc.tick(1);
vc.tick(2);
assert_eq!(vc.get(1), 2);
assert_eq!(vc.get(2), 1);
assert_eq!(vc.get(99), 0);
}
#[test]
fn test_happens_before() {
let mut a = VectorClock::new();
a.tick(1);
let mut b = VectorClock::new();
b.merge_and_tick(&a, 2);
assert_eq!(a.compare(&b), VectorClockOrder::HappensBefore);
assert_eq!(b.compare(&a), VectorClockOrder::HappensAfter);
}
#[test]
fn test_concurrent() {
let mut a = VectorClock::new();
a.tick(1);
let mut b = VectorClock::new();
b.tick(2);
assert_eq!(a.compare(&b), VectorClockOrder::Concurrent);
assert!(a.is_concurrent(&b));
}
#[test]
fn test_merge() {
let mut a = VectorClock::new();
a.tick(1);
a.tick(1); let mut b = VectorClock::new();
b.tick(2); b.merge(&a);
assert_eq!(b.get(1), 2);
assert_eq!(b.get(2), 1);
}
#[test]
fn test_equal_clocks() {
let mut a = VectorClock::new();
let mut b = VectorClock::new();
a.tick(1);
b.tick(1);
assert_eq!(a.compare(&b), VectorClockOrder::Equal);
}
}