Skip to content

Commit

Permalink
we also don’t need dotset
Browse files Browse the repository at this point in the history
  • Loading branch information
rmgk committed Feb 6, 2024
1 parent f51481d commit b089e46
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 88 deletions.
3 changes: 1 addition & 2 deletions Modules/RDTs/src/main/scala/kofre/base/Bottom.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package kofre.base

import kofre.dotted.{DotFun, DotSet, Dotted}
import kofre.dotted.{DotFun, Dotted}
import kofre.time.Dots

import scala.collection.immutable.Queue
Expand Down Expand Up @@ -58,7 +58,6 @@ object Bottom {
}

given dotFun[V]: Bottom[DotFun[V]] = Bottom.derived
given dotSet[K, V]: Bottom[DotSet] = Bottom.derived
given dots: Bottom[Dots] = Bottom.derived
given dotted[A: Bottom]: Bottom[Dotted[A]] = Bottom.derived

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ package kofre.datatypes.contextual

import kofre.base.{Bottom, Lattice}

import kofre.dotted.{DotSet, Dotted, HasDots}
import kofre.dotted.{Dotted, HasDots}
import kofre.syntax.{OpsSyntaxHelper, ReplicaId}
import kofre.time.Dots

/** An EWFlag (Enable-Wins Flag) is a Delta CRDT modeling a boolean flag.
*
* When the flag is concurrently disabled and enabled then the enable operation wins, i.e. the resulting flag is enabled.
*/
case class EnableWinsFlag(inner: DotSet) derives Bottom
case class EnableWinsFlag(inner: Dots) derives Bottom

object EnableWinsFlag {

given lattice: Lattice[EnableWinsFlag] = Lattice.derived
given hasDotsEWF: HasDots[EnableWinsFlag] = HasDots.derived

val empty: EnableWinsFlag = EnableWinsFlag(DotSet.empty)
val empty: EnableWinsFlag = EnableWinsFlag(Dots.empty)

extension [C](container: C)
def enableWinsFlag: syntax[C] = syntax(container)
Expand All @@ -26,19 +26,19 @@ object EnableWinsFlag {
* It relies on the external context to track removals.
*/
implicit class syntax[C](container: C) extends OpsSyntaxHelper[C, EnableWinsFlag](container) {
def read(using IsQuery): Boolean = !current.inner.dots.isEmpty
def read(using IsQuery): Boolean = !current.dots.isEmpty

def enable(using ReplicaId)(): CausalMutator = {
val nextDot = context.nextDot(replicaId)
Dotted(
EnableWinsFlag(DotSet(Dots.single(nextDot))),
current.inner.dots add nextDot
EnableWinsFlag(Dots.single(nextDot)),
current.dots add nextDot
).mutator
}
def disable(using IsCausalMutator)(): C = {
Dotted(
EnableWinsFlag(DotSet(Dots.empty)),
current.inner.dots
EnableWinsFlag(Dots.empty),
current.dots
).mutator
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import kofre.dotted.HasDots.mapInstance
*
* When an element is concurrently added and removed/cleared from the set then the add operation wins, i.e. the resulting set contains the element.
*/
case class ReplicatedSet[E](inner: Map[E, DotSet])
case class ReplicatedSet[E](inner: Map[E, Dots])

object ReplicatedSet {

Expand Down Expand Up @@ -39,11 +39,11 @@ object ReplicatedSet {
def add(using ReplicaId)(e: E): CausalMutator = {
val dm = current.inner
val nextDot = context.nextDot(replicaId)
val v: DotSet = dm.getOrElse(e, DotSet.empty)
val v: Dots = dm.getOrElse(e, Dots.empty)

deltaState(
dm = Map(e -> DotSet(Dots.single(nextDot))),
cc = v.dots add nextDot
dm = Map(e -> Dots.single(nextDot)),
cc = v add nextDot
).mutator
}

Expand All @@ -55,31 +55,31 @@ object ReplicatedSet {

val ccontextSet = elems.foldLeft(nextDots) {
case (dots, e) => dm.get(e) match {
case Some(ds) => dots union ds.dots
case Some(ds) => dots union ds
case None => dots
}
}

deltaState(
dm = (elems zip nextDots.iterator.map(dot => DotSet(Dots.single(dot)))).toMap,
dm = (elems zip nextDots.iterator.map(dot => Dots.single(dot))).toMap,
cc = ccontextSet
).mutator
}

def remove(using IsQuery, IsCausalMutator)(e: E): C = {
val dm = current.inner
val v = dm.getOrElse(e, DotSet.empty)
val v = dm.getOrElse(e, Dots.empty)

deltaState(
v.dots
v
).mutator
}

def removeAll(elems: Iterable[E])(using IsQuery, IsCausalMutator): C = {
val dm = current.inner
val dotsToRemove = elems.foldLeft(Dots.empty) {
case (dots, e) => dm.get(e) match {
case Some(ds) => dots union ds.dots
case Some(ds) => dots union ds
case None => dots
}
}
Expand All @@ -93,7 +93,7 @@ object ReplicatedSet {
val dm = current.inner
val removedDots = dm.collect {
case (k, v) if cond(k) => v
}.foldLeft(Dots.empty)(_ union _.dots)
}.foldLeft(Dots.empty)(_ union _)

deltaState(
removedDots
Expand All @@ -110,7 +110,7 @@ object ReplicatedSet {
}

private def deltaState[E](
dm: Map[E, DotSet],
dm: Map[E, Dots],
cc: Dots
): Dotted[ReplicatedSet[E]] = Dotted(ReplicatedSet(dm), cc)

Expand Down
31 changes: 0 additions & 31 deletions Modules/RDTs/src/main/scala/kofre/dotted/DotSet.scala

This file was deleted.

15 changes: 14 additions & 1 deletion Modules/RDTs/src/main/scala/kofre/time/Dots.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kofre.time

import kofre.base.{Lattice, Uid}
import kofre.dotted.Dotted
import kofre.dotted.{Dotted, HasDots}

/** Essentially a more efficient version of a [[Set[Dot] ]].
* It typically tracks all dots known within some scope.
Expand Down Expand Up @@ -125,4 +125,17 @@ object Dots {
override def lteq(x: Dots, y: Dots): Boolean = x <= y
}


given HasDots[Dots] with {
extension (dotted: Dots)
def dots: Dots = dotted

/** Removes dots and anything associated to them from the value.
* In case the value becomes fully “empty” returns None
*/
def removeDots(dots: Dots): Option[Dots] =
val res = dotted.diff(dots)
if res.isEmpty then None else Some(res)
}

}
5 changes: 1 addition & 4 deletions Modules/RDTs/src/test/scala/test/kofre/DataGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,6 @@ object DataGenerator {
case Nil => acc
case h :: t => makeUnique(t, h.subtract(state) :: acc, state union h)

given arbDotSet: Arbitrary[DotSet] = Arbitrary:
arbDots.arbitrary.map(DotSet.apply)

case class SmallTimeSet(s: Set[Time])

given Arbitrary[SmallTimeSet] = Arbitrary(for {
Expand Down Expand Up @@ -175,7 +172,7 @@ object DataGenerator {
MultiVersionRegister(DotFun(pairs.toMap))

given arbEnableWinsFlag: Arbitrary[contextual.EnableWinsFlag] = Arbitrary:
arbDotSet.arbitrary.map(EnableWinsFlag.apply)
arbDots.arbitrary.map(EnableWinsFlag.apply)

given arbCausalDelta[A: Arbitrary: HasDots]: Arbitrary[CausalDelta[A]] = Arbitrary:
for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package test.kofre.baseproperties
import kofre.base.Uid
import kofre.datatypes.contextual.ReplicatedList
import kofre.datatypes.experiments.CausalStore
import kofre.dotted.{DotFun, DotSet, Dotted, HasDots}
import kofre.dotted.{DotFun, Dotted, HasDots}
import kofre.time.{ArrayRanges, Dots, VectorClock}
import org.scalacheck.Prop.*
import org.scalacheck.{Arbitrary, Gen}
Expand All @@ -13,7 +13,7 @@ import test.kofre.DataGenerator.RGAGen.given
import scala.collection.immutable.Queue
import scala.math.Ordering.Implicits.infixOrderingOps

class DotSetHDChecks extends HasDotsChecks[DotSet]
class DotSetHDChecks extends HasDotsChecks[Dots]
class CausalStoreHDChecks extends HasDotsChecks[CausalStore[DotFun[ExampleData]]]
class ReplicatedListHDChecks extends HasDotsChecks[ReplicatedList[ExampleData]]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kofre.datatypes.experiments.CausalStore
import kofre.datatypes.{
GrowOnlyCounter, GrowOnlyList, GrowOnlyMap, LastWriterWins, PosNegCounter, TwoPhaseSet, contextual
}
import kofre.dotted.{DotFun, DotSet, Dotted, HasDots}
import kofre.dotted.{DotFun, Dotted, HasDots}
import kofre.time.{Dots, Time, VectorClock}
import org.scalacheck.Prop.*
import org.scalacheck.{Arbitrary, Gen, Shrink}
Expand All @@ -20,12 +20,12 @@ class OpGraphChecks extends LatticePropertyChecks[OpGraph[ExampleData]
class CausalStoreChecks extends LatticePropertyChecks[CausalStore[DotFun[ExampleData]]]
class DottedCausalStoreChecks extends LatticePropertyChecks[Dotted[CausalStore[DotFun[ExampleData]]]]
class CausalQueueChecks extends LatticePropertyChecks[Dotted[CausalQueue[ExampleData]]]
class DotSetChecks extends LatticePropertyChecks[Dotted[DotSet]]
class DotSetChecks extends LatticePropertyChecks[Dotted[Dots]]
class EnableWinsFlagChecks extends LatticePropertyChecks[Dotted[contextual.EnableWinsFlag]]
class DotFunChecks extends LatticePropertyChecks[Dotted[DotFun[Int]]]
class DotFunExampleChecks extends LatticePropertyChecks[Dotted[DotFun[ExampleData]]]
class ConMultiVersionChecks extends LatticePropertyChecks[Dotted[contextual.MultiVersionRegister[Int]]]
class DotMapChecks extends LatticePropertyChecks[Dotted[Map[kofre.base.Uid, DotSet]]](expensive = true)
class DotMapChecks extends LatticePropertyChecks[Dotted[Map[kofre.base.Uid, Dots]]](expensive = true)
class GrowOnlyCounterChecks extends LatticePropertyChecks[GrowOnlyCounter]
class GrowOnlyMapChecks extends LatticePropertyChecks[GrowOnlyMap[String, Int]]
class TwoPhaseSetChecks extends LatticePropertyChecks[TwoPhaseSet[Int]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class DecomposeManualTests extends munit.ScalaCheckSuite {
assertEquals(merged_diff_delta_2, None, "delta_2 should be contained in merged")

val decomposed: Seq[Dotted[EnableWinsFlag]] =
Lattice[Dotted[EnableWinsFlag]].decompose(merged).toSeq.sortBy(_.data.inner.repr.internal.keys.headOption)
Lattice[Dotted[EnableWinsFlag]].decompose(merged).toSeq.sortBy(_.data.inner.internal.keys.headOption)
// EnableWinsFlag does not decompose, only returns the value.
// Dotted decomposes context and value. As context is completely covered by EnableWinsFlag, no additional entry for context.
assertEquals(decomposed.size, 1)
Expand Down Expand Up @@ -159,7 +159,7 @@ class DecomposeManualTests extends munit.ScalaCheckSuite {
assertEquals(val_3.data.read, true)

val decomposed: Seq[Dotted[EnableWinsFlag]] =
Lattice[Dotted[EnableWinsFlag]].decompose(val_3).toSeq.sortBy(_.data.inner.repr.internal.keys.headOption)
Lattice[Dotted[EnableWinsFlag]].decompose(val_3).toSeq.sortBy(_.data.inner.internal.keys.headOption)
// Dotted decomposes context and value - one entry for EnableWinsFlag with their Dot and one entry with remaining context
assertEquals(decomposed.size, 2)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package test.kofre.corestructs

import kofre.base.Lattice
import kofre.dotted.{DotFun, DotSet, Dotted}
import kofre.dotted.{DotFun, Dotted}
import kofre.time.{ArrayRanges, Dot, Dots}
import org.scalacheck.Prop.*
import org.scalacheck.{Arbitrary, Gen}
Expand All @@ -12,7 +12,7 @@ import scala.annotation.tailrec

class DotMapTest extends munit.ScalaCheckSuite {

type TestedMap = Map[Int, DotSet]
type TestedMap = Map[Int, Dots]

property("dots") {
forAll { (dm: TestedMap) =>
Expand Down Expand Up @@ -73,8 +73,8 @@ class DotMapTest extends munit.ScalaCheckSuite {
if (dotsA.intersect(dotsB).isEmpty) {
(dmA.keySet union dmB.keySet).foreach { k =>
val vMerged =
Dotted(dmA.getOrElse(k, DotSet.empty), (ccA)) merge
Dotted(dmB.getOrElse(k, DotSet.empty), (ccB))
Dotted(dmA.getOrElse(k, Dots.empty), (ccA)) merge
Dotted(dmB.getOrElse(k, Dots.empty), (ccB))

assert(
vMerged.data.isEmpty || dmMerged(k) == vMerged.data,
Expand Down Expand Up @@ -124,13 +124,13 @@ class DotMapTest extends munit.ScalaCheckSuite {

@tailrec
private def removeDuplicates(
start: List[(Int, DotSet)],
start: List[(Int, Dots)],
acc: TestedMap,
con: Dots
): TestedMap =
start match
case Nil => acc
case (i, c) :: t => removeDuplicates(t, acc.updated(i, DotSet(c.repr.subtract(con))), con union c.dots)
case (i, c) :: t => removeDuplicates(t, acc.updated(i, c.subtract(con)), con union c.dots)

property("decompose") {
forAll { (dmdup: TestedMap, deleted: Dots) =>
Expand All @@ -142,7 +142,7 @@ class DotMapTest extends munit.ScalaCheckSuite {
val decomposed: Iterable[Dotted[TestedMap]] =
Lattice[Dotted[TestedMap]].decompose(Dotted(dm, (cc)))
val wc: Dotted[TestedMap] =
decomposed.foldLeft(Dotted(Map.empty[Int, DotSet], Dots.empty)) {
decomposed.foldLeft(Dotted(Map.empty[Int, Dots], Dots.empty)) {
case (Dotted(dmA, ccA), Dotted(dmB, ccB)) =>
Lattice[Dotted[TestedMap]].merge(Dotted(dmA, ccA), Dotted(dmB, ccB))
}
Expand All @@ -158,7 +158,7 @@ class DotMapTest extends munit.ScalaCheckSuite {
dm.keys.foreach { k =>
assertEquals(
dm(k),
dmMerged.getOrElse(k, DotSet.empty),
dmMerged.getOrElse(k, Dots.empty),
s"Merging the list of atoms returned by DotMap.decompose should produce an equal Causal Context, but on key $k the $ccMerged does not equal $cc"
)
}
Expand Down
Loading

0 comments on commit b089e46

Please sign in to comment.