{-# LANGUAGE DisambiguateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-} {-| Hoon's `map` and `set` types and conversions to/from Nouns. -} module Urbit.Noun.Tree ( HoonSet, setToHoonSet, setFromHoonSet , HoonMap, mapToHoonMap, mapFromHoonMap , mug ) where import ClassyPrelude import Control.Lens hiding (non) import Urbit.Atom import Urbit.Noun.Conversions () import Urbit.Noun.Convert import Urbit.Noun.Core import Urbit.Noun.TH import Data.Bits (shiftR, xor) import Data.Hash.Murmur (murmur3) import GHC.Natural (Natural) -- Types ----------------------------------------------------------------------- data NounVal a = NounVal { non ∷ Noun , val ∷ !a } data HoonTreeNode a = NTN { n ∷ NounVal a , l ∷ HoonTree a , r ∷ HoonTree a } deriving (Eq, Ord, Show) data HoonTree a = E | Node (HoonTreeNode a) deriving (Eq, Ord, Show) pattern N n l r = Node (NTN n l r) newtype HoonSet a = HoonSet { unHoonSet ∷ HoonTree a } deriving newtype (Eq, Ord, Show, FromNoun, ToNoun) newtype HoonMap k v = HoonMap { unHoonMap ∷ HoonTree (k, v) } deriving newtype (Eq, Ord, Show, FromNoun, ToNoun) -- Instances ------------------------------------------------------------------- instance Eq (NounVal a) where (==) = on (==) non instance Ord (NounVal a) where compare = comparing non instance ToNoun (NounVal a) where toNoun = non instance Show a ⇒ Show (NounVal a) where show = show . val instance FromNoun a ⇒ FromNoun (NounVal a) where parseNoun x = NounVal x <$> parseNoun x instance ToNoun a ⇒ ToNoun (HoonTree a) where toNoun E = A 0 toNoun (Node n) = toNoun n instance FromNoun a ⇒ FromNoun (HoonTree a) where parseNoun (A 0) = pure E parseNoun n = Node <$> parseNoun n deriveNoun ''HoonTreeNode -- Mug ------------------------------------------------------------------------- type Nat = Natural slowMug ∷ Noun → Nat slowMug = trim 0xcafe_babe . \case A a → a C h t → mix (slowMug h) $ mix 0x7fff_ffff (slowMug t) where trim ∷ Nat → Nat → Nat trim syd key = if 0/=ham then ham else trim (succ syd) key where haz = muk syd (met 3 key) key ham = mix (rsh 0 31 haz) (end 0 31 haz) mix ∷ Nat → Nat → Nat mix = xor -- Murmur3 muk ∷ Nat → Nat → Nat → Nat muk seed len = fromIntegral . murmur3 (word32 seed) . resize . atomBytes where resize ∷ ByteString → ByteString resize buf = case compare (length buf) (int len) of EQ → buf LT → error "bad-muk" GT → error "bad-muk" -- LT → buf <> replicate (len - length buf) 0 -- GT → take len buf int ∷ Integral i ⇒ i → Int int = fromIntegral word32 ∷ Integral i ⇒ i → Word32 word32 = fromIntegral bex ∷ Nat → Nat bex = (2^) end ∷ Nat → Nat → Nat → Nat end blockSize blocks n = n `mod` (bex (bex blockSize * blocks)) rsh ∷ Nat → Nat → Nat → Nat rsh blockSize blocks n = shiftR n $ fromIntegral $ (bex blockSize * blocks) met ∷ Nat → Nat → Nat met bloq = go 0 where go c 0 = c go c n = go (succ c) (rsh bloq 1 n) -- XX TODO mug ∷ Noun → Nat mug = slowMug -- Order ----------------------------------------------------------------------- {- Orders in ascending double mug hash order, collisions fall back to dor. -} mor ∷ Noun → Noun → Bool mor a b = if c == d then dor a b else c < d where c = mug $ A $ mug a d = mug $ A $ mug b {- Orders in ascending tree depth. -} dor ∷ Noun → Noun → Bool dor a b | a == b = True dor (A a) (C _ _) = True dor (C x y) (A b) = False dor (A a) (A b) = a < b dor (C x y) (C p q) | x == p = dor y q dor (C x y) (C p q) = dor x p {- Orders in ascending +mug hash order. Collisions fall back to dor. -} gor ∷ Noun → Noun → Bool gor a b = if c==d then dor a b else c (b, c)) = \case E → N kv E E Node a | kv == n a → Node a Node a | b == p (n a) → N kv (l a) (r a) Node a | gorVal b (p $ n a) → lef kv a Node a → rit kv a lef kv@(pq -> (b, c)) a = put kv (l a) & \case E → error "bad-put-map" Node d | morVal (p $ n a) (p $ n d) → N (n a) (Node d) (r a) Node d → N (n d) (l d) (N (n a) (r d) (r a)) rit kv@(pq -> (b, c)) a = put kv (r a) & \case E → error "bad-put-map" Node d | morVal (p $ n a) (p $ n d) → N (n a) (l a) (Node d) Node d → N (n d) (N (n a) (l a) (l d)) (r d)