use super::causal::VClock;
use super::delta::DeltaCrdt;
use super::replica::{generate_replica_id, ReplicaId};
use super::Merge;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PNCounterDelta {
pub increments: HashMap<ReplicaId, u64>,
pub decrements: HashMap<ReplicaId, u64>,
pub version: VClock,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PNCounter {
increments: HashMap<ReplicaId, u64>,
decrements: HashMap<ReplicaId, u64>,
replica_id: ReplicaId,
#[serde(default)]
version: VClock,
}
impl PNCounter {
pub fn new() -> Self {
Self {
increments: HashMap::new(),
decrements: HashMap::new(),
replica_id: generate_replica_id(),
version: VClock::new(),
}
}
pub fn with_replica_id(id: ReplicaId) -> Self {
Self {
increments: HashMap::new(),
decrements: HashMap::new(),
replica_id: id,
version: VClock::new(),
}
}
pub fn increment(&mut self, amount: u64) {
*self.increments.entry(self.replica_id).or_insert(0) += amount;
self.version.increment(self.replica_id);
}
pub fn decrement(&mut self, amount: u64) {
*self.decrements.entry(self.replica_id).or_insert(0) += amount;
self.version.increment(self.replica_id);
}
pub fn value(&self) -> i64 {
let inc: u64 = self.increments.values().sum();
let dec: u64 = self.decrements.values().sum();
inc as i64 - dec as i64
}
pub fn replica_id(&self) -> ReplicaId {
self.replica_id
}
}
impl Merge for PNCounter {
fn merge(&mut self, other: &Self) {
for (&replica, &count) in &other.increments {
let entry = self.increments.entry(replica).or_insert(0);
*entry = (*entry).max(count);
}
for (&replica, &count) in &other.decrements {
let entry = self.decrements.entry(replica).or_insert(0);
*entry = (*entry).max(count);
}
self.version.merge_vclock(&other.version);
}
}
impl DeltaCrdt for PNCounter {
type Delta = PNCounterDelta;
fn delta_since(&self, since: &VClock) -> Option<Self::Delta> {
if since.dominates(&self.version) {
return None;
}
Some(PNCounterDelta {
increments: self.increments.clone(),
decrements: self.decrements.clone(),
version: self.version.clone(),
})
}
fn apply_delta(&mut self, delta: &Self::Delta) {
for (&replica, &count) in &delta.increments {
let entry = self.increments.entry(replica).or_insert(0);
*entry = (*entry).max(count);
}
for (&replica, &count) in &delta.decrements {
let entry = self.decrements.entry(replica).or_insert(0);
*entry = (*entry).max(count);
}
self.version.merge_vclock(&delta.version);
}
fn version(&self) -> VClock {
self.version.clone()
}
}
impl PartialEq<i64> for PNCounter {
fn eq(&self, other: &i64) -> bool {
self.value() == *other
}
}
impl PartialEq<i32> for PNCounter {
fn eq(&self, other: &i32) -> bool {
self.value() == (*other as i64)
}
}
impl Default for PNCounter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pncounter_new() {
let c = PNCounter::new();
assert_eq!(c.value(), 0);
}
#[test]
fn test_pncounter_increment_decrement() {
let mut c = PNCounter::with_replica_id(1);
c.increment(10);
c.decrement(3);
assert_eq!(c.value(), 7);
}
#[test]
fn test_pncounter_negative() {
let mut c = PNCounter::with_replica_id(1);
c.decrement(5);
assert_eq!(c.value(), -5);
}
#[test]
fn test_pncounter_merge() {
let mut a = PNCounter::with_replica_id(1);
let mut b = PNCounter::with_replica_id(2);
a.increment(10);
b.decrement(3);
a.merge(&b);
assert_eq!(a.value(), 7);
}
#[test]
fn test_pncounter_merge_commutative() {
let mut a = PNCounter::with_replica_id(1);
let mut b = PNCounter::with_replica_id(2);
a.increment(10);
b.decrement(5);
let mut a1 = a.clone();
let mut b1 = b.clone();
a1.merge(&b);
b1.merge(&a);
assert_eq!(a1.value(), b1.value());
}
}