///|
/// A dot identifies exactly one event in one replica's monotonic sequence.
pub(all) struct Dot {
  replica : String
  counter : Int
} derive(Eq, Debug)

///|
/// Validation errors for dots and dot collections.
pub(all) enum DotError {
  EmptyDotReplica
  NonPositiveDotCounter(Int)
} derive(Eq, Debug)

///|
/// Construct a validated causal dot.
pub fn Dot::new(replica : String, counter : Int) -> Result[Dot, DotError] {
  if replica.length() == 0 {
    Err(EmptyDotReplica)
  } else if counter <= 0 {
    Err(NonPositiveDotCounter(counter))
  } else {
    Ok({ replica, counter })
  }
}

///|
/// Convert a dot to the version vector that covers only its own sequence.
pub fn Dot::context(self : Dot) -> VersionVector {
  VersionVector::from_entries([{ replica: self.replica, counter: self.counter }]).unwrap()
}

///|
/// Test whether a version vector has observed this dot.
pub fn Dot::is_covered_by(self : Dot, context : VersionVector) -> Bool {
  context.counter(self.replica) >= self.counter
}

///|
/// Deterministic MoonBit string ordering by replica and then counter.
pub fn Dot::compare(self : Dot, other : Dot) -> Int {
  if self.replica < other.replica {
    -1
  } else if self.replica > other.replica {
    1
  } else if self.counter < other.counter {
    -1
  } else if self.counter > other.counter {
    1
  } else {
    0
  }
}

///| A normalized set of unique dots. Ordering is deterministic and does not

///|
/// depend on message arrival order.
pub struct DotSet {
  dots : Array[Dot]
} derive(Eq, Debug)

///|
/// Create an empty dot set.
pub fn DotSet::new() -> DotSet {
  { dots: [] }
}

///|
/// Validate, deduplicate, and sort a collection of dots.
pub fn DotSet::from_array(dots : Array[Dot]) -> Result[DotSet, DotError] {
  let mut output = DotSet::new()
  for dot in dots {
    match Dot::new(dot.replica, dot.counter) {
      Err(error) => return Err(error)
      Ok(valid) => output = output.add(valid)
    }
  }
  Ok(output)
}

///|
/// Return normalized dots for inspection or host serialization.
pub fn DotSet::dots(self : DotSet) -> Array[Dot] {
  let output : Array[Dot] = []
  for dot in self.dots {
    output.push(dot)
  }
  output
}

///|
/// Number of distinct dots.
pub fn DotSet::length(self : DotSet) -> Int {
  self.dots.length()
}

///|
/// Test exact dot membership.
pub fn DotSet::contains(self : DotSet, candidate : Dot) -> Bool {
  for dot in self.dots {
    if dot == candidate {
      return true
    }
  }
  false
}

///|
/// Add one dot, retaining deterministic order.
pub fn DotSet::add(self : DotSet, candidate : Dot) -> DotSet {
  if self.contains(candidate) {
    return self
  }
  let output : Array[Dot] = []
  let mut inserted = false
  for dot in self.dots {
    if !inserted && candidate.compare(dot) < 0 {
      output.push(candidate)
      inserted = true
    }
    output.push(dot)
  }
  if !inserted {
    output.push(candidate)
  }
  { dots: output }
}

///|
/// Remove one exact dot.
pub fn DotSet::remove(self : DotSet, candidate : Dot) -> DotSet {
  let output : Array[Dot] = []
  for dot in self.dots {
    if dot != candidate {
      output.push(dot)
    }
  }
  { dots: output }
}

///|
/// Set union.
pub fn DotSet::union(self : DotSet, other : DotSet) -> DotSet {
  let mut output = self
  for dot in other.dots {
    output = output.add(dot)
  }
  output
}

///|
/// Set intersection.
pub fn DotSet::intersection(self : DotSet, other : DotSet) -> DotSet {
  let output : Array[Dot] = []
  for dot in self.dots {
    if other.contains(dot) {
      output.push(dot)
    }
  }
  { dots: output }
}

///|
/// Set difference.
pub fn DotSet::difference(self : DotSet, other : DotSet) -> DotSet {
  let output : Array[Dot] = []
  for dot in self.dots {
    if !other.contains(dot) {
      output.push(dot)
    }
  }
  { dots: output }
}

///|
/// Keep only dots not already covered by a version vector.
pub fn DotSet::after(self : DotSet, context : VersionVector) -> DotSet {
  let output : Array[Dot] = []
  for dot in self.dots {
    if !dot.is_covered_by(context) {
      output.push(dot)
    }
  }
  { dots: output }
}

///|
/// Keep only dots covered by a version vector.
pub fn DotSet::covered(self : DotSet, context : VersionVector) -> DotSet {
  let output : Array[Dot] = []
  for dot in self.dots {
    if dot.is_covered_by(context) {
      output.push(dot)
    }
  }
  { dots: output }
}

///|
/// Build the component-wise maximum context represented by these dots.
pub fn DotSet::frontier(self : DotSet) -> VersionVector {
  let entries : Array[VectorEntry] = []
  for dot in self.dots {
    entries.push({ replica: dot.replica, counter: dot.counter })
  }
  VersionVector::from_entries(entries).unwrap()
}

///|
/// Return true when every dot in this set occurs in the other set.
pub fn DotSet::is_subset_of(self : DotSet, other : DotSet) -> Bool {
  for dot in self.dots {
    if !other.contains(dot) {
      return false
    }
  }
  true
}

///|
/// Return the next counter after the greatest dot for a replica.
pub fn DotSet::next_counter(self : DotSet, replica : String) -> Int {
  let mut greatest = 0
  for dot in self.dots {
    if dot.replica == replica && dot.counter > greatest {
      greatest = dot.counter
    }
  }
  greatest + 1
}