-- tree.hs: conjuring functions over trees
--
-- Copyright (C) 2021-2025 Rudy Matela
-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
{-# LANGUAGE CPP, TemplateHaskell #-}
import Conjure
import Test.LeanCheck
import Data.Express hiding (height,size)
data Tree = Leaf
| Node Tree Int Tree
deriving (Eq, Ord, Show, Read)
deriveExpress ''Tree
unit :: Int -> Tree
unit x = Node Leaf x Leaf
nil :: Tree -> Bool
nil Leaf = True
nil _ = False
left :: Tree -> Tree
left (Node l _ _) = l
right :: Tree -> Tree
right (Node _ _ r) = r
valu :: Tree -> Int
valu (Node _ x _) = x
leftmost :: Tree -> Int
leftmost (Node l x _) = if nil l then x else leftmost l
rightmost :: Tree -> Int
rightmost (Node _ x r) = if nil r then x else rightmost r
height :: Tree -> Int
height Leaf = -1
height (Node l _ r) = 1 + max (height l) (height r)
size :: Tree -> Int
size Leaf = 0
size (Node l _ r) = size l + 1 + size r
ordered :: Tree -> Bool
ordered Leaf = True
ordered (Node l x r) = (nil l || rightmost l < x)
&& (nil r || x < leftmost r)
&& ordered l
&& ordered r
preorder :: Tree -> [Int]
preorder Leaf = []
preorder (Node l x r) = [x] ++ preorder l ++ preorder r
inorder :: Tree -> [Int]
inorder Leaf = []
inorder (Node l x r) = inorder l ++ [x] ++ inorder r
posorder :: Tree -> [Int]
posorder Leaf = []
posorder (Node l x r) = posorder l ++ posorder r ++ [x]
-- this mem searches both sides of the tree
mem :: Int -> Tree -> Bool
mem _ Leaf = False
mem y (Node l x r) = y == x || mem y l || mem y r
instance Listable Tree where
tiers = cons0 Leaf
\/ cons3 Node
instance Name Tree where
name _ = "t1"
-- the following instance could have been derived with:
-- deriveConjurable ''Tree
instance Conjurable Tree where
conjureExpress = reifyExpress
conjureEquality = reifyEquality
conjureTiers = reifyTiers
conjureSubTypes x = conjureType (undefined :: Int)
conjureSize = size
conjureCases t = [ val (Leaf -: t)
, value "Node" (Node ->>>: t) :$ hole l :$ hole x :$ hole r
]
where
Node l x r = Node undefined undefined undefined -: t
main :: IO ()
main = do
conjure "leftmost" leftmost
[ prim "undefined" (undefined :: Int)
, prif (undefined :: Int)
, prim "nil" nil
]
conjure "rightmost" rightmost
[ prim "undefined" (undefined :: Int)
, prif (undefined :: Int)
, prim "nil" nil
]
conjure "size" size
[ pr (0 :: Int)
, pr (1 :: Int)
, prim "+" ((+) :: Int -> Int -> Int)
, prim "nil" nil
]
conjure "height" height
[ pr (0 :: Int)
, pr (1 :: Int)
, pr (-1 :: Int)
, prim "+" ((+) :: Int -> Int -> Int)
, prim "max" (max :: Int -> Int -> Int)
, prim "nil" nil
]
conjure "mem" mem
[ pr False
, prim "||" (||)
, prim "==" ((==) :: Int -> Int -> Bool)
]
-- unreachable: needs size 22 but OOMs at 19/20 (v0.5.16)
conjureWithMaxSize 12 "ordered" ordered
[ pr True
, pr False
, prim "&&" (&&)
, prim "||" (||)
, prim "<" ((<) :: Int -> Int -> Bool)
, prim "rightmost" rightmost
, prim "leftmost" leftmost
, prim "nil" nil
]
conjure "ordered" ordered
[ prim "strictlyOrdered" (strictlyOrdered :: [Int] -> Bool)
, prim "inorder" inorder
]
conjureWithMaxSize 12 "preorder" preorder
[ pr ([] :: [Int])
, prim ":" ((:) :: Int -> [Int] -> [Int])
, prim "++" ((++) :: [Int] -> [Int] -> [Int])
]
conjureWithMaxSize 12 "inorder" inorder
[ pr ([] :: [Int])
, prim ":" ((:) :: Int -> [Int] -> [Int])
, prim "++" ((++) :: [Int] -> [Int] -> [Int])
]
conjureWithMaxSize 12 "posorder" posorder
[ pr ([] :: [Int])
, prim ":" ((:) :: Int -> [Int] -> [Int])
, prim "++" ((++) :: [Int] -> [Int] -> [Int])
]
strictlyOrdered :: [Int] -> Bool
strictlyOrdered [] = True
strictlyOrdered (x:xs) = (null xs || x < head xs) && strictlyOrdered xs