use crate::lattice::Lattice;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LWWRegister<T: Ord + Clone, K: Ord + Clone> {
value: Option<T>,
timestamp: u64,
replica_id: K,
}
impl<T: Ord + Clone, K: Ord + Clone> LWWRegister<T, K> {
pub fn new(replica_id: K) -> Self {
Self {
value: None,
timestamp: 0,
replica_id,
}
}
pub fn set(&mut self, value: T, timestamp: u64, replica_id: K) {
if timestamp > self.timestamp
|| (timestamp == self.timestamp && replica_id >= self.replica_id)
{
self.value = Some(value);
self.timestamp = timestamp;
self.replica_id = replica_id;
}
}
pub fn get(&self) -> Option<&T> {
self.value.as_ref()
}
pub fn timestamp(&self) -> u64 {
self.timestamp
}
pub fn replica_id(&self) -> &K {
&self.replica_id
}
pub fn is_empty(&self) -> bool {
self.value.is_none()
}
pub fn clear(&mut self) {
self.value = None;
self.timestamp = 0;
}
}
impl<T: Ord + Clone, K: Ord + Clone + Default> Default for LWWRegister<T, K> {
fn default() -> Self {
Self::new(K::default())
}
}
impl<T: Ord + Clone, K: Ord + Clone + Default> Lattice for LWWRegister<T, K> {
fn bottom() -> Self {
Self {
value: None,
timestamp: 0,
replica_id: K::default(),
}
}
fn join(&self, other: &Self) -> Self {
let self_wins = match self.timestamp.cmp(&other.timestamp) {
std::cmp::Ordering::Greater => true,
std::cmp::Ordering::Less => false,
std::cmp::Ordering::Equal => {
match self.replica_id.cmp(&other.replica_id) {
std::cmp::Ordering::Greater => true,
std::cmp::Ordering::Less => false,
std::cmp::Ordering::Equal => {
self.value >= other.value
}
}
}
};
if self_wins {
self.clone()
} else {
other.clone()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lwwreg_basic_operations() {
let mut reg: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
assert!(reg.is_empty());
assert_eq!(reg.get(), None);
reg.set(42, 100, "replica1".to_string());
assert_eq!(reg.get(), Some(&42));
assert_eq!(reg.timestamp(), 100);
}
#[test]
fn test_lwwreg_higher_timestamp_wins() {
let mut reg: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg.set(10, 100, "replica1".to_string());
assert_eq!(reg.get(), Some(&10));
reg.set(20, 200, "replica2".to_string());
assert_eq!(reg.get(), Some(&20));
reg.set(30, 150, "replica1".to_string());
assert_eq!(reg.get(), Some(&20));
}
#[test]
fn test_lwwreg_tie_break_replica_id() {
let mut reg: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg.set(10, 100, "replica1".to_string());
assert_eq!(reg.get(), Some(&10));
reg.set(20, 100, "replica2".to_string());
assert_eq!(reg.get(), Some(&20));
reg.set(30, 100, "replica1".to_string());
assert_eq!(reg.get(), Some(&20));
}
#[test]
fn test_lwwreg_join_idempotent() {
let mut reg1: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg1.set(42, 100, "replica1".to_string());
let joined = reg1.join(®1);
assert_eq!(joined.get(), Some(&42));
assert_eq!(joined.timestamp(), 100);
}
#[test]
fn test_lwwreg_join_commutative() {
let mut reg1: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg1.set(10, 100, "replica1".to_string());
let mut reg2: LWWRegister<i32, String> = LWWRegister::new("replica2".to_string());
reg2.set(20, 150, "replica2".to_string());
let joined1 = reg1.join(®2);
let joined2 = reg2.join(®1);
assert_eq!(joined1.get(), joined2.get());
assert_eq!(joined1.timestamp(), joined2.timestamp());
}
#[test]
fn test_lwwreg_join_associative() {
let mut reg1: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg1.set(10, 100, "replica1".to_string());
let mut reg2: LWWRegister<i32, String> = LWWRegister::new("replica2".to_string());
reg2.set(20, 150, "replica2".to_string());
let mut reg3: LWWRegister<i32, String> = LWWRegister::new("replica3".to_string());
reg3.set(30, 120, "replica3".to_string());
let left = reg1.join(®2).join(®3);
let right = reg1.join(®2.join(®3));
assert_eq!(left.get(), right.get());
assert_eq!(left.timestamp(), right.timestamp());
}
#[test]
fn test_lwwreg_bottom_is_identity() {
let mut reg: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg.set(42, 100, "replica1".to_string());
let bottom = LWWRegister::bottom();
let joined = reg.join(&bottom);
assert_eq!(joined.get(), reg.get());
assert_eq!(joined.timestamp(), reg.timestamp());
}
#[test]
fn test_lwwreg_serialization() {
let mut reg: LWWRegister<i32, String> = LWWRegister::new("replica1".to_string());
reg.set(42, 100, "replica1".to_string());
let serialized = serde_json::to_string(®).unwrap();
let deserialized: LWWRegister<i32, String> = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.get(), Some(&42));
assert_eq!(deserialized.timestamp(), 100);
}
}