diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/compiler/Makefile b/compiler/Makefile index 216554ec..47df99ca 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -1,9 +1,20 @@ .PHONY: test -all: - stack -v build $(STACK_OPTS) +all: build install + +build: VERBOSITY_FLAG = +build: + stack $(VERBOSITY_FLAG) build $(STACK_OPTS) +build/verbose: + $(MAKE) $(MAKE_FLAGS) build VERBOSITY_FLAG="-v" + +install: VERBOSITY_FLAG = +install: + $(MAKE) $(MAKE_FLAGS) build mkdir -p ./../bin - stack -v install $(STACK_OPTS) --local-bin-path ./../bin/ + stack $(VERBOSITY_FLAG) install $(STACK_OPTS) --local-bin-path ./../bin/ +install/verbose: + $(MAKE) $(MAKE_FLAGS) install VERBOSITY_FLAG="-v" clean: rm *.cabal @@ -11,14 +22,14 @@ clean: rm -rf ../bin # If problems still persist after this, remove all GHC compilers in ~/.stack/programs/**/ -ghci-irtester: - stack ghci --main-is Troupe-compiler:exe:irtester --no-load - -ghci-troupec: - stack ghci --main-is Troupe-compiler:exe:troupec --no-load - test: stack test $(STACK_OPTS) parser-info: stack exec happy -- -i src/Parser.y + +ghci/irtester: + stack ghci --main-is Troupe-compiler:exe:irtester --no-load + +ghci/troupec: + stack ghci --main-is Troupe-compiler:exe:troupec --no-load diff --git a/compiler/app/Main.hs b/compiler/app/Main.hs index fd007e2b..400fa6f5 100644 --- a/compiler/app/Main.hs +++ b/compiler/app/Main.hs @@ -21,9 +21,8 @@ import qualified Raw2Stack import qualified Stack2JS import qualified RawOpt -- import System.IO (isEOF) -import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as BS import Data.ByteString.Base64 (decode) -import qualified Data.ByteString.Char8 as BSChar8 import qualified Data.ByteString.Lazy.Char8 as BSLazyChar8 import System.IO import System.Exit @@ -220,7 +219,7 @@ fromStdinIR = do input <- BS.getLine if BS.isPrefixOf "!ECHO " input then let response = BS.drop 6 input - in do BSChar8.putStrLn response + in do BS.putStrLn response -- debugOut "echo" else case decode input of @@ -244,7 +243,7 @@ fromStdinIRJson = do input <- BS.getLine if BS.isPrefixOf "!ECHO " input then let response = BS.drop 6 input - in BSChar8.putStrLn response + in BS.putStrLn response else case decode input of Right bs -> diff --git a/compiler/src/ClosureConv.hs b/compiler/src/ClosureConv.hs index d92d4024..4b212f1c 100644 --- a/compiler/src/ClosureConv.hs +++ b/compiler/src/ClosureConv.hs @@ -201,7 +201,7 @@ cpsToIR (CPS.LetSimple vname@(VN ident) st kt) = do cpsToIR (CPS.LetRet (CPS.Cont arg kt') kt) = do t <- cpsToIR kt t' <- local (insVar arg) (cpsToIR kt') - return $ CCIR.BB [] $ Call arg t t' + return $ CCIR.BB [] $ StackExpand arg t t' cpsToIR (CPS.LetFun fdefs kt) = do let vnames_orig = map (\(CPS.Fun fname _) -> fname) fdefs let localExt = local (insVars vnames_orig) diff --git a/compiler/src/IR.hs b/compiler/src/IR.hs index 8621c088..c4836153 100644 --- a/compiler/src/IR.hs +++ b/compiler/src/IR.hs @@ -91,7 +91,7 @@ data IRTerminator -- and then execute the second BB, which can refer to this variable and -- where PC is reset to the level before entering the first BB. -- Represents a "let x = ... in ..." format. - | Call VarName IRBBTree IRBBTree + | StackExpand VarName IRBBTree IRBBTree deriving (Eq,Show,Generic) @@ -147,7 +147,7 @@ instance ComputesDependencies IRBBTree where instance ComputesDependencies IRTerminator where dependencies (If _ bb1 bb2) = mapM_ dependencies [bb1, bb2] dependencies (AssertElseError _ bb1 _ _) = dependencies bb1 - dependencies (Call _ t1 t2) = dependencies t1 >> dependencies t2 + dependencies (StackExpand _ t1 t2) = dependencies t1 >> dependencies t2 dependencies _ = return () instance ComputesDependencies FunDef where @@ -231,15 +231,15 @@ instance WellFormedIRCheck IRInst where wfir (Assign (VN x) e) = do checkId x wfir e wfir (MkFunClosures _ fdefs) = mapM_ (\((VN x), _) -> checkId x) fdefs - + instance WellFormedIRCheck IRTerminator where wfir (If _ bb1 bb2) = do wfir bb1 wfir bb2 wfir (AssertElseError _ bb _ _) = wfir bb - wfir (Call (VN x) bb1 bb2 ) = do - checkId x + wfir (StackExpand (VN x) bb1 bb2 ) = do + checkId x wfir bb1 wfir bb2 @@ -442,7 +442,8 @@ ppIR (MkFunClosures varmap fdefs) = -ppTr (Call vn bb1 bb2) = (ppId vn <+> text "= call" $$ nest 2 (ppBB bb1)) $$ (ppBB bb2) + +ppTr (StackExpand vn bb1 bb2) = (ppId vn <+> text "= call" $$ nest 2 (ppBB bb1)) $$ (ppBB bb2) ppTr (AssertElseError va ir va2 _) diff --git a/compiler/src/IR2Raw.hs b/compiler/src/IR2Raw.hs index 7f663c17..6bc633c9 100644 --- a/compiler/src/IR2Raw.hs +++ b/compiler/src/IR2Raw.hs @@ -699,7 +699,7 @@ tr2raw = \case return $ If r bb1' bb2' -- Revision 2023-08: Equivalent, only way of modifying bb2 changed. - IR.Call v irBB1 irBB2 -> do + IR.StackExpand v irBB1 irBB2 -> do bb1 <- tree2raw irBB1 BB insts2 tr2 <- tree2raw irBB2 -- Prepend before insts2 instructions to store in variable v the result @@ -711,7 +711,7 @@ tr2raw = \case -- generally using Sequence (faster concatenation) for instructions -- might improve performance let bb2 = BB insts2' tr2 - return $ Call bb1 bb2 + return $ StackExpand bb1 bb2 -- Note: This is translated into branching and Error for throwing RT exception -- Revision 2023-08: More fine-grained raising of blocking label, see below. diff --git a/compiler/src/IROpt.hs b/compiler/src/IROpt.hs index 610c1f24..f0676ef2 100644 --- a/compiler/src/IROpt.hs +++ b/compiler/src/IROpt.hs @@ -67,7 +67,7 @@ instance Substitutable IRTerminator where AssertElseError (apply subst x) (apply subst bb) (apply subst y) pos LibExport x -> LibExport (apply subst x) Error x pos -> Error (apply subst x) pos - Call decVar bb1 bb2 -> Call decVar (apply subst bb1) (apply subst bb2) + StackExpand decVar bb1 bb2 -> StackExpand decVar (apply subst bb1) (apply subst bb2) instance Substitutable IRBBTree where apply subst (BB insts tr) = @@ -462,7 +462,7 @@ trPeval (AssertElseError x bb y_err pos) = do return $ BB [] (AssertElseError x bb' y_err pos) -trPeval (Call x bb1 bb2) = do +trPeval (StackExpand x bb1 bb2) = do bb1' <- peval bb1 bb2' <- peval bb2 @@ -473,7 +473,7 @@ trPeval (Call x bb1 bb2) = do setChangeFlag return $ BB (insts1 ++ insts2) tr2 _ -> - return $ BB [] (Call x bb1' bb2') + return $ BB [] (StackExpand x bb1' bb2') trPeval tr@(Ret x) = do markUsed' x diff --git a/compiler/src/Raw.hs b/compiler/src/Raw.hs index a9a17046..2f7a5ff9 100644 --- a/compiler/src/Raw.hs +++ b/compiler/src/Raw.hs @@ -158,7 +158,7 @@ data RawTerminator | Error RawVar PosInf -- | Execute the first BB and then execute the second BB where -- PC is reset to the level before entering the first BB. - | Call RawBBTree RawBBTree + | StackExpand RawBBTree RawBBTree deriving (Eq, Show) @@ -341,7 +341,7 @@ ppIR (MkFunClosures varmap fdefs) = -- ppIR (LevelOperations _ insts) = -- text "level operation" $$ nest 2 (vcat (map ppIR insts)) -ppTr (Call bb1 bb2) = (text "call" $$ nest 4 (ppBB bb1)) $$ (ppBB bb2) +ppTr (StackExpand bb1 bb2) = (text "call" $$ nest 4 (ppBB bb1)) $$ (ppBB bb2) -- ppTr (AssertElseError va ir va2 _) diff --git a/compiler/src/Raw2Stack.hs b/compiler/src/Raw2Stack.hs index caf87c3b..b4e892a7 100644 --- a/compiler/src/Raw2Stack.hs +++ b/compiler/src/Raw2Stack.hs @@ -188,7 +188,7 @@ trTr (Raw.LibExport v) = do return $ Stack.LibExport v trTr (Raw.Error r1 p) = do return $ Stack.Error r1 p -trTr (Raw.Call bb1 bb2) = do +trTr (Raw.StackExpand bb1 bb2) = do __callDepth <- localCallDepth <$> ask bb1' <- local (\tenv -> tenv { localCallDepth = __callDepth + 1 } ) $ trBB bb1 n <- getBlockNumber @@ -205,7 +205,7 @@ trTr (Raw.Call bb1 bb2) = do | x <- filter filterConsts (Set.elems varsToLoad) ] bb2'@(Stack.BB inst_2 tr_2) <- trBB bb2 - return $ Stack.Call bb1' (Stack.BB (loads ++ inst_2) tr_2) + return $ Stack.StackExpand bb1' (Stack.BB (loads ++ inst_2) tr_2) trBB :: Raw.RawBBTree -> Tr Stack.StackBBTree diff --git a/compiler/src/RawDefUse.hs b/compiler/src/RawDefUse.hs index c6b7314f..e987b917 100644 --- a/compiler/src/RawDefUse.hs +++ b/compiler/src/RawDefUse.hs @@ -233,7 +233,7 @@ instance Trav RawTerminator where trav bb2 LibExport v -> use v Error r _ -> use r - Call bb1 bb2 -> do + StackExpand bb1 bb2 -> do trav bb1 modify (\s -> let (c, _) = locInfo s diff --git a/compiler/src/RawOpt.hs b/compiler/src/RawOpt.hs index 937dc8be..e7253b77 100644 --- a/compiler/src/RawOpt.hs +++ b/compiler/src/RawOpt.hs @@ -78,7 +78,7 @@ instance Substitutable RawTerminator where If r bb1 bb2 -> If (apply subst r) (apply subst bb1) (apply subst bb2) Error r p -> Error (apply subst r) p - Call bb1 bb2 -> Call (apply subst bb1) (apply subst bb2) + StackExpand bb1 bb2 -> StackExpand (apply subst bb1) (apply subst bb2) _ -> tr instance Substitutable RawBBTree where @@ -420,7 +420,7 @@ instance PEval RawTerminator where } bb2' <- peval bb2 return $ If x bb1' bb2' - Call bb1 bb2 -> do + StackExpand bb1 bb2 -> do s <- get bb1' <- peval bb1 put $ s { stateMon = Map.empty @@ -428,7 +428,7 @@ instance PEval RawTerminator where , stateJoins = stateJoins s } -- reset the monitor state bb2' <- peval bb2 - return $ Call bb1' bb2' + return $ StackExpand bb1' bb2' Ret -> do return tr' TailCall x -> do @@ -470,14 +470,15 @@ filterInstBwd ls = f (Nothing, Nothing) (reverse ls) [] --- | This optimization for 'Call' moves instructions from the continuation to before the 'Call'. --- This can result in a 'Call' which just contains a 'Ret', which is then optimized away. --- The optimization compensates for redundant assignments introduced by the translation. -hoistCalls :: RawBBTree -> RawBBTree -hoistCalls bb@(BB insts tr) = +-- | This optimization for 'StackExpand' moves instructions from the continuation to before the +-- 'StackExpand'. This can result in a 'StackExpand' which just contains a 'Ret', which is then +-- optimized away. The optimization compensates for redundant assignments introduced by the +-- translation. +hoistStackExpand :: RawBBTree -> RawBBTree +hoistStackExpand bb@(BB insts tr) = case tr of -- Here we check which instructions from ii_1 can be moved to before the call - Call (BB ii_1 tr_1) bb2 -> + StackExpand (BB ii_1 tr_1) bb2 -> let isFrameSpecific i = case i of SetBranchFlag -> True @@ -487,7 +488,7 @@ hoistCalls bb@(BB insts tr) = -- jx_1: non-frame-specific instructions, are moved to before the call -- jx_2: frame-specific instructions, stay under the call's instructions (jx_1, jx_2) = Data.List.break isFrameSpecific ii_1 - in BB (insts ++ jx_1) (Call (BB jx_2 tr_1) bb2) + in BB (insts ++ jx_1) (StackExpand (BB jx_2 tr_1) bb2) -- If returning, the current frame will be removed, and thus all PC set instructions -- are redundant and can be removed. Ret -> @@ -537,7 +538,7 @@ instance PEval RawBBTree where If x (BB (set_pc_bl ++ i_then) tr_then) (BB (set_pc_bl ++ i_else) tr_else) - _ -> hoistCalls $ BB (insts_no_ret ++ set_pc_bl) tr'' + _ -> hoistStackExpand $ BB (insts_no_ret ++ set_pc_bl) tr'' let insts_sorted = instOrder insts_ return $ BB insts_sorted bb_ diff --git a/compiler/src/Stack.hs b/compiler/src/Stack.hs index 6427a452..91f3e4f9 100644 --- a/compiler/src/Stack.hs +++ b/compiler/src/Stack.hs @@ -47,7 +47,7 @@ data StackTerminator | If RawVar StackBBTree StackBBTree | LibExport VarAccess | Error RawVar PosInf - | Call StackBBTree StackBBTree + | StackExpand StackBBTree StackBBTree deriving (Eq, Show) @@ -150,7 +150,7 @@ ppIR (MkFunClosures varmap fdefs) = ppIR (LabelGroup insts) = text "group" $$ nest 2 (vcat (map ppIR insts)) -ppTr (Call bb1 bb2) = (text "= call" $$ nest 2 (ppBB bb1)) $$ (ppBB bb2) +ppTr (StackExpand bb1 bb2) = (text "= call" $$ nest 2 (ppBB bb1)) $$ (ppBB bb2) -- ppTr (AssertElseError va ir va2 _) diff --git a/compiler/src/Stack2JS.hs b/compiler/src/Stack2JS.hs index 5717b99f..0a11bedd 100644 --- a/compiler/src/Stack2JS.hs +++ b/compiler/src/Stack2JS.hs @@ -452,7 +452,7 @@ ir2js InvalidateSparseBit = return $ {-- TERMINATORS --} -tr2js (Call bb bb2) = do +tr2js (StackExpand bb bb2) = do _frameSize <- gets frameSize _sparseSlot <- gets sparseSlot _consts <- gets consts diff --git a/compiler/test/ir2raw-test/testcases/TR.hs b/compiler/test/ir2raw-test/testcases/TR.hs index 4800b478..f330a8e0 100644 --- a/compiler/test/ir2raw-test/testcases/TR.hs +++ b/compiler/test/ir2raw-test/testcases/TR.hs @@ -30,8 +30,8 @@ tcs = map (second mkP) (BB [Assign (VN "b1") (Base "v1") ] (LibExport (mkV "b1"))) (BB [Assign (VN "b2") (Base "v2") ] (LibExport (mkV "b2"))) ), - ( "Call" - , Call (VN "x") + ( "StackExpand" + , StackExpand (VN "x") (BB [Assign (VN "b1") (Base "v1") ] (LibExport (mkV "b1"))) (BB [Assign (VN "b2") (Base "v2") ] (LibExport (mkV "b2"))) ), diff --git a/lib/Hash.trp b/lib/Hash.trp index 5a4b0d90..f10ec7b0 100644 --- a/lib/Hash.trp +++ b/lib/Hash.trp @@ -68,15 +68,13 @@ let (*--- Module ---*) val Hash = { - hashString = hashString, - hashMultiplyShift = hashMultiplyShift, - hashInt = hashInt, - hashNumber = hashNumber, - hashList = hashList, - hash = hash + hashString, + hashMultiplyShift, + hashInt, + hashNumber, + hashList, + hash } -in [ ("Hash", Hash) - , ("hash", hash) - ] +in [ ("Hash", Hash), ("hash", hash) ] end diff --git a/lib/HashMap.trp b/lib/HashMap.trp index 43358544..a8e25072 100644 --- a/lib/HashMap.trp +++ b/lib/HashMap.trp @@ -202,24 +202,20 @@ let (* NOTE: The map is implemented as a Hash Array Mapped Trie (HAMT), i.e. a p (*--- Module ---*) val HashMap = { - (* Construction *) - empty = empty, - singleton = singleton, - insert = insert, - remove = remove, - (* Queries *) - null = null, - size = size, - findOpt = findOpt, - find = find, - mem = mem, - (* Manipulation *) - fold = fold, - (* List Conversion*) - keys = keys, - values = values, - toList = toList, - fromList = fromList + empty, + singleton, + insert, + remove, + null, + size, + findOpt, + find, + mem, + fold, + keys, + values, + toList, + fromList } in [ ("HashMap", HashMap) ] diff --git a/lib/HashSet.trp b/lib/HashSet.trp index 0ffccbc5..ccad42d0 100644 --- a/lib/HashSet.trp +++ b/lib/HashSet.trp @@ -47,21 +47,17 @@ let (* NOTE: The set is implemented as a HashMap with dummy values, `()`. This i (*--- Module ---*) val HashSet = { - (* Construction *) - empty = empty, - singleton = singleton, - insert = insert, - remove = remove, - (* Queries *) - null = null, - size = size, - mem = mem, - (* Manipulation *) - fold = fold, - (* List Conversion*) - elems = elems, - toList = toList, - fromList = fromList + empty, + singleton, + insert, + remove, + null, + size, + mem, + fold, + elems, + toList, + fromList } in [ ("HashSet", HashSet) ] diff --git a/lib/List.trp b/lib/List.trp index 872936e9..775007e3 100644 --- a/lib/List.trp +++ b/lib/List.trp @@ -169,33 +169,26 @@ let (* -- List Access -- *) (*--- Module ---*) val List = { - head = head, - tail = tail, - nth = nth, - - null = null, - elem = elem, - length = length, - - reverse = reverse, - append = append, - revAppend = revAppend, - appendAt = appendAt, - sublist = sublist, - - map = map, - mapi = mapi, - foldl = foldl, - filter = filter, - filteri = filteri, - partition = partition, - - range = range, - - sort = sort + head, + tail, + nth, + null, + elem, + length, + reverse, + append, + revAppend, + appendAt, + sublist, + map, + mapi, + foldl, + filter, + filteri, + partition, + range, + sort } -in [ ("List", List), - ("length", length) - ] +in [ ("List", List), ("length", length) ] end diff --git a/lib/ListPair.trp b/lib/ListPair.trp index 20d03ca6..94b54eed 100644 --- a/lib/ListPair.trp +++ b/lib/ListPair.trp @@ -64,22 +64,19 @@ let (* -- ListPair Generation -- *) (*--- Module ---*) val ListPair = { - zip = zip, - unzip = unzip, - - null = null, - length = length, - - reverse = reverse, - append = append, - revAppend = revAppend, - - findOpt = findOpt, - find = find, - mem = mem, - - map = map, - foldl = foldl + zip, + unzip, + null, + length, + reverse, + append, + revAppend, + findOpt, + find, + mem, + map, + foldl } -in [ ("ListPair", ListPair) ] end +in [ ("ListPair", ListPair) ] +end diff --git a/lib/Makefile b/lib/Makefile index e8942aca..6d7c73b0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,8 @@ build: $(COMPILER) ./String.trp -l $(COMPILER) ./Hash.trp -l $(COMPILER) ./Unit.trp -l + $(COMPILER) ./Map.trp -l + $(COMPILER) ./Set.trp -l $(COMPILER) ./StencilVector.trp -l $(COMPILER) ./HashMap.trp -l $(COMPILER) ./HashSet.trp -l @@ -21,7 +23,6 @@ build: $(COMPILER) ./stdio.trp -l $(COMPILER) ./raft.trp -l $(COMPILER) ./raft_debug.trp -l - $(COMPILER) ./bst.trp -l $(COMPILER) ./localregistry.trp -l clean: diff --git a/lib/Map.trp b/lib/Map.trp new file mode 100644 index 00000000..224076ec --- /dev/null +++ b/lib/Map.trp @@ -0,0 +1,193 @@ +import Number +import List + +let (* TODO: The map is implemented as an unbalanced binary search tree. This opens up the + * possibility for an adversary to induce a denial of service by choosing a bad + * order of insertion. + *) + + (*--- Construction ---*) + + (** Returns an empty map using the comparator, `cmp`. *) + fun empty cmp = (cmp, ()) + + (** Returns a map using the comparator function, `cmp` with the single `key`-`value` pair. *) + fun singleton cmp key value = (cmp, ((), (key , value), ())) + + (** Returns the map with the given `key`-`value` pair inserted. *) + fun insert (cmp,t) key value = + let fun insertRec () = ((), (key, value), ()) + | insertRec (l, (k,v), r) = + if key = k + then (l, (k, value), r) + else if cmp key k then (insertRec l, (k,v), r) else (l, (k,v), insertRec r) + in (cmp, insertRec t) end + + (*--- Queries ---*) + + (** Returns `true` if the map is empty. *) + fun null (_, ()) = true + | null _ = false + + (** Returns the `[value]` associated with the `key` if it exists; otherwise, `[]`. *) + (* HACK (#53): Use option type instead of list. *) + fun findOpt (cmp,t) key = + let fun findRec () = [] + | findRec (l, (k,v), r) = if key = k then [v] + else if cmp key k then findRec l else findRec r + in findRec t end + + (** Returns the value associated with the `key`. *) + fun find m key = case findOpt m key of [v] => v + + (** Returns `true` if the given key is associated with a value. *) + fun mem m key = case findOpt m key of [] => false + | _ => true + + (** Number of key-value pairs stored in the given map. *) + (* TODO (Optimisation): Store size at root to make this O(1)? *) + fun size (_,t) = + let fun sizeRec () = 0 + | sizeRec (l, (_,_), r) = (sizeRec l) + 1 + (sizeRec r) + in sizeRec t end + + (** Depth of the tree. *) + fun depth (_,t) = + let fun depthRec () = 0 + | depthRec (l, (_,_), r) = 1 + Number.max ((depthRec l), (depthRec r)) + in depthRec t end + + (*--- Priority Queue ---*) + + (** The smallest `[(key,value)]` if it exists; otherwise, `[]`. *) + fun minOpt (_,t) = + let fun minRec () = [] + | minRec ((), (k,v), _) = [(k,v)] + | minRec (l, (_,_), _) = minRec l + in minRec t + end + + (** The smallest `(key,value)`. *) + fun min m = case minOpt m of [kv] => kv + + (** The smallest `[(key,value)]` (if any) together with the tree updated to not include said + * value. If the tree is empty, then `[]` is returned. *) + fun extractMinOpt (cmp, t) = + let fun extractMinRec () = ([], ()) + | extractMinRec ((), (k,v), r) = ([(k,v)], r) + | extractMinRec (l, (k,v), r) = let val (kv', l') = extractMinRec l + in (kv', (l', (k,v), r)) end + + val (kv', t') = extractMinRec t + in (kv', (cmp, t')) end + + (** The smallest `(key,value)` together with the tree updated to not include said value. *) + fun extractMin m = case extractMinOpt m of ([kv], m') => (kv, m') + + (*--- Destruction ---*) + + (** Returns the map without the given `key` and its value removed. *) + fun remove (cmp,t) key = + let fun removeRec () = () + | removeRec (l, (k,v), ()) = if key = k then l else (l, (k,v), ()) + | removeRec ((), (k,v), r) = if key = k then r else ((), (k,v), r) + | removeRec (l, (k,v), r) = + if key = k + then let val ((k',v'), (_,r')) = extractMin (cmp, r) + in (l, (k',v'), r') end + else if cmp key k then (removeRec l, (k,v), r) else (l, (k,v), removeRec r) + in (cmp, removeRec t) end + + (*--- Manipulation ---*) + + (** Fold function `f` over all key-value pairs in the map in order of the keys. + * Not tail-recursive. *) + fun foldl f y (_,t) = + let fun foldlRec y' () = y' + | foldlRec y' (l, (k,v), r) = foldlRec (f ((k,v), foldlRec y' l)) r + in foldlRec y t end + + (** Fold function `f` over all key-value pairs in the map in reverse order of the keys. + * Not tail-recursive. *) + fun foldr f y (_,t) = + let fun foldrRec y' () = y' + | foldrRec y' (l, (k,v), r) = foldrRec (f ((k,v), foldrRec y' r)) l + in foldrRec y t end + + (** Map function `f` onto all key-value pairs in the map. The new map uses the comparator `cmp`. + * Not tail-recursive. *) + fun map cmp f m = + let fun f' (kv, m') = let val (k', v') = f kv + in insert m' k' v' end + in foldl f' (empty cmp) m end + + (** Filters key-value pairs in the given map. Not tail-recursive. *) + fun filter f (cmp, t) = + let fun filterRec () = () + | filterRec (l, kv, ()) = if f kv then (filterRec l, kv, ()) else filterRec l + | filterRec ((), kv, r ) = if f kv then ((), kv, filterRec r) else filterRec r + | filterRec (l, kv, r ) = + if f kv + then (filterRec l, kv, filterRec r) + else case filterRec r of () => filterRec l + | r' => let val (kv', (_, r'')) = extractMin (cmp, r') + in (filterRec l, kv', r'') end + in (cmp, filterRec t) end + + (* TODO: merge / union, split. *) + + (*--- Comparison ---*) + + (* TODO *) + + (*--- List Conversion ---*) + + (** A list of all keys stored in the given map in ascending order. *) + fun keys m = foldr (fn ((k,_), acc) => k::acc) [] m + + (** A list of all keys stored in the given map. *) + fun values m = foldr (fn ((_,v), acc) => v::acc) [] m + + (** A list of key-value pairs stored in the given map. *) + fun toDescList m = foldl (fn ((k,v), acc) => (k,v)::acc) [] m + + (** A list of key-value pairs stored in the given map. *) + fun toAscList m = foldr (fn ((k,v), acc) => (k,v)::acc) [] m + + (** Alias for `toAscList`. *) + val toList = toAscList + + (** The map (using the provided comparator function) that contains all key-value pairs in the + * given list. Tail-recursive. *) + fun fromList cmp xs = List.foldl (fn ((k,v), m) => insert m k v) (empty cmp) xs + + (*--- Module ---*) + val Map = { + empty, + singleton, + insert, + null, + size, + depth, + findOpt, + find, + mem, + minOpt, + min, + extractMinOpt, + extractMin, + remove, + foldl, + foldr, + map, + filter, + keys, + values, + toDescList, + toAscList, + toList, + fromList + } + +in [ ("Map", Map) ] +end diff --git a/lib/Number.trp b/lib/Number.trp index ad9b7527..6ea67ada 100644 --- a/lib/Number.trp +++ b/lib/Number.trp @@ -1,7 +1,9 @@ (** In Troupe, there is not per-se an `int` type. Rather, there is a `number` and some integral * operations on them which treats them as if they were 32 bit signed integers. *) -let (** Largest (safe) possible integral value. Anything larger than this cannot represent an +let (*--- Constants ---*) + + (** Largest (safe) possible integral value. Anything larger than this cannot represent an * increment of 1. * * NOTE: Value copied from the JavaScript documentation for `Number.MAX_SAFE_INTEGER`. *) @@ -34,6 +36,8 @@ let (** Largest (safe) possible integral value. Anything larger than this cannot * - inf *) + (*--- Operations ---*) + (** Returns the absolute value of x *) fun abs x = if x < 0 then -x else x @@ -69,6 +73,8 @@ let (** Largest (safe) possible integral value. Anything larger than this cannot *) val sqrt = sqrt + (*--- Integer ---*) + (** Returns `x` with the fractional parts removed. *) fun isInt x = floor x = x @@ -79,6 +85,25 @@ let (** Largest (safe) possible integral value. Anything larger than this cannot * are capped. *) fun toInt32 x = (max (min (maxInt32, x), minInt32)) orb 0 + (*--- Comparators ---*) + + (** Returns `true` if `x` is smaller than `y`. *) + fun eq x y = x = y + + (** Returns `true` if `x` is smaller than `y`. *) + fun lt x y = x < y + + (** Returns `true` if `x` is smaller than or equal to `y`. *) + fun le x y = x <= y + + (** Returns `true` if `x` is greater than `y`. *) + fun gt x y = x > y + + (** Returns `true` if `x` is greater than or equal to `y`. *) + fun ge x y = x >= y + + (*--- String Formatting ---*) + (** Returns the string representing the given `number`. * * TODO: Move out of the TCB? @@ -93,25 +118,31 @@ let (** Largest (safe) possible integral value. Anything larger than this cannot (*--- Module ---*) val Number = { - maxInt = maxInt, - minInt = minInt, - precision = precision, - maxInt32 = maxInt32, - minInt32 = minInt32, - maxNum = maxNum, - minNum = minNum, - abs = abs, - min = min, - max = max, - ceil = ceil, - floor = floor, - round = round, - sqrt = sqrt, - isInt = isInt, - toInt = toInt, - toInt32 = toInt32, - toString = toString, - fromString = fromString + maxInt, + minInt, + precision, + maxInt32, + minInt32, + maxNum, + minNum, + abs, + min, + max, + ceil, + floor, + round, + sqrt, + isInt, + toInt, + toInt32, + eq, + lt, + le, + gt, + ge, + toString, + fromString } + in [("Number", Number)] end diff --git a/lib/README.md b/lib/README.md index ea43f188..44119947 100644 --- a/lib/README.md +++ b/lib/README.md @@ -21,13 +21,19 @@ reviewed rigorously rather than depend on the monitor. To compile a module as part of the standard library, add it to the list of files in the `lib` target of the *makefile*. +## Design Principles + +- File names are written in `CamelCase`. This makes them conform to the Standard ML Basis Library. +- It is more important to match the function names and signatures in the Standard ML library than to + improve on them. For example, `String.sub` would make more sense with the type `[Char] -> Int -> + Char` but to match the SML library, we will stick with `[Char] * Int -> Char`. +- Each module exports a single *record* with the same name as the file. This (1) makes it closer to + the SML module system and (2) allows for name resolution, e.g. `HashMap.findOpt` and + `ListPair.findOpt` can be used in the same file. +- Each function that is exported has to be documented (`(** *)`). In the long run, we will + auto-generate documentation for the Standard Library. + ## TODO -- To conform with the Standard ML Basis Library, we should have the files conform to a `CamelCase` - style. -- To fake namespaced import, e.g. `List.length`, the library should export a struct instead. Only - certain functions should "pollute" the global namespace. -- Quite a lot of the standard library is not documented in any way. What is the purpose of each - function and each module? The [modules](#modules) above are the ones that have been updated and - documented. -- There are a lot of things in here - some of it dead. Can we merge/remove some things? +The [modules](#modules) mentioned above already follow the [design principles](#design-principles). +The remaining files either need to be updated or to be removed. diff --git a/lib/Set.trp b/lib/Set.trp new file mode 100644 index 00000000..7cda30b5 --- /dev/null +++ b/lib/Set.trp @@ -0,0 +1,118 @@ +import List +import Map + +let (*--- Set Construction ---*) + + (** Returns an empty set using the comparator, `cmp`. *) + val empty = Map.empty + + (** Returns a set using the comparator function, `cmp` with the single value `x` pair. *) + fun singleton cmp x = Map.singleton cmp x () + + (** Returns the set with the given value, `x`, inserted. *) + fun insert s x = Map.insert s x () + + (*--- Set Queries ---*) + + (** Returns `true` if the set is empty. *) + val null = Map.null + + (** Returns `true` if the value `x` is in the set. *) + val mem = Map.mem + + (** Number of elements stored in the set. *) + val size = Map.size + + (** Depth of the tree representation of the set. *) + val depth = Map.depth + + (*--- Priority Queue ---*) + + (** The smallest element in the set, if it exists. Otherwise, `[]`. *) + fun minOpt s = case Map.minOpt s of [] => [] + | [(k,_)] => [k] + + (** The smallest element in the non-empty set. *) + fun min s = case minOpt s of [k] => k + + (** The smallest element (if any) together with an updated set that does not include it. If the + * tree is empty, then `[]` is returned. *) + fun extractMinOpt s = case Map.extractMinOpt s of ([], m') => ([], m') + | ([(k,_)], m') => ([k], m') + + (** The smallest element together with an updated set that does not include said value. *) + fun extractMin s = case extractMinOpt s of ([k], m') => (k, m') + + (*--- Set Destruction ---*) + + (** Returns the set with the given value `x` removed. *) + fun remove s x = Map.remove s x + + (*--- Manipulation ---*) + + (** Fold function `f` over all elements in the set in-order. Not tail-recursive. *) + fun foldl f y s = + let fun f' ((k,_), y') = f (k,y') + in Map.foldl f' y s end + + (** Fold function `f` over all elements in the set in reverse order. Not tail-recursive. *) + fun foldr f y s = + let fun f' ((k,_), y') = f (k,y') + in Map.foldr f' y s end + + (** Map function `f` onto all elements in the set. The new set uses the comparator `cmp`. + * Not tail-recursive. *) + fun map cmp f s = + let fun f' (k,_) = (f k, ()) + in Map.map cmp f' s end + + (** Filters key-value pairs in the given map. Not tail-recursive. *) + fun filter f s = + let fun f' (k,_) = f k + in Map.filter f' s end + + (*--- Comparison ---*) + + (* TODO *) + + (*--- List Conversion ---*) + + (** A list of elements stored in the given set in descending order. *) + fun toDescList m = foldl (fn (x,xs) => x::xs) [] m + + (** A list of elements stored in the given map in ascending order. *) + fun toAscList m = foldr (fn (x,xs) => x::xs) [] m + + (** Alias for `toAscList`. *) + val toList = toAscList + + (** Returns the set (using the provided comparator function, `cmp`) that contains all key-value + pairs in the given list. Tail-recursive. *) + fun fromList cmp xs = List.foldl (fn (k, s) => insert s k) (empty cmp) xs + + (*--- Module ---*) + val Set = { + empty, + singleton, + insert, + null, + mem, + size, + depth, + minOpt, + min, + extractMinOpt, + extractMin, + remove, + foldl, + foldr, + map, + filter, + toDescList, + toAscList, + toList, + fromList + } + +in [ ("Set", Set) ] +end diff --git a/lib/StencilVector.trp b/lib/StencilVector.trp index a272bc91..f73701cc 100644 --- a/lib/StencilVector.trp +++ b/lib/StencilVector.trp @@ -146,26 +146,24 @@ let (*--- Constants ---*) (* TODO: Lift list functions `mapi`, `find` and `filter`? *) + (*--- Module ---*) val StencilVector = { - (* Constants *) - maskBits = maskBits, - maskMax = maskMax, - (* Functions *) - empty = empty, - singleton = singleton, - get = get, - getOrDefault = getOrDefault, - set = set, - unset = unset, - mem = mem, - valid = valid, - null = null, - mask = mask, - length = length, - map = map, - fold = fold + maskBits, + maskMax, + empty, + singleton, + get, + getOrDefault, + set, + unset, + mem, + valid, + null, + mask, + length, + map, + fold } -in (* Export public functions *) - [ ("StencilVector", StencilVector) - ] + +in [ ("StencilVector", StencilVector) ] end diff --git a/lib/String.trp b/lib/String.trp index b275f776..2dfe068e 100644 --- a/lib/String.trp +++ b/lib/String.trp @@ -70,17 +70,18 @@ let (** The maximum length of a string. (*--- Module ---*) val String = { - maxSize = maxSize, - size = size, - sub = sub, - subCode = subCode, - substring = substring, - concat = concat, - concatWith = concatWith, - implode = implode, - explode = explode, - map = map, - translate = translate + maxSize, + size, + sub, + subCode, + substring, + concat, + concatWith, + implode, + explode, + map, + translate } + in [("String", String)] end diff --git a/lib/Unit.trp b/lib/Unit.trp index 483d32ac..f4b49eba 100644 --- a/lib/Unit.trp +++ b/lib/Unit.trp @@ -112,13 +112,13 @@ let (*--- Module ---*) val Unit = { - group = group, - it = it, - isEq = isEq, - isTrue = isTrue, - isFalse = isFalse, - isNeq = isNeq, - run = run + group, + it, + isEq, + isTrue, + isFalse, + isNeq, + run } in [ ("Unit", Unit) ] diff --git a/lib/bst.trp b/lib/bst.trp deleted file mode 100644 index db7a8b8d..00000000 --- a/lib/bst.trp +++ /dev/null @@ -1,55 +0,0 @@ -datatype Atoms = NOT_FOUND - -let - - val leaf = () - - val empty_tree = () - - fun insert k v t = - case t of - () => ((), k, v, ()) - | (l, k', v', r) => if k = k' then (l, k, v, r) - else if k > k' then (l, k', v', insert k v r) else (insert k v l, k', v', r) - - fun lookup k t = - case t of - () => NOT_FOUND - | (l, k', v', r) => if k = k' then v' - else if k > k' then lookup k r else lookup k l - - fun contains k t = - case t of - () => false - | (l, k', v', r) => if k = k' then true - else if k > k' then contains k r else contains k l - - fun remove k t = - let fun extract_smallest (l, k, v, r) = - case l of - () => (k, v, r) - | _ => let val (k', v', l') = extract_smallest l - in (k', v', (l', k, v, r)) end - in case t of - () => () - | (l, k', v', r) => - if k = k' then - case l of - () => r - | _ => case r of - () => l - | _ => let val (k'', v'', r') = extract_smallest r - in (l, k'', v'', r') end - else if k > k' then (l, k', v', remove k r) else (remove k l, k', v', r) - end - -in - [ ("empty_tree", empty_tree) - , ("insert", insert) - , ("lookup", lookup) - , ("contains", contains) - , ("remove", remove) - ] - -end - diff --git a/tests/lib/HashMap.golden b/tests/lib/HashMap.golden index 02611f1f..160cc8aa 100644 --- a/tests/lib/HashMap.golden +++ b/tests/lib/HashMap.golden @@ -1,4 +1,4 @@ -2025-09-05T06:43:01.771Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. +2025-10-24T12:16:41.706Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. begin HashMap begin empty [ TEST ] it is null [ PASS ] it is null @@ -52,7 +52,7 @@ [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 end  - begin fromList [(1,42), (0,21)] + begin fromList [(0,21), (1,42)] [ TEST ] it is not null [ PASS ] it is not null [ TEST ] it has size 2 [ PASS ] it has size 2 [ TEST ] it contains 0 [ PASS ] it contains 0 diff --git a/tests/lib/HashMap.trp b/tests/lib/HashMap.trp index d9352616..269cb358 100644 --- a/tests/lib/HashMap.trp +++ b/tests/lib/HashMap.trp @@ -82,7 +82,7 @@ let (* Hash based on the lowest ten bits only. This way we can easily force vari ] end, let val m = fromList [(0,21),(1,42)] - in Unit.group "fromList [(1,42), (0,21)]" [ + in Unit.group "fromList [(0,21), (1,42)]" [ Unit.it "is not null" (Unit.isFalse (HashMap.null m)) , Unit.it "has size 2" (Unit.isEq 2 (HashMap.size m)) , Unit.it "contains 0" (Unit.isTrue (HashMap.mem m 0)) diff --git a/tests/lib/Map.golden b/tests/lib/Map.golden new file mode 100644 index 00000000..4c6fe128 --- /dev/null +++ b/tests/lib/Map.golden @@ -0,0 +1,184 @@ +2025-10-24T13:04:10.622Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. +begin Map + begin empty + [ TEST ] it is null [ PASS ] it is null + [ TEST ] it has size 0 [ PASS ] it has size 0 + [ TEST ] it has depth 0 [ PASS ] it has depth 0 + [ TEST ] it does not contain 0 [ PASS ] it does not contain 0 + [ TEST ] it returns [] for findOpt 0 [ PASS ] it returns [] for findOpt 0 + [ TEST ] it does not contain 1 [ PASS ] it does not contain 1 + [ TEST ] it returns [] for findOpt 1 [ PASS ] it returns [] for findOpt 1 + [ TEST ] it is not equal to 'empty >' [ PASS ] it is not equal to 'empty >' + [ TEST ] it returns [] for minOpt [ PASS ] it returns [] for minOpt + [ TEST ] it returns [], Ø for extractMinOpt [ PASS ] it returns [], Ø for extractMinOpt + end  + begin insert (empty) 0 42 + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 1 [ PASS ] it has size 1 + [ TEST ] it has depth 1 [ PASS ] it has depth 1 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [42] for findOpt 0 [ PASS ] it returns [42] for findOpt 0 + [ TEST ] it returns 42 for find 0 [ PASS ] it returns 42 for find 0 + [ TEST ] it does not contain 1 [ PASS ] it does not contain 1 + [ TEST ] it returns [] for findOpt 1 [ PASS ] it returns [] for findOpt 1 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], Ø for extractMinOpt [ PASS ] it returns [(0,42)], Ø for extractMinOpt + end  + begin singleton 0 42 + [ TEST ] it is equivalent to 'insert (empty) 0 42' [ PASS ] it is equivalent to 'insert (empty) 0 42' + end  + begin fromList [(0,42)] + [ TEST ] it is equivalent to 'insert (empty) 0 42' [ PASS ] it is equivalent to 'insert (empty) 0 42' + end  + begin fromList [(1,42)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 1 [ PASS ] it has size 1 + [ TEST ] it has depth 1 [ PASS ] it has depth 1 + [ TEST ] it does not contain 0 [ PASS ] it does not contain 0 + [ TEST ] it returns [] for findOpt 0 [ PASS ] it returns [] for findOpt 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], Ø for extractMinOpt [ PASS ] it returns [(0,42)], Ø for extractMinOpt + end  + begin insert (fromList [(0,42)]) 0 'a' + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 1 [ PASS ] it has size 1 + [ TEST ] it has depth 1 [ PASS ] it has depth 1 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns ['a'] for findOpt 0 [ PASS ] it returns ['a'] for findOpt 0 + [ TEST ] it returns 'a' for find 0 [ PASS ] it returns 'a' for find 0 + [ TEST ] it does not contain 1 [ PASS ] it does not contain 1 + [ TEST ] it returns [] for findOpt 1 [ PASS ] it returns [] for findOpt 1 + [ TEST ] it returns [(0,'a')] for minOpt [ PASS ] it returns [(0,'a')] for minOpt + [ TEST ] it returns [(0,'a')], Ø for extractMinOpt [ PASS ] it returns [(0,'a')], Ø for extractMinOpt + end  + begin fromList [(1,42), (0,21)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 2 [ PASS ] it has size 2 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [21] for findOpt 0 [ PASS ] it returns [21] for findOpt 0 + [ TEST ] it returns 21 for find 0 [ PASS ] it returns 21 for find 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], .. for extractMinOpt [ PASS ] it returns [(0,42)], .. for extractMinOpt + end  + begin fromList [(0,21), (1,42)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 2 [ PASS ] it has size 2 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [21] for findOpt 0 [ PASS ] it returns [21] for findOpt 0 + [ TEST ] it returns 21 for find 0 [ PASS ] it returns 21 for find 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], .. for extractMinOpt [ PASS ] it returns [(0,42)], .. for extractMinOpt + end  + begin fromList [(0,21), (1,42), (2,7)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [21] for findOpt 0 [ PASS ] it returns [21] for findOpt 0 + [ TEST ] it returns 21 for find 0 [ PASS ] it returns 21 for find 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [7] for findOpt 2 [ PASS ] it returns [7] for findOpt 2 + [ TEST ] it returns 7 for find 2 [ PASS ] it returns 7 for find 2 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], .. for extractMinOpt [ PASS ] it returns [(0,42)], .. for extractMinOpt + end  + begin fromList [(2,7), (1,42), (0,21)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 3 [ PASS ] it has depth 3 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [21] for findOpt 0 [ PASS ] it returns [21] for findOpt 0 + [ TEST ] it returns 21 for find 0 [ PASS ] it returns 21 for find 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [7] for findOpt 2 [ PASS ] it returns [7] for findOpt 2 + [ TEST ] it returns 7 for find 2 [ PASS ] it returns 7 for find 2 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], .. for extractMinOpt [ PASS ] it returns [(0,42)], .. for extractMinOpt + end  + begin fromList [(0,21), (1,42), (2,7)] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 3 [ PASS ] it has depth 3 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it returns [21] for findOpt 0 [ PASS ] it returns [21] for findOpt 0 + [ TEST ] it returns 21 for find 0 [ PASS ] it returns 21 for find 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [42] for findOpt 1 [ PASS ] it returns [42] for findOpt 1 + [ TEST ] it returns 42 for find 1 [ PASS ] it returns 42 for find 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [7] for findOpt 2 [ PASS ] it returns [7] for findOpt 2 + [ TEST ] it returns 7 for find 2 [ PASS ] it returns 7 for find 2 + [ TEST ] it returns [(0,42)] for minOpt [ PASS ] it returns [(0,42)] for minOpt + [ TEST ] it returns [(0,42)], empty for extractMinOpt [ PASS ] it returns [(0,42)], empty for extractMinOpt + end  + begin remove (fromList [(0,42)]) 0 + [ TEST ] it is equivalent to 'empty' [ PASS ] it is equivalent to 'empty' + end  + begin remove (fromList [(1,42)]) 0 + [ TEST ] it is equivalent to itself [ PASS ] it is equivalent to itself + end  + begin remove (fromList [(1,42)]) 2 + [ TEST ] it is equivalent to itself [ PASS ] it is equivalent to itself + end  + begin remove (fromList [(1,42),(0,21),(2,7)]) 0 + [ TEST ] it is equivalent to 'fromList [(1,42), (2,7)]' [ PASS ] it is equivalent to 'fromList [(1,42), (2,7)]' + end  + begin remove (fromList [(1,42),(0,21),(2,7)]) 1 + [ TEST ] it is equivalent to 'fromList [(2,7), (0,21)]' [ PASS ] it is equivalent to 'fromList [(2,7), (0,21)]' + end  + begin remove (fromList [(1,42),(0,21),(2,7)]) 2 + [ TEST ] it is equivalent to 'fromList [(1,42), (0,21)]' [ PASS ] it is equivalent to 'fromList [(1,42), (0,21)]' + end  + begin foldl + [ TEST ] it folds fromList [] [ PASS ] it folds fromList [] + [ TEST ] it folds fromList [(1,2)] [ PASS ] it folds fromList [(1,2)] + [ TEST ] it folds fromList [(2,0), (1,3)] [ PASS ] it folds fromList [(2,0), (1,3)] + end  + begin foldr + [ TEST ] it folds fromList [] [ PASS ] it folds fromList [] + [ TEST ] it folds fromList [(1,2)] [ PASS ] it folds fromList [(1,2)] + [ TEST ] it folds fromList [(2,0), (1,3)] [ PASS ] it folds fromList [(2,0), (1,3)] + end  + begin map + [ TEST ] it mirrors > (x -> x) (fromList [(2,0), (1,3)]) [ PASS ] it mirrors > (x -> x) (fromList [(2,0), (1,3)]) + [ TEST ] it maps keys > ((k,v) -> (-k,v)) (fromList [(2,0), (1,3)]) [ PASS ] it maps keys > ((k,v) -> (-k,v)) (fromList [(2,0), (1,3)]) + [ TEST ] it maps values > ((k,v) -> (k,-v)) (fromList [(2,0), (1,3)]) [ PASS ] it maps values > ((k,v) -> (k,-v)) (fromList [(2,0), (1,3)]) + end  + begin keys, values, and toList + [ TEST ] it converts [] [ PASS ] it converts [] + [ TEST ] it converts [(0,'a')] [ PASS ] it converts [(0,'a')] + [ TEST ] it converts [(1,'b'), (0,'a')] [ PASS ] it converts [(1,'b'), (0,'a')] + [ TEST ] it converts [(1, 'b'), (0,'a'), (2, 'c')] [ PASS ] it converts [(1, 'b'), (0,'a'), (2, 'c')] + begin filter + [ TEST ] it flushes (_ -> false) [ PASS ] it flushes (_ -> false) + [ TEST ] it copies (_ -> true) [ PASS ] it copies (_ -> true) + [ TEST ] it filters for even keys ((k,_) -> k % 2 = 0) [ PASS ] it filters for even keys ((k,_) -> k % 2 = 0) + [ TEST ] it filters for odd keys ((k,_) -> k % 2 = 1) [ PASS ] it filters for odd keys ((k,_) -> k % 2 = 1) + [ TEST ] it filters for small keys ((k,_) -> k < 2) [ PASS ] it filters for small keys ((k,_) -> k < 2) + [ TEST ] it filters for large keys ((k,_) -> k > 1) [ PASS ] it filters for large keys ((k,_) -> k > 1) + [ TEST ] it filters for even values ((_,v) -> v % 2 = 0) [ PASS ] it filters for even values ((_,v) -> v % 2 = 0) + [ TEST ] it filters for odd values ((k,_) -> v % 2 = 1) [ PASS ] it filters for odd values ((k,_) -> v % 2 = 1) + end  + end  +end  + +Total: 133 +Passes: 133 +>>> Main thread finished with value: true@{}%{} diff --git a/tests/lib/Map.trp b/tests/lib/Map.trp new file mode 100644 index 00000000..9079b31c --- /dev/null +++ b/tests/lib/Map.trp @@ -0,0 +1,312 @@ +import Unit +import Map + +let fun lt a b = a < b + fun gt a b = a > b + + val empty = Map.empty lt + val singleton = Map.singleton lt + val fromList = Map.fromList lt + + val tests = Unit.group "Map" [ + (* -- Empty, size, and findOpt (mem, find) -- *) + let val m = empty + in Unit.group "empty" [ + Unit.it "is null" (Unit.isTrue (Map.null m)) + , Unit.it "has size 0" (Unit.isEq 0 (Map.size m)) + , Unit.it "has depth 0" (Unit.isEq 0 (Map.depth m)) + , Unit.it "does not contain 0" (Unit.isFalse (Map.mem m 0)) + , Unit.it "returns [] for findOpt 0" (Unit.isEq [] (Map.findOpt m 0)) + , Unit.it "does not contain 1" (Unit.isFalse (Map.mem m 1)) + , Unit.it "returns [] for findOpt 1" (Unit.isEq [] (Map.findOpt m 1)) + , Unit.it "is not equal to 'empty >'" (Unit.isNeq (Map.empty gt) m) + , Unit.it "returns [] for minOpt" (Unit.isEq [] (Map.minOpt m)) + , Unit.it "returns [], Ø for extractMinOpt" + (Unit.isEq ([], empty) (Map.extractMinOpt m)) + ] + end, + (* -- Insert / Singleton, size, and findOpt (mem, find) -- *) + let val m = Map.insert (empty) 0 42 + in Unit.group "insert (empty) 0 42" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 1" (Unit.isEq 1 (Map.size m)) + , Unit.it "has depth 1" (Unit.isEq 1 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [42] for findOpt 0" (Unit.isEq [42] (Map.findOpt m 0)) + , Unit.it "returns 42 for find 0" (Unit.isEq 42 (Map.find m 0)) + , Unit.it "does not contain 1" (Unit.isFalse (Map.mem m 1)) + , Unit.it "returns [] for findOpt 1" (Unit.isEq [] (Map.findOpt m 1)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,42)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], Ø for extractMinOpt" + (Unit.isEq ([(0,42)], empty) (Map.extractMinOpt m)) + ] + end, + let val m = singleton 0 42 + in Unit.group "singleton 0 42" [ + Unit.it "is equivalent to 'insert (empty) 0 42'" + (Unit.isEq (Map.insert empty 0 42) m) + ] + end, + (* -- Insert (fromList), size, findOpt (mem, find), min / extractMin -- *) + let val m = fromList [(0,42)] + in Unit.group "fromList [(0,42)]" [ + Unit.it "is equivalent to 'insert (empty) 0 42'" + (Unit.isEq (Map.insert empty 0 42) m) + ] + end, + let val m = fromList [(1,42)] + in Unit.group "fromList [(1,42)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 1" (Unit.isEq 1 (Map.size m)) + , Unit.it "has depth 1" (Unit.isEq 1 (Map.depth m)) + , Unit.it "does not contain 0" (Unit.isFalse (Map.mem m 0)) + , Unit.it "returns [] for findOpt 0" (Unit.isEq [] (Map.findOpt m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(1,42)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], Ø for extractMinOpt" + (Unit.isEq ([(1,42)], empty) (Map.extractMinOpt m)) + ] + end, + let val m = Map.insert (fromList [(0,42)]) 0 "a" + in Unit.group "insert (fromList [(0,42)]) 0 'a'" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 1" (Unit.isEq 1 (Map.size m)) + , Unit.it "has depth 1" (Unit.isEq 1 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns ['a'] for findOpt 0" (Unit.isEq ["a"] (Map.findOpt m 0)) + , Unit.it "returns 'a' for find 0" (Unit.isEq "a" (Map.find m 0)) + , Unit.it "does not contain 1" (Unit.isFalse (Map.mem m 1)) + , Unit.it "returns [] for findOpt 1" (Unit.isEq [] (Map.findOpt m 1)) + , Unit.it "returns [(0,'a')] for minOpt" (Unit.isEq [(0,"a")] (Map.minOpt m)) + , Unit.it "returns [(0,'a')], Ø for extractMinOpt" + (Unit.isEq ([(0,"a")], empty) (Map.extractMinOpt m)) + ] + end, + let val m = fromList [(1,42), (0,21)] + in Unit.group "fromList [(1,42), (0,21)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 2" (Unit.isEq 2 (Map.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [21] for findOpt 0" (Unit.isEq [21] (Map.findOpt m 0)) + , Unit.it "returns 21 for find 0" (Unit.isEq 21 (Map.find m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,21)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], .. for extractMinOpt" + (Unit.isEq ([(0,21)], fromList [(1,42)]) (Map.extractMinOpt m)) + ] + end, + let val m = fromList [(0,21),(1,42)] + in Unit.group "fromList [(0,21), (1,42)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 2" (Unit.isEq 2 (Map.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [21] for findOpt 0" (Unit.isEq [21] (Map.findOpt m 0)) + , Unit.it "returns 21 for find 0" (Unit.isEq 21 (Map.find m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,21)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], .. for extractMinOpt" + (Unit.isEq ([(0,21)], fromList [(1,42)]) (Map.extractMinOpt m)) + ] + end, + let val m = fromList [(1,42),(0,21),(2,7)] + in Unit.group "fromList [(0,21), (1,42), (2,7)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Map.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [21] for findOpt 0" (Unit.isEq [21] (Map.findOpt m 0)) + , Unit.it "returns 21 for find 0" (Unit.isEq 21 (Map.find m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "contains 2" (Unit.isTrue (Map.mem m 2)) + , Unit.it "returns [7] for findOpt 2" (Unit.isEq [7] (Map.findOpt m 2)) + , Unit.it "returns 7 for find 2" (Unit.isEq 7 (Map.find m 2)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,21)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], .. for extractMinOpt" + (Unit.isEq ([(0,21)], fromList [(1,42),(2,7)]) (Map.extractMinOpt m)) + ] + end, + let val m = fromList [(2,7),(1,42),(0,21)] + in Unit.group "fromList [(2,7), (1,42), (0,21)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Map.size m)) + , Unit.it "has depth 3" (Unit.isEq 3 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [21] for findOpt 0" (Unit.isEq [21] (Map.findOpt m 0)) + , Unit.it "returns 21 for find 0" (Unit.isEq 21 (Map.find m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "contains 2" (Unit.isTrue (Map.mem m 2)) + , Unit.it "returns [7] for findOpt 2" (Unit.isEq [7] (Map.findOpt m 2)) + , Unit.it "returns 7 for find 2" (Unit.isEq 7 (Map.find m 2)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,21)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], .. for extractMinOpt" + (Unit.isEq ([(0,21)], fromList [(2,7),(1,42)]) (Map.extractMinOpt m)) + ] + end, + let val m = fromList [(0,21),(1,42),(2,7)] + in Unit.group "fromList [(0,21), (1,42), (2,7)]" [ + Unit.it "is not null" (Unit.isFalse (Map.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Map.size m)) + , Unit.it "has depth 3" (Unit.isEq 3 (Map.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Map.mem m 0)) + , Unit.it "returns [21] for findOpt 0" (Unit.isEq [21] (Map.findOpt m 0)) + , Unit.it "returns 21 for find 0" (Unit.isEq 21 (Map.find m 0)) + , Unit.it "contains 1" (Unit.isTrue (Map.mem m 1)) + , Unit.it "returns [42] for findOpt 1" (Unit.isEq [42] (Map.findOpt m 1)) + , Unit.it "returns 42 for find 1" (Unit.isEq 42 (Map.find m 1)) + , Unit.it "contains 2" (Unit.isTrue (Map.mem m 2)) + , Unit.it "returns [7] for findOpt 2" (Unit.isEq [7] (Map.findOpt m 2)) + , Unit.it "returns 7 for find 2" (Unit.isEq 7 (Map.find m 2)) + , Unit.it "returns [(0,42)] for minOpt" (Unit.isEq [(0,21)] (Map.minOpt m)) + , Unit.it "returns [(0,42)], empty for extractMinOpt" + (Unit.isEq ([(0,21)], fromList [(1,42),(2,7)]) (Map.extractMinOpt m)) + ] + end, + (* -- Insert (fromList), delete, size, and findOpt (mem and find) -- *) + let val m = Map.remove (fromList [(0,21)]) 0 + in Unit.group "remove (fromList [(0,42)]) 0" [ + Unit.it "is equivalent to 'empty'" (Unit.isEq empty m) + ] + end, + let val m = fromList [(1,42)] + val m' = Map.remove m 0 + in Unit.group "remove (fromList [(1,42)]) 0" [ + Unit.it "is equivalent to itself" (Unit.isEq m m') + ] + end, + let val m = fromList [(1,42)] + val m' = Map.remove m 2 + in Unit.group "remove (fromList [(1,42)]) 2" [ + Unit.it "is equivalent to itself" (Unit.isEq m m') + ] + end, + let val m = Map.remove (fromList [(1,42),(0,21),(2,7)]) 0 + in Unit.group "remove (fromList [(1,42),(0,21),(2,7)]) 0" [ + Unit.it "is equivalent to 'fromList [(1,42), (2,7)]'" + (Unit.isEq (fromList [(1,42), (2,7)]) m) + ] + end, + let val m = Map.remove (fromList [(1,42),(0,21),(2,7)]) 1 + in Unit.group "remove (fromList [(1,42),(0,21),(2,7)]) 1" [ + Unit.it "is equivalent to 'fromList [(2,7), (0,21)]'" + (Unit.isEq (fromList [(2,7), (0,21)]) m) + ] + end, + let val m = Map.remove (fromList [(1,42),(0,21),(2,7)]) 2 + in Unit.group "remove (fromList [(1,42),(0,21),(2,7)]) 2" [ + Unit.it "is equivalent to 'fromList [(1,42), (0,21)]'" + (Unit.isEq (fromList [(1,42), (0,21)]) m) + ] + end, + (* -- foldl, foldlr, and its derivatives: map, keys, values, and toList -- *) + let fun f (kv,x) = kv::x + in Unit.group "foldl" [ + Unit.it "folds fromList []" + (Unit.isEq [] (Map.foldl f [] (fromList []))) + , Unit.it "folds fromList [(1,2)]" + (Unit.isEq [(1,2)] (Map.foldl f [] (fromList [(1,2)]))) + , Unit.it "folds fromList [(2,0), (1,3)]" + (Unit.isEq [(2,0), (1,3)] (Map.foldl f [] (fromList [(2,0),(1,3)]))) + ] + end, + let fun f (kv,x) = kv::x + in Unit.group "foldr" [ + Unit.it "folds fromList []" + (Unit.isEq [] (Map.foldr f [] (fromList []))) + , Unit.it "folds fromList [(1,2)]" + (Unit.isEq [(1,2)] (Map.foldr f [] (fromList [(1,2)]))) + , Unit.it "folds fromList [(2,0), (1,3)]" + (Unit.isEq [(1,3), (2,0)] (Map.foldr f [] (fromList [(2,0),(1,3)]))) + ] + end, + Unit.group "map" [ + Unit.it "mirrors > (x -> x) (fromList [(2,0), (1,3)])" + (Unit.isEq (Map.fromList gt [(1,3), (2,0)]) + (Map.map gt (fn kv => kv) (fromList [(2,0), (1,3)]))) + , Unit.it "maps keys > ((k,v) -> (-k,v)) (fromList [(2,0), (1,3)])" + (Unit.isEq (Map.fromList gt [(-1,3), (-2,0)]) + (Map.map gt (fn (k,v) => (-k,v)) (fromList [(2,0), (1,3)]))) + , Unit.it "maps values > ((k,v) -> (k,-v)) (fromList [(2,0), (1,3)])" + (Unit.isEq (Map.fromList gt [(1,-3), (2,-0)]) + (Map.map gt (fn (k,v) => (k,-v)) (fromList [(2,0), (1,3)]))) + ], + Unit.group "keys, values, and toList" [ + let val m = fromList [] + in Unit.it "converts []" [ + (Unit.isEq [] (Map.keys m)) + , (Unit.isEq [] (Map.values m)) + , (Unit.isEq [] (Map.toList m)) + , (Unit.isEq [] (Map.toAscList m)) + , (Unit.isEq [] (Map.toDescList m)) + ] + end + , let val m = fromList [(0,"a")] + in Unit.it "converts [(0,'a')]" [ + (Unit.isEq [0] (Map.keys m)) + , (Unit.isEq ["a"] (Map.values m)) + , (Unit.isEq [(0,"a")] (Map.toList m)) + , (Unit.isEq [(0,"a")] (Map.toAscList m)) + , (Unit.isEq [(0,"a")] (Map.toDescList m)) + ] + end + , let val m = fromList [(1,"b"), (0,"a")] + in Unit.it "converts [(1,'b'), (0,'a')]" [ + (Unit.isEq [0, 1] (Map.keys m)) + , (Unit.isEq ["a", "b"] (Map.values m)) + , (Unit.isEq [(0,"a"), (1,"b")] (Map.toList m)) + , (Unit.isEq [(0,"a"), (1,"b")] (Map.toAscList m)) + , (Unit.isEq [(1,"b"), (0,"a")] (Map.toDescList m)) + ] + end + , let val m = fromList [(1,"b"), (0,"a"), (2,"c")] + in Unit.it "converts [(1, 'b'), (0,'a'), (2, 'c')]" [ + (Unit.isEq [0, 1, 2] (Map.keys m)) + , (Unit.isEq ["a", "b", "c"] (Map.values m)) + , (Unit.isEq [(0, "a"), (1, "b"), (2, "c")] (Map.toList m)) + , (Unit.isEq [(0, "a"), (1, "b"), (2, "c")] (Map.toAscList m)) + , (Unit.isEq [(2, "c"), (1, "b"), (0, "a")] (Map.toDescList m)) + ] + end, + (* -- filter -- *) + let val m = fromList [(1,2), (2,0), (0,3), (3,1)] + in Unit.group "filter" [ + Unit.it "flushes (_ -> false)" + (Unit.isEq (fromList []) + (Map.filter (fn _ => false) m)) + , Unit.it "copies (_ -> true)" + (Unit.isEq (fromList [(1,2), (2,0), (0,3), (3,1)]) + (Map.filter (fn _ => true) m)) + , Unit.it "filters for even keys ((k,_) -> k % 2 = 0)" + (Unit.isEq (fromList [(2,0), (0,3)]) + (Map.filter (fn (k,_) => (k mod 2) = 0) m)) + , Unit.it "filters for odd keys ((k,_) -> k % 2 = 1)" + (Unit.isEq (fromList [(1,2), (3,1)]) + (Map.filter (fn (k,_) => (k mod 2) = 1) m)) + , Unit.it "filters for small keys ((k,_) -> k < 2)" + (Unit.isEq (fromList [(1,2), (0,3)]) + (Map.filter (fn (k,_) => k < 2) m)) + , Unit.it "filters for large keys ((k,_) -> k > 1)" + (Unit.isEq (fromList [(2,0), (3,1)]) + (Map.filter (fn (k,_) => k > 1) m)) + , Unit.it "filters for even values ((_,v) -> v % 2 = 0)" + (Unit.isEq (fromList [(1,2), (2,0)]) + (Map.filter (fn (_,v) => (v mod 2) = 0) m)) + , Unit.it "filters for odd values ((k,_) -> v % 2 = 1)" + (Unit.isEq (fromList [(3,1), (0,3)]) + (Map.filter (fn (_,v) => (v mod 2) = 1) m)) + ] + end + ] + ] +in Unit.run authority tests end diff --git a/tests/lib/Number.golden b/tests/lib/Number.golden index f1ffff2c..caa218fc 100644 --- a/tests/lib/Number.golden +++ b/tests/lib/Number.golden @@ -1,4 +1,4 @@ -2025-09-19T11:39:50.589Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. +2025-10-24T09:49:37.065Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. begin Number begin maxInt / minInt [ TEST ] it maxInt++ = maxInt [ PASS ] it maxInt++ = maxInt @@ -84,6 +84,72 @@ [ TEST ] it truncates 2^33-1 [ PASS ] it truncates 2^33-1 [ TEST ] it truncates -2^33-1 [ PASS ] it truncates -2^33-1 end  + begin eq + [ TEST ] it accepts 0 0 [ PASS ] it accepts 0 0 + [ TEST ] it accepts 1 1 [ PASS ] it accepts 1 1 + [ TEST ] it accepts -1 -1 [ PASS ] it accepts -1 -1 + [ TEST ] it accepts 42 42 [ PASS ] it accepts 42 42 + [ TEST ] it accepts 1/2 2/4 [ PASS ] it accepts 1/2 2/4 + [ TEST ] it rejects 0 1 [ PASS ] it rejects 0 1 + [ TEST ] it rejects 1 0 [ PASS ] it rejects 1 0 + [ TEST ] it rejects 0 -1 [ PASS ] it rejects 0 -1 + [ TEST ] it rejects -1 0 [ PASS ] it rejects -1 0 + [ TEST ] it rejects 2 1 [ PASS ] it rejects 2 1 + [ TEST ] it rejects 2 1 [ PASS ] it rejects 2 1 + [ TEST ] it rejects 42 12 [ PASS ] it rejects 42 12 + end  + begin lt + [ TEST ] it accepts 0 1 [ PASS ] it accepts 0 1 + [ TEST ] it accepts 1 2 [ PASS ] it accepts 1 2 + [ TEST ] it accepts -1 0 [ PASS ] it accepts -1 0 + [ TEST ] it accepts 12 42 [ PASS ] it accepts 12 42 + [ TEST ] it accepts 0 1/2 [ PASS ] it accepts 0 1/2 + [ TEST ] it rejects 0 0 [ PASS ] it rejects 0 0 + [ TEST ] it rejects 42 42 [ PASS ] it rejects 42 42 + [ TEST ] it rejects 1 0 [ PASS ] it rejects 1 0 + [ TEST ] it rejects 2 1 [ PASS ] it rejects 2 1 + [ TEST ] it rejects 0 -1 [ PASS ] it rejects 0 -1 + [ TEST ] it rejects 42 12 [ PASS ] it rejects 42 12 + end  + begin le + [ TEST ] it accepts 0 1 [ PASS ] it accepts 0 1 + [ TEST ] it accepts 1 2 [ PASS ] it accepts 1 2 + [ TEST ] it accepts -1 0 [ PASS ] it accepts -1 0 + [ TEST ] it accepts 12 42 [ PASS ] it accepts 12 42 + [ TEST ] it accepts 0 1/2 [ PASS ] it accepts 0 1/2 + [ TEST ] it accepts 0 0 [ PASS ] it accepts 0 0 + [ TEST ] it accepts 42 42 [ PASS ] it accepts 42 42 + [ TEST ] it rejects 1 0 [ PASS ] it rejects 1 0 + [ TEST ] it rejects 2 1 [ PASS ] it rejects 2 1 + [ TEST ] it rejects 0 -1 [ PASS ] it rejects 0 -1 + [ TEST ] it rejects 42 12 [ PASS ] it rejects 42 12 + end  + begin gt + [ TEST ] it rejects 0 1 [ PASS ] it rejects 0 1 + [ TEST ] it rejects 1 2 [ PASS ] it rejects 1 2 + [ TEST ] it rejects -1 0 [ PASS ] it rejects -1 0 + [ TEST ] it rejects 12 42 [ PASS ] it rejects 12 42 + [ TEST ] it rejects 0 1/2 [ PASS ] it rejects 0 1/2 + [ TEST ] it rejects 0 0 [ PASS ] it rejects 0 0 + [ TEST ] it rejects 42 42 [ PASS ] it rejects 42 42 + [ TEST ] it accepts 1 0 [ PASS ] it accepts 1 0 + [ TEST ] it accepts 2 1 [ PASS ] it accepts 2 1 + [ TEST ] it accepts 0 -1 [ PASS ] it accepts 0 -1 + [ TEST ] it accepts 42 12 [ PASS ] it accepts 42 12 + end  + begin ge + [ TEST ] it rejects 0 1 [ PASS ] it rejects 0 1 + [ TEST ] it rejects 1 2 [ PASS ] it rejects 1 2 + [ TEST ] it rejects -1 0 [ PASS ] it rejects -1 0 + [ TEST ] it rejects 12 42 [ PASS ] it rejects 12 42 + [ TEST ] it rejects 0 1/2 [ PASS ] it rejects 0 1/2 + [ TEST ] it accepts 0 0 [ PASS ] it accepts 0 0 + [ TEST ] it accepts 42 42 [ PASS ] it accepts 42 42 + [ TEST ] it accepts 1 0 [ PASS ] it accepts 1 0 + [ TEST ] it accepts 2 1 [ PASS ] it accepts 2 1 + [ TEST ] it accepts 0 -1 [ PASS ] it accepts 0 -1 + [ TEST ] it accepts 42 12 [ PASS ] it accepts 42 12 + end  begin toString [ TEST ] it returns '0' for 0 [ PASS ] it returns '0' for 0 [ TEST ] it returns '0' for 0/2 [ PASS ] it returns '0' for 0/2 @@ -100,6 +166,6 @@ end  end  -Total: 72 -Passes: 72 +Total: 128 +Passes: 128 >>> Main thread finished with value: true@{}%{} diff --git a/tests/lib/Number.trp b/tests/lib/Number.trp index aa03aa02..5ba4eebf 100644 --- a/tests/lib/Number.trp +++ b/tests/lib/Number.trp @@ -86,6 +86,72 @@ let val tests = Unit.group "Number" [ , Unit.it "truncates 2^33-1" (Unit.isEq Number.maxInt32 (Number.toInt32 (2*2*2*(1 << 30)))) , Unit.it "truncates -2^33-1" (Unit.isEq Number.minInt32 (Number.toInt32 (-(2*2*2*(1 << 30) - 1)))) ], + Unit.group "eq" [ + Unit.it "accepts 0 0" (Unit.isTrue (Number.eq 0 0)) + , Unit.it "accepts 1 1" (Unit.isTrue (Number.eq 1 1)) + , Unit.it "accepts -1 -1" (Unit.isTrue (Number.eq (-1) (-1))) + , Unit.it "accepts 42 42" (Unit.isTrue (Number.eq 42 42)) + , Unit.it "accepts 1/2 2/4" (Unit.isTrue (Number.eq (1/2) (2/4))) + , Unit.it "rejects 0 1" (Unit.isFalse (Number.eq 0 1)) + , Unit.it "rejects 1 0" (Unit.isFalse (Number.eq 1 0)) + , Unit.it "rejects 0 -1" (Unit.isFalse (Number.eq 0 (-1))) + , Unit.it "rejects -1 0" (Unit.isFalse (Number.eq (-1) 0)) + , Unit.it "rejects 2 1" (Unit.isFalse (Number.eq 2 1)) + , Unit.it "rejects 2 1" (Unit.isFalse (Number.eq 2 1)) + , Unit.it "rejects 42 12" (Unit.isFalse (Number.eq 42 12)) + ], + Unit.group "lt" [ + Unit.it "accepts 0 1" (Unit.isTrue (Number.lt 0 1)) + , Unit.it "accepts 1 2" (Unit.isTrue (Number.lt 1 2)) + , Unit.it "accepts -1 0" (Unit.isTrue (Number.lt (-1) 0)) + , Unit.it "accepts 12 42" (Unit.isTrue (Number.lt 12 42)) + , Unit.it "accepts 0 1/2" (Unit.isTrue (Number.lt 0 (1/2))) + , Unit.it "rejects 0 0" (Unit.isFalse (Number.lt 0 0)) + , Unit.it "rejects 42 42" (Unit.isFalse (Number.lt 42 42)) + , Unit.it "rejects 1 0" (Unit.isFalse (Number.lt 1 0)) + , Unit.it "rejects 2 1" (Unit.isFalse (Number.lt 2 1)) + , Unit.it "rejects 0 -1" (Unit.isFalse (Number.lt 1 (-1))) + , Unit.it "rejects 42 12" (Unit.isFalse (Number.lt 42 12)) + ], + Unit.group "le" [ + Unit.it "accepts 0 1" (Unit.isTrue (Number.le 0 1)) + , Unit.it "accepts 1 2" (Unit.isTrue (Number.le 1 2)) + , Unit.it "accepts -1 0" (Unit.isTrue (Number.le (-1) 0)) + , Unit.it "accepts 12 42" (Unit.isTrue (Number.le 12 42)) + , Unit.it "accepts 0 1/2" (Unit.isTrue (Number.le 0 (1/2))) + , Unit.it "accepts 0 0" (Unit.isTrue (Number.le 0 0)) + , Unit.it "accepts 42 42" (Unit.isTrue (Number.le 42 42)) + , Unit.it "rejects 1 0" (Unit.isFalse (Number.le 1 0)) + , Unit.it "rejects 2 1" (Unit.isFalse (Number.le 2 1)) + , Unit.it "rejects 0 -1" (Unit.isFalse (Number.le 1 (-1))) + , Unit.it "rejects 42 12" (Unit.isFalse (Number.le 42 12)) + ], + Unit.group "gt" [ + Unit.it "rejects 0 1" (Unit.isFalse (Number.gt 0 1)) + , Unit.it "rejects 1 2" (Unit.isFalse (Number.gt 1 2)) + , Unit.it "rejects -1 0" (Unit.isFalse (Number.gt (-1) 0)) + , Unit.it "rejects 12 42" (Unit.isFalse (Number.gt 12 42)) + , Unit.it "rejects 0 1/2" (Unit.isFalse (Number.gt 0 (1/2))) + , Unit.it "rejects 0 0" (Unit.isFalse (Number.gt 0 0)) + , Unit.it "rejects 42 42" (Unit.isFalse (Number.gt 42 42)) + , Unit.it "accepts 1 0" (Unit.isTrue (Number.gt 1 0)) + , Unit.it "accepts 2 1" (Unit.isTrue (Number.gt 2 1)) + , Unit.it "accepts 0 -1" (Unit.isTrue (Number.gt 1 (-1))) + , Unit.it "accepts 42 12" (Unit.isTrue (Number.gt 42 12)) + ], + Unit.group "ge" [ + Unit.it "rejects 0 1" (Unit.isFalse (Number.ge 0 1)) + , Unit.it "rejects 1 2" (Unit.isFalse (Number.ge 1 2)) + , Unit.it "rejects -1 0" (Unit.isFalse (Number.ge (-1) 0)) + , Unit.it "rejects 12 42" (Unit.isFalse (Number.ge 12 42)) + , Unit.it "rejects 0 1/2" (Unit.isFalse (Number.ge 0 (1/2))) + , Unit.it "accepts 0 0" (Unit.isTrue (Number.ge 0 0)) + , Unit.it "accepts 42 42" (Unit.isTrue (Number.ge 42 42)) + , Unit.it "accepts 1 0" (Unit.isTrue (Number.ge 1 0)) + , Unit.it "accepts 2 1" (Unit.isTrue (Number.ge 2 1)) + , Unit.it "accepts 0 -1" (Unit.isTrue (Number.ge 1 (-1))) + , Unit.it "accepts 42 12" (Unit.isTrue (Number.ge 42 12)) + ], Unit.group "toString" [ Unit.it "returns '0' for 0" (Unit.isEq "0" (Number.toString 0)) , Unit.it "returns '0' for 0/2" (Unit.isEq "0" (Number.toString (0/2))) diff --git a/tests/lib/Set.golden b/tests/lib/Set.golden new file mode 100644 index 00000000..d8c78393 --- /dev/null +++ b/tests/lib/Set.golden @@ -0,0 +1,152 @@ +2025-10-24T13:05:01.367Z [RTM] info: Skipping network creation. Observe that all external IO operations will yield a runtime error. +begin Set + begin empty + [ TEST ] it is null [ PASS ] it is null + [ TEST ] it has size 0 [ PASS ] it has size 0 + [ TEST ] it has depth 0 [ PASS ] it has depth 0 + [ TEST ] it does not contain 0 [ PASS ] it does not contain 0 + [ TEST ] it does not contain 1 [ PASS ] it does not contain 1 + [ TEST ] it is not equal to 'empty >' [ PASS ] it is not equal to 'empty >' + [ TEST ] it returns [] for minOpt [ PASS ] it returns [] for minOpt + [ TEST ] it returns [], Ø for extractMinOpt [ PASS ] it returns [], Ø for extractMinOpt + end  + begin insert (empty) 0 + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 1 [ PASS ] it has size 1 + [ TEST ] it has depth 1 [ PASS ] it has depth 1 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it does not contain 1 [ PASS ] it does not contain 1 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], Ø for extractMinOpt [ PASS ] it returns [0], Ø for extractMinOpt + [ TEST ] it returns 0, Ø for extractMin [ PASS ] it returns 0, Ø for extractMin + end  + begin singleton 0 42 + [ TEST ] it is equivalent to 'insert (empty) 0' [ PASS ] it is equivalent to 'insert (empty) 0' + end  + begin fromList [(0,42)] + [ TEST ] it is equivalent to 'insert (empty) 0' [ PASS ] it is equivalent to 'insert (empty) 0' + end  + begin fromList [1] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 1 [ PASS ] it has size 1 + [ TEST ] it has depth 1 [ PASS ] it has depth 1 + [ TEST ] it does not contain 0 [ PASS ] it does not contain 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [1] for minOpt [ PASS ] it returns [1] for minOpt + [ TEST ] it returns 1 for minOpt [ PASS ] it returns 1 for minOpt + [ TEST ] it returns [1], Ø for extractMinOpt [ PASS ] it returns [1], Ø for extractMinOpt + [ TEST ] it returns 1, Ø for extractMin [ PASS ] it returns 1, Ø for extractMin + end  + begin insert (fromList [0]) 0 + [ TEST ] it is equivalent to 'insert (empty) 0' [ PASS ] it is equivalent to 'insert (empty) 0' + end  + begin fromList [1,0] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 2 [ PASS ] it has size 2 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], .. for extractMinOpt [ PASS ] it returns [0], .. for extractMinOpt + [ TEST ] it returns 0, .. for extractMin [ PASS ] it returns 0, .. for extractMin + end  + begin fromList [0,1] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 2 [ PASS ] it has size 2 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], .. for extractMinOpt [ PASS ] it returns [0], .. for extractMinOpt + [ TEST ] it returns 0, .. for extractMin [ PASS ] it returns 0, .. for extractMin + end  + begin insert (fromList [1,2]) 1 + [ TEST ] it is equivalent to 'fromList [1,2]' [ PASS ] it is equivalent to 'fromList [1,2]' + end  + begin insert (fromList [1,2]) 2 + [ TEST ] it is equivalent to 'fromList [1,2]' [ PASS ] it is equivalent to 'fromList [1,2]' + end  + begin fromList [1,0,2] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 2 [ PASS ] it has depth 2 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], .. for extractMinOpt [ PASS ] it returns [0], .. for extractMinOpt + [ TEST ] it returns 0, .. for extractMin [ PASS ] it returns 0, .. for extractMin + end  + begin fromList [0,1,2] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 3 [ PASS ] it has depth 3 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], .. for extractMinOpt [ PASS ] it returns [0], .. for extractMinOpt + [ TEST ] it returns 0, .. for extractMin [ PASS ] it returns 0, .. for extractMin + end  + begin fromList [2,1,0] + [ TEST ] it is not null [ PASS ] it is not null + [ TEST ] it has size 3 [ PASS ] it has size 3 + [ TEST ] it has depth 3 [ PASS ] it has depth 3 + [ TEST ] it contains 0 [ PASS ] it contains 0 + [ TEST ] it contains 1 [ PASS ] it contains 1 + [ TEST ] it contains 2 [ PASS ] it contains 2 + [ TEST ] it returns [0] for minOpt [ PASS ] it returns [0] for minOpt + [ TEST ] it returns 0 for min [ PASS ] it returns 0 for min + [ TEST ] it returns [0], .. for extractMinOpt [ PASS ] it returns [0], .. for extractMinOpt + [ TEST ] it returns 0, .. for extractMin [ PASS ] it returns 0, .. for extractMin + end  + begin remove (fromList [0]) 0 + [ TEST ] it is equivalent to 'empty' [ PASS ] it is equivalent to 'empty' + end  + begin remove (fromList [1]) 0 + [ TEST ] it is equivalent to itself [ PASS ] it is equivalent to itself + end  + begin remove (fromList [1]) 2 + [ TEST ] it is equivalent to itself [ PASS ] it is equivalent to itself + end  + begin remove (fromList [1,0,2]) 0 + [ TEST ] it is equivalent to 'fromList [1,2]' [ PASS ] it is equivalent to 'fromList [1,2]' + end  + begin remove (fromList [1,0,2]) 1 + [ TEST ] it is equivalent to 'fromList [2,0]' [ PASS ] it is equivalent to 'fromList [2,0]' + end  + begin remove (fromList [1,0,2]) 2 + [ TEST ] it is equivalent to 'fromList [1,0]' [ PASS ] it is equivalent to 'fromList [1,0]' + end  + begin foldl + [ TEST ] it folds fromList [] [ PASS ] it folds fromList [] + [ TEST ] it folds fromList [1] [ PASS ] it folds fromList [1] + [ TEST ] it folds fromList [2,1] [ PASS ] it folds fromList [2,1] + [ TEST ] it folds fromList [2,1,3] [ PASS ] it folds fromList [2,1,3] + end  + begin foldr + [ TEST ] it folds fromList [] [ PASS ] it folds fromList [] + [ TEST ] it folds fromList [1] [ PASS ] it folds fromList [1] + [ TEST ] it folds fromList [2,1] [ PASS ] it folds fromList [2,1] + [ TEST ] it folds fromList [2,1,3] [ PASS ] it folds fromList [2,1,3] + end  + begin map + [ TEST ] it mirrors > (x -> x) (fromList [2,1]) [ PASS ] it mirrors > (x -> x) (fromList [2,1]) + [ TEST ] it maps keys > (x -> -x) (fromList [2,1]) [ PASS ] it maps keys > (x -> -x) (fromList [2,1]) + end  + begin keys, values, and toList + [ TEST ] it converts [] [ PASS ] it converts [] + [ TEST ] it converts [0] [ PASS ] it converts [0] + [ TEST ] it converts [1,0] [ PASS ] it converts [1,0] + [ TEST ] it converts [1,0,2] [ PASS ] it converts [1,0,2] + end  +end  + +Total: 99 +Passes: 99 +>>> Main thread finished with value: true@{}%{} diff --git a/tests/lib/Set.trp b/tests/lib/Set.trp new file mode 100644 index 00000000..388792a1 --- /dev/null +++ b/tests/lib/Set.trp @@ -0,0 +1,261 @@ +import Unit +import Set + +let (* Simple less-than comparator. *) + fun lt a b = a < b + fun gt a b = a > b + + val empty = Set.empty lt + val singleton = Set.singleton lt + val fromList = Set.fromList lt + + val tests = Unit.group "Set" [ + (* -- Empty, size, and findOpt (mem, find) -- *) + let val m = empty + in Unit.group "empty" [ + Unit.it "is null" (Unit.isTrue (Set.null m)) + , Unit.it "has size 0" (Unit.isEq 0 (Set.size m)) + , Unit.it "has depth 0" (Unit.isEq 0 (Set.depth m)) + , Unit.it "does not contain 0" (Unit.isFalse (Set.mem m 0)) + , Unit.it "does not contain 1" (Unit.isFalse (Set.mem m 1)) + , Unit.it "is not equal to 'empty >'" (Unit.isNeq (Set.empty gt) m) + , Unit.it "returns [] for minOpt" (Unit.isEq [] (Set.minOpt m)) + , Unit.it "returns [], Ø for extractMinOpt" (Unit.isEq ([], empty) (Set.extractMinOpt m)) + ] + end, + (* -- Insert / Singleton, size, and findOpt (mem, find) -- *) + let val m = Set.insert (empty) 0 + in Unit.group "insert (empty) 0" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 1" (Unit.isEq 1 (Set.size m)) + , Unit.it "has depth 1" (Unit.isEq 1 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "does not contain 1" (Unit.isFalse (Set.mem m 1)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], Ø for extractMinOpt" + (Unit.isEq ([0], empty) (Set.extractMinOpt m)) + , Unit.it "returns 0, Ø for extractMin" + (Unit.isEq (0, empty) (Set.extractMin m)) + ] + end, + let val m = singleton 0 + in Unit.group "singleton 0 42" [ + Unit.it "is equivalent to 'insert (empty) 0'" (Unit.isEq (Set.insert empty 0) m) + ] + end, + (* -- Insert (fromList), size, findOpt (mem, find), min / extractMin -- *) + let val m = fromList [0] + in Unit.group "fromList [(0,42)]" [ + Unit.it "is equivalent to 'insert (empty) 0'" (Unit.isEq (Set.insert empty 0) m) + ] + end, + let val m = fromList [1] + in Unit.group "fromList [1]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 1" (Unit.isEq 1 (Set.size m)) + , Unit.it "has depth 1" (Unit.isEq 1 (Set.depth m)) + , Unit.it "does not contain 0" (Unit.isFalse (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "returns [1] for minOpt" (Unit.isEq [1] (Set.minOpt m)) + , Unit.it "returns 1 for minOpt" (Unit.isEq 1 (Set.min m)) + , Unit.it "returns [1], Ø for extractMinOpt" + (Unit.isEq ([1], empty) (Set.extractMinOpt m)) + , Unit.it "returns 1, Ø for extractMin" + (Unit.isEq (1, empty) (Set.extractMin m)) + ] + end, + let val m = Set.insert (fromList [0]) 0 + in Unit.group "insert (fromList [0]) 0" [ + Unit.it "is equivalent to 'insert (empty) 0'" (Unit.isEq (Set.insert empty 0) m) + ] + end, + let val m = fromList [1,0] + in Unit.group "fromList [1,0]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 2" (Unit.isEq 2 (Set.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], .. for extractMinOpt" + (Unit.isEq ([0], fromList [1]) (Set.extractMinOpt m)) + , Unit.it "returns 0, .. for extractMin" + (Unit.isEq (0, fromList [1]) (Set.extractMin m)) + ] + end, + let val m = fromList [0,1] + in Unit.group "fromList [0,1]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 2" (Unit.isEq 2 (Set.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], .. for extractMinOpt" + (Unit.isEq ([0], fromList [1]) (Set.extractMinOpt m)) + , Unit.it "returns 0, .. for extractMin" + (Unit.isEq (0, fromList [1]) (Set.extractMin m)) + ] + end, + let val m = Set.insert (fromList [1,2]) 1 + in Unit.group "insert (fromList [1,2]) 1" [ + Unit.it "is equivalent to 'fromList [1,2]'" (Unit.isEq (fromList [1,2]) m) + ] + end, + let val m = Set.insert (fromList [1,2]) 2 + in Unit.group "insert (fromList [1,2]) 2" [ + Unit.it "is equivalent to 'fromList [1,2]'" (Unit.isEq (fromList [1,2]) m) + ] + end, + let val m = fromList [1,0,2] + in Unit.group "fromList [1,0,2]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Set.size m)) + , Unit.it "has depth 2" (Unit.isEq 2 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "contains 2" (Unit.isTrue (Set.mem m 2)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], .. for extractMinOpt" + (Unit.isEq ([0], fromList [1,2]) (Set.extractMinOpt m)) + , Unit.it "returns 0, .. for extractMin" + (Unit.isEq (0, fromList [1,2]) (Set.extractMin m)) + ] + end, + let val m = fromList [0,1,2] + in Unit.group "fromList [0,1,2]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Set.size m)) + , Unit.it "has depth 3" (Unit.isEq 3 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "contains 2" (Unit.isTrue (Set.mem m 2)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], .. for extractMinOpt" + (Unit.isEq ([0], fromList [1,2]) (Set.extractMinOpt m)) + , Unit.it "returns 0, .. for extractMin" + (Unit.isEq (0, fromList [1,2]) (Set.extractMin m)) + ] + end, + let val m = fromList [2,1,0] + in Unit.group "fromList [2,1,0]" [ + Unit.it "is not null" (Unit.isFalse (Set.null m)) + , Unit.it "has size 3" (Unit.isEq 3 (Set.size m)) + , Unit.it "has depth 3" (Unit.isEq 3 (Set.depth m)) + , Unit.it "contains 0" (Unit.isTrue (Set.mem m 0)) + , Unit.it "contains 1" (Unit.isTrue (Set.mem m 1)) + , Unit.it "contains 2" (Unit.isTrue (Set.mem m 2)) + , Unit.it "returns [0] for minOpt" (Unit.isEq [0] (Set.minOpt m)) + , Unit.it "returns 0 for min" (Unit.isEq 0 (Set.min m)) + , Unit.it "returns [0], .. for extractMinOpt" + (Unit.isEq ([0], fromList [2,1]) (Set.extractMinOpt m)) + , Unit.it "returns 0, .. for extractMin" + (Unit.isEq (0, fromList [2,1]) (Set.extractMin m)) + ] + end, + (* -- Insert (fromList), delete, size, and findOpt (mem and find) -- *) + let val m = Set.remove (fromList [0]) 0 + in Unit.group "remove (fromList [0]) 0" [ + Unit.it "is equivalent to 'empty'" (Unit.isEq empty m) + ] + end, + let val m = fromList [1] + val m' = Set.remove m 0 + in Unit.group "remove (fromList [1]) 0" [ + Unit.it "is equivalent to itself" (Unit.isEq m m') + ] + end, + let val m = fromList [1] + val m' = Set.remove m 2 + in Unit.group "remove (fromList [1]) 2" [ + Unit.it "is equivalent to itself" (Unit.isEq m m') + ] + end, + let val m = Set.remove (fromList [1,0,2]) 0 + in Unit.group "remove (fromList [1,0,2]) 0" [ + Unit.it "is equivalent to 'fromList [1,2]'" + (Unit.isEq (fromList [1,2]) m) + ] + end, + let val m = Set.remove (fromList [1,0,2]) 1 + in Unit.group "remove (fromList [1,0,2]) 1" [ + Unit.it "is equivalent to 'fromList [2,0]'" + (Unit.isEq (fromList [2,0]) m) + ] + end, + let val m = Set.remove (fromList [1,0,2]) 2 + in Unit.group "remove (fromList [1,0,2]) 2" [ + Unit.it "is equivalent to 'fromList [1,0]'" + (Unit.isEq (fromList [1,0]) m) + ] + end, + (* -- foldl, foldlr, and its derivatives: map, keys, values, and toList -- *) + let fun f (x,xs) = x::xs + in Unit.group "foldl" [ + Unit.it "folds fromList []" + (Unit.isEq [] (Set.foldl f [] (fromList []))) + , Unit.it "folds fromList [1]" + (Unit.isEq [1] (Set.foldl f [] (fromList [1]))) + , Unit.it "folds fromList [2,1]" + (Unit.isEq [2,1] (Set.foldl f [] (fromList [2,1]))) + , Unit.it "folds fromList [2,1,3]" + (Unit.isEq [3,2,1] (Set.foldl f [] (fromList [2,1,3]))) + ] + end, + let fun f (x,xs) = x::xs + in Unit.group "foldr" [ + Unit.it "folds fromList []" + (Unit.isEq [] (Set.foldr f [] (fromList []))) + , Unit.it "folds fromList [1]" + (Unit.isEq [1] (Set.foldr f [] (fromList [1]))) + , Unit.it "folds fromList [2,1]" + (Unit.isEq [1,2] (Set.foldr f [] (fromList [2,1]))) + , Unit.it "folds fromList [2,1,3]" + (Unit.isEq [1,2,3] (Set.foldr f [] (fromList [2,1,3]))) + ] + end, + Unit.group "map" [ + Unit.it "mirrors > (x -> x) (fromList [2,1])" + (Unit.isEq (Set.fromList gt [1,2]) + (Set.map gt (fn x => x) (fromList [2,1]))) + , Unit.it "maps keys > (x -> -x) (fromList [2,1])" + (Unit.isEq (Set.fromList gt [-1, -2]) + (Set.map gt (fn x => -x) (fromList [2,1]))) + ], + Unit.group "keys, values, and toList" [ + let val s = fromList [] + in Unit.it "converts []" [ + (Unit.isEq [] (Set.toDescList s)) + , (Unit.isEq [] (Set.toAscList s)) + , (Unit.isEq [] (Set.toList s)) + ] + end + , let val s = fromList [0] + in Unit.it "converts [0]" [ + (Unit.isEq [0] (Set.toDescList s)) + , (Unit.isEq [0] (Set.toAscList s)) + , (Unit.isEq [0] (Set.toList s)) + ] + end + , let val s = fromList [1,0] + in Unit.it "converts [1,0]" [ + (Unit.isEq [1,0] (Set.toDescList s)) + , (Unit.isEq [0,1] (Set.toAscList s)) + , (Unit.isEq [0,1] (Set.toList s)) + ] + end + , let val s = fromList [1,0,2] + in Unit.it "converts [1,0,2]" [ + (Unit.isEq [2,1,0] (Set.toDescList s)) + , (Unit.isEq [0,1,2] (Set.toAscList s)) + , (Unit.isEq [0,1,2] (Set.toList s)) + ] + end + ] + ] +in Unit.run authority tests end