module CRDT.Cv.GCounter
    ( GCounter (..)
    , initial
    , query
    -- * Operation
    , increment
    ) where

import           Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IntMap
import           Data.Semilattice (Semilattice)

-- | Grow-only counter.
newtype GCounter a = GCounter (IntMap a)
    deriving (GCounter a -> GCounter a -> Bool
(GCounter a -> GCounter a -> Bool)
-> (GCounter a -> GCounter a -> Bool) -> Eq (GCounter a)
forall a. Eq a => GCounter a -> GCounter a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GCounter a -> GCounter a -> Bool
$c/= :: forall a. Eq a => GCounter a -> GCounter a -> Bool
== :: GCounter a -> GCounter a -> Bool
$c== :: forall a. Eq a => GCounter a -> GCounter a -> Bool
Eq, Int -> GCounter a -> ShowS
[GCounter a] -> ShowS
GCounter a -> String
(Int -> GCounter a -> ShowS)
-> (GCounter a -> String)
-> ([GCounter a] -> ShowS)
-> Show (GCounter a)
forall a. Show a => Int -> GCounter a -> ShowS
forall a. Show a => [GCounter a] -> ShowS
forall a. Show a => GCounter a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GCounter a] -> ShowS
$cshowList :: forall a. Show a => [GCounter a] -> ShowS
show :: GCounter a -> String
$cshow :: forall a. Show a => GCounter a -> String
showsPrec :: Int -> GCounter a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> GCounter a -> ShowS
Show)

instance Ord a => Semigroup (GCounter a) where
    GCounter IntMap a
x <> :: GCounter a -> GCounter a -> GCounter a
<> GCounter IntMap a
y = IntMap a -> GCounter a
forall a. IntMap a -> GCounter a
GCounter (IntMap a -> GCounter a) -> IntMap a -> GCounter a
forall a b. (a -> b) -> a -> b
$ (a -> a -> a) -> IntMap a -> IntMap a -> IntMap a
forall a. (a -> a -> a) -> IntMap a -> IntMap a -> IntMap a
IntMap.unionWith a -> a -> a
forall a. Ord a => a -> a -> a
max IntMap a
x IntMap a
y

-- | See 'CvRDT'
instance Ord a => Semilattice (GCounter a)

-- | Increment counter
increment
    :: Num a
    => Word -- ^ replica id
    -> GCounter a
    -> GCounter a
increment :: Word -> GCounter a -> GCounter a
increment Word
replicaId (GCounter IntMap a
imap) = IntMap a -> GCounter a
forall a. IntMap a -> GCounter a
GCounter ((a -> a -> a) -> Int -> a -> IntMap a -> IntMap a
forall a. (a -> a -> a) -> Int -> a -> IntMap a -> IntMap a
IntMap.insertWith a -> a -> a
forall a. Num a => a -> a -> a
(+) Int
i a
1 IntMap a
imap)
  where
    i :: Int
i = Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
replicaId

-- | Initial state
initial :: GCounter a
initial :: GCounter a
initial = IntMap a -> GCounter a
forall a. IntMap a -> GCounter a
GCounter IntMap a
forall a. IntMap a
IntMap.empty

-- | Get value from the state
query :: Num a => GCounter a -> a
query :: GCounter a -> a
query (GCounter IntMap a
v) = IntMap a -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum IntMap a
v