using System;
using System.Collections.Generic;
using System.Linq;
namespace ZB.MOM.WW.CBDDC.Core;
///
/// Represents a Vector Clock for tracking causality in a distributed system.
/// Maps NodeId -> HlcTimestamp to track the latest known state of each node.
///
public class VectorClock
{
private readonly Dictionary _clock;
///
/// Initializes a new empty vector clock.
///
public VectorClock()
{
_clock = new Dictionary(StringComparer.Ordinal);
}
///
/// Initializes a new vector clock from an existing clock state.
///
/// The clock state to copy.
public VectorClock(Dictionary clock)
{
_clock = new Dictionary(clock, StringComparer.Ordinal);
}
///
/// Gets all node IDs in this vector clock.
///
public IEnumerable NodeIds => _clock.Keys;
///
/// Gets the timestamp for a specific node, or default if not present.
///
/// The node identifier.
public HlcTimestamp GetTimestamp(string nodeId)
{
return _clock.TryGetValue(nodeId, out var ts) ? ts : default;
}
///
/// Sets or updates the timestamp for a specific node.
///
/// The node identifier.
/// The timestamp to set.
public void SetTimestamp(string nodeId, HlcTimestamp timestamp)
{
_clock[nodeId] = timestamp;
}
///
/// Merges another vector clock into this one, taking the maximum timestamp for each node.
///
/// The vector clock to merge from.
public void Merge(VectorClock other)
{
foreach (var nodeId in other.NodeIds)
{
var otherTs = other.GetTimestamp(nodeId);
if (!_clock.TryGetValue(nodeId, out var currentTs) || otherTs.CompareTo(currentTs) > 0)
{
_clock[nodeId] = otherTs;
}
}
}
///
/// Compares this vector clock with another to determine causality.
/// Returns:
/// - Positive: This is strictly ahead (dominates other)
/// - Negative: Other is strictly ahead (other dominates this)
/// - Zero: Concurrent (neither dominates)
///
/// The vector clock to compare with.
public CausalityRelation CompareTo(VectorClock other)
{
bool thisAhead = false;
bool otherAhead = false;
var allNodes = new HashSet(_clock.Keys.Union(other._clock.Keys), StringComparer.Ordinal);
foreach (var nodeId in allNodes)
{
var thisTs = GetTimestamp(nodeId);
var otherTs = other.GetTimestamp(nodeId);
int cmp = thisTs.CompareTo(otherTs);
if (cmp > 0)
{
thisAhead = true;
}
else if (cmp < 0)
{
otherAhead = true;
}
// Early exit if concurrent
if (thisAhead && otherAhead)
{
return CausalityRelation.Concurrent;
}
}
if (thisAhead && !otherAhead)
return CausalityRelation.StrictlyAhead;
if (otherAhead && !thisAhead)
return CausalityRelation.StrictlyBehind;
return CausalityRelation.Equal;
}
///
/// Determines which nodes have updates that this vector clock doesn't have.
/// Returns node IDs where the other vector clock is ahead.
///
/// The vector clock to compare against.
public IEnumerable GetNodesWithUpdates(VectorClock other)
{
var allNodes = new HashSet(_clock.Keys, StringComparer.Ordinal);
foreach (var nodeId in other._clock.Keys)
{
allNodes.Add(nodeId);
}
foreach (var nodeId in allNodes)
{
var thisTs = GetTimestamp(nodeId);
var otherTs = other.GetTimestamp(nodeId);
if (otherTs.CompareTo(thisTs) > 0)
{
yield return nodeId;
}
}
}
///
/// Determines which nodes have updates that the other vector clock doesn't have.
/// Returns node IDs where this vector clock is ahead.
///
/// The vector clock to compare against.
public IEnumerable GetNodesToPush(VectorClock other)
{
var allNodes = new HashSet(_clock.Keys.Union(other._clock.Keys), StringComparer.Ordinal);
foreach (var nodeId in allNodes)
{
var thisTs = GetTimestamp(nodeId);
var otherTs = other.GetTimestamp(nodeId);
if (thisTs.CompareTo(otherTs) > 0)
{
yield return nodeId;
}
}
}
///
/// Creates a copy of this vector clock.
///
public VectorClock Clone()
{
return new VectorClock(new Dictionary(_clock, StringComparer.Ordinal));
}
///
public override string ToString()
{
if (_clock.Count == 0)
return "{}";
var entries = _clock.Select(kvp => $"{kvp.Key}:{kvp.Value}");
return "{" + string.Join(", ", entries) + "}";
}
}
///
/// Represents the causality relationship between two vector clocks.
///
public enum CausalityRelation
{
/// Both vector clocks are equal.
Equal,
/// This vector clock is strictly ahead (dominates).
StrictlyAhead,
/// This vector clock is strictly behind (dominated).
StrictlyBehind,
/// Vector clocks are concurrent (neither dominates).
Concurrent
}