shrub/pkg/hair/lib/Data/Noun/Jam.hs

219 lines
7.4 KiB
Haskell
Raw Normal View History

module Data.Noun.Jam where
import ClassyPrelude
import Data.Noun
2019-05-10 05:45:28 +03:00
import Data.Noun.Atom
import Data.Noun.Poet
import Data.Bits
import Control.Lens
2019-05-15 04:30:44 +03:00
import Text.Printf
import Data.Map (Map)
import Control.Monad (guard)
import Test.Tasty
import Test.Tasty.TH
import Test.Tasty.QuickCheck as QC
import Test.QuickCheck
2019-05-15 04:30:44 +03:00
-- Length-Encoded Atoms --------------------------------------------------------
2019-05-20 06:20:03 +03:00
bex :: (Num a, Bits a) => Int -> a
bex = shiftL 1
mat' :: Atom -> Buf
mat' 0 = Buf 1 1
mat' atm = Buf bufWid buffer
where
atmWid = bitWidth atm
preWid = bitWidth (toAtom atmWid)
bufWid = preWid + preWid + atmWid - 1
prefix = bex preWid
extras = takeBits (preWid-1) (toAtom atmWid)
suffix = xor extras (shiftL (takeBits (atmWid-1) atm) (preWid-1))
buffer = bitConcat prefix suffix
rub' :: Cursor -> Maybe Buf
rub' slc@(Cursor idx buf) =
leadingZeros slc >>= \case
0 -> pure (Buf 1 0)
prefix -> pure (Buf sz val)
where
widIdx = idx + 1 + prefix
width = fromSlice (Slice widIdx (prefix - 1) buf)
datIdx = widIdx + (prefix-1)
datWid = fromIntegral (2^(prefix-1) + width) - 1
sz = datWid + (2*prefix)
val = bex datWid .|. fromSlice (Slice datIdx datWid buf)
jam' :: Noun -> Atom
jam' = toAtom . fst . go 0 mempty
where
insertNoun :: Noun -> Int -> Map Noun Int -> Map Noun Int
insertNoun n i tbl = lookup n tbl
& maybe tbl (const $ insertMap n i tbl)
go :: Int -> Map Noun Int -> Noun -> (Buf, Map Noun Int)
go off oldTbl noun =
let tbl = insertNoun noun off oldTbl in
case (lookup noun oldTbl, noun) of
(Just ref, Atom atm) | bitWidth atm <= bitWidth (toAtom ref) ->
(Buf (1+sz) (shiftL res 1), tbl)
where Buf sz res = mat' atm
(Just ref, _) ->
(Buf (2+sz) (xor 3 (shiftL res 2)), tbl)
where Buf sz res = mat' (toAtom ref)
(Nothing, Atom atm) ->
(Buf (1+sz) (shiftL res 1), tbl)
where Buf sz res = mat' atm
(Nothing, Cell lef rit) ->
(Buf (2+lSz+rSz) (xor 1 (shiftL (lRes .|. shiftL rRes lSz) 2)), rTbl)
where (Buf lSz lRes, lTbl) = go (off+2) tbl lef
(Buf rSz rRes, rTbl) = go (off+lSz) lTbl rit
cue' :: Atom -> Maybe Noun
cue' buf = view _2 <$> go mempty 0
where
go :: Map Int Noun -> Int -> Maybe (Int, Noun, Map Int Noun)
go tbl i =
case (bitIdx i buf, bitIdx (i+1) buf) of
(False, _ ) -> do Buf wid at <- rub' (Cursor (i+1) buf)
let r = toNoun at
pure (wid+1, r, insertMap i r tbl)
(True, False ) -> do (lSz,lef,tbl) <- go tbl (i+2)
(rSz,rit,tbl) <- go tbl (i+2+fromIntegral lSz)
let r = Cell lef rit
pure (2+lSz+rSz, r, insertMap i r tbl)
(True, True ) -> do Buf wid at <- rub' (Cursor (i+2) buf)
r <- lookup (fromIntegral at) tbl & \case
Nothing -> error ("bad-ref-" <> show at)
Just ix -> Just ix
pure (2+wid, r, tbl)
--------------------------------------------------------------------------------
2019-05-15 08:09:53 +03:00
mat :: Atom -> Buf
mat 0 = Buf 1 1
mat atm = Buf bufWid buffer
2019-05-15 04:30:44 +03:00
where
atmWid = bitWidth atm
preWid = bitWidth (toAtom atmWid)
bufWid = preWid + preWid + atmWid
prefix = shiftL 1 preWid
2019-05-15 08:09:53 +03:00
extras = takeBits (preWid-1) (toAtom atmWid)
2019-05-15 04:30:44 +03:00
suffix = xor extras (shiftL atm (preWid-1))
buffer = bitConcat prefix suffix
bufVal Nothing = "<nil>"
bufVal (Just (Buf sz v)) = show v <> " [" <> show sz <> "]"
2019-05-15 08:09:53 +03:00
rub :: Cursor -> Maybe Buf
rub slc@(Cursor idx buf) =
leadingZeros slc >>= \case
0 -> pure (Buf 1 0)
prefix -> pure (Buf sz val)
where
widIdx = idx + 1 + prefix
width = fromSlice (Slice widIdx (prefix - 1) buf)
datIdx = widIdx + (prefix-1)
datWid = fromIntegral $ 2^(prefix-1) + width
sz = datWid + (2*prefix)
val = fromSlice (Slice datIdx datWid buf)
2019-05-15 04:30:44 +03:00
-- Noun Serialization ----------------------------------------------------------
-- bex can be implemented using
-- `mpz_mul_2exp(a_mp, a_mp, a);
jam :: Noun -> Atom
2019-05-15 08:09:53 +03:00
jam = toAtom . fst . go 0 mempty
where
2019-05-15 04:30:44 +03:00
insertNoun :: Noun -> Int -> Map Noun Int -> Map Noun Int
insertNoun n i tbl = lookup n tbl
& maybe tbl (const $ insertMap n i tbl)
2019-05-15 08:09:53 +03:00
go :: Int -> Map Noun Int -> Noun -> (Buf, Map Noun Int)
go off oldTbl noun =
let tbl = insertNoun noun off oldTbl in
2019-05-20 06:20:03 +03:00
case (lookup noun oldTbl, noun) of
2019-05-15 08:09:53 +03:00
(Just ref, Atom atm) | bitWidth atm <= bitWidth (toAtom ref) ->
(Buf (1+sz) (shiftL res 1), tbl)
where Buf sz res = mat atm
(Just ref, _) ->
(Buf (2+sz) (xor 3 (shiftL res 2)), tbl)
where Buf sz res = mat (toAtom ref)
(Nothing, Atom atm) ->
(Buf (1+sz) (shiftL res 1), tbl)
where Buf sz res = mat atm
(Nothing, Cell lef rit) ->
(Buf (2+lSz+rSz) (xor 1 (shiftL (bitConcat lRes rRes) 2)), rTbl)
where (Buf lSz lRes, lTbl) = go (off+2) tbl lef
(Buf rSz rRes, rTbl) = go (off+lSz) lTbl rit
2019-05-15 04:30:44 +03:00
2019-05-20 06:20:03 +03:00
2019-05-11 00:59:45 +03:00
leadingZeros :: Cursor -> Maybe Int
leadingZeros (Cursor idx buf) = go 0
where wid = bitWidth buf
2019-05-20 06:20:03 +03:00
go n = do () <- if (n < wid) then pure ()
else error "infinite-atom"
guard (n < wid)
if bitIdx (idx+n) buf then pure n else go (n+1)
cue :: Atom -> Maybe Noun
cue buf = view _2 <$> go mempty 0
where
2019-05-11 00:59:45 +03:00
go :: Map Int Noun -> Int -> Maybe (Int, Noun, Map Int Noun)
go tbl i =
-- trace ("go-" <> show i)
case (bitIdx i buf, bitIdx (i+1) buf) of
2019-05-15 08:09:53 +03:00
(False, _ ) -> do Buf wid at <- rub (Cursor (i+1) buf)
let r = toNoun at
pure (wid+1, r, insertMap i r tbl)
(True, False ) -> do (lSz,lef,tbl) <- go tbl (i+2)
2019-05-11 00:59:45 +03:00
(rSz,rit,tbl) <- go tbl (i+2+fromIntegral lSz)
let r = Cell lef rit
pure (2+lSz+rSz, r, insertMap i r tbl)
2019-05-15 08:09:53 +03:00
(True, True ) -> do Buf wid at <- rub (Cursor (i+2) buf)
-- traceM ("ref-" <> show at)
2019-05-20 06:20:03 +03:00
r <- lookup (fromIntegral at) tbl & \case
Nothing -> error ("bad-ref-" <> show at)
Just ix -> Just ix
pure (2+wid, r, tbl)
2019-05-11 00:59:45 +03:00
2019-05-15 04:30:44 +03:00
-- Tests -----------------------------------------------------------------------
pills :: [Atom]
pills = [ 0x2, 0xc, 0x48, 0x29, 0xc9, 0x299
2019-05-15 04:30:44 +03:00
, 0x3170_c7c1, 0x93_c7c1, 0xa_72e0, 0x1bd5_b7dd_e080
]
2019-05-11 00:59:45 +03:00
cueTest :: Maybe [Noun]
cueTest = traverse cue pills
jamTest :: Maybe [Atom]
jamTest = fmap jam <$> cueTest
2019-05-15 04:30:44 +03:00
prop_jamCue :: Noun -> Bool
prop_jamCue n = Just n == cue (jam n)
prop_matRub :: Atom -> Bool
prop_matRub atm = matSz==rubSz && rubRes==atm
where
2019-05-15 08:09:53 +03:00
Buf matSz matBuf = mat atm
Buf rubSz rubRes = fromMaybe mempty (rub $ Cursor 0 matBuf)
2019-05-20 06:20:03 +03:00
prop_jamCue' :: Noun -> Bool
prop_jamCue' n = Just n == cue' (jam' n)
prop_matRub' :: Atom -> Bool
prop_matRub' atm = matSz==rubSz && rubRes==atm
where
Buf matSz matBuf = mat' atm
Buf rubSz rubRes = fromMaybe mempty (rub' $ Cursor 0 matBuf)
main :: IO ()
main = $(defaultMainGenerator)