combinat-0.1: Math/Combinat/Trees.hs
-- | Trees, forests, etc. See:
-- Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 4A.
module Math.Combinat.Trees
( -- * Types
BinTree(..)
, leaf
, module Data.Tree
, Paren(..)
, parenthesesToString
, stringToParentheses
-- * Bijections
, forestToNestedParentheses
, forestToBinaryTree
, nestedParenthesesToForest
, nestedParenthesesToForestUnsafe
, nestedParenthesesToBinaryTree
, nestedParenthesesToBinaryTreeUnsafe
, binaryTreeToForest
, binaryTreeToNestedParentheses
-- * Nested parentheses
, nestedParentheses
, fasc4A_algorithm_P
-- * Binary trees
, binaryTrees
, countBinaryTrees
, binaryTreesNaive
)
where
import Data.List
import Data.Tree (Tree(..),Forest(..))
import Math.Combinat.Helper
-------------------------------------------------------
-- * Types
data BinTree a
= Branch (BinTree a) (BinTree a)
| Leaf a
deriving (Eq,Ord,Show,Read)
leaf :: BinTree ()
leaf = Leaf ()
-------------------------------------------------------
data Paren = LeftParen | RightParen deriving (Eq,Ord,Show,Read)
parenToChar :: Paren -> Char
parenToChar LeftParen = '('
parenToChar RightParen = ')'
parenthesesToString :: [Paren] -> String
parenthesesToString = map parenToChar
stringToParentheses :: String -> [Paren]
stringToParentheses [] = []
stringToParentheses (x:xs) = p : stringToParentheses xs where
p = case x of
'(' -> LeftParen
')' -> RightParen
_ -> error "stringToParentheses: invalid character"
-------------------------------------------------------
-- * Bijections
forestToNestedParentheses :: Forest a -> [Paren]
forestToNestedParentheses = forest where
-- forest :: Forest a -> [Paren]
forest = concatMap tree
-- tree :: Tree a -> [Paren]
tree (Node _ sf) = LeftParen : forest sf ++ [RightParen]
forestToBinaryTree :: Forest a -> BinTree ()
forestToBinaryTree = forest where
-- forest :: Forest a -> BinTree ()
forest = foldr Branch leaf . map tree
-- tree :: Tree a -> BinTree ()
tree (Node _ sf) = case sf of
[] -> leaf
_ -> forest sf
nestedParenthesesToForest :: [Paren] -> Maybe (Forest ())
nestedParenthesesToForest ps =
case parseForest ps of
(rest,forest) -> case rest of
[] -> Just forest
_ -> Nothing
where
parseForest :: [Paren] -> ( [Paren] , Forest () )
parseForest ps = unfoldEither parseTree ps
parseTree :: [Paren] -> Either [Paren] ( [Paren] , Tree () )
parseTree orig@(LeftParen:ps) = let (rest,ts) = parseForest ps in case rest of
(RightParen:qs) -> Right (qs, Node () ts)
_ -> Left orig
parseTree qs = Left qs
nestedParenthesesToForestUnsafe :: [Paren] -> Forest ()
nestedParenthesesToForestUnsafe = fromJust . nestedParenthesesToForest
nestedParenthesesToBinaryTree :: [Paren] -> Maybe (BinTree ())
nestedParenthesesToBinaryTree ps =
case parseForest ps of
(rest,forest) -> case rest of
[] -> Just forest
_ -> Nothing
where
parseForest :: [Paren] -> ( [Paren] , BinTree () )
parseForest ps = let (rest,ts) = unfoldEither parseTree ps in (rest , foldr Branch leaf ts)
parseTree :: [Paren] -> Either [Paren] ( [Paren] , BinTree () )
parseTree orig@(LeftParen:ps) = let (rest,ts) = parseForest ps in case rest of
(RightParen:qs) -> Right (qs, ts)
_ -> Left orig
parseTree qs = Left qs
nestedParenthesesToBinaryTreeUnsafe :: [Paren] -> BinTree ()
nestedParenthesesToBinaryTreeUnsafe = fromJust . nestedParenthesesToBinaryTree
binaryTreeToNestedParentheses :: BinTree a -> [Paren]
binaryTreeToNestedParentheses = worker where
worker (Branch l r) = LeftParen : worker l ++ RightParen : worker r
worker (Leaf _) = []
binaryTreeToForest :: BinTree a -> Forest ()
binaryTreeToForest = worker where
worker (Branch l r) = Node () (worker l) : worker r
worker (Leaf _) = []
-------------------------------------------------------
-- * Nested parentheses
-- | Synonym for 'fasc4A_algorithm_P'.
nestedParentheses :: Int -> [[Paren]]
nestedParentheses = fasc4A_algorithm_P
-- | Generates all sequences of nested parentheses of length 2n.
-- Order is lexigraphic (when right parentheses are considered
-- smaller then left ones).
-- Based on \"Algorithm P\" in Knuth, but less efficient because of
-- the \"idiomatic\" code.
fasc4A_algorithm_P :: Int -> [[Paren]]
fasc4A_algorithm_P 0 = []
fasc4A_algorithm_P 1 = [[LeftParen,RightParen]]
fasc4A_algorithm_P n = unfold next ( start , [] ) where
start = concat $ replicate n [RightParen,LeftParen] -- already reversed!
next :: ([Paren],[Paren]) -> ( [Paren] , Maybe ([Paren],[Paren]) )
next ( (a:b:ls) , [] ) = next ( ls , b:a:[] )
next ( lls@(l:ls) , rrs@(r:rs) ) = ( visit , new ) where
visit = reverse lls ++ rrs
new =
{- debug (reverse ls,l,r,rs) $ -}
case l of
RightParen -> Just ( ls , LeftParen:RightParen:rs )
LeftParen ->
{- debug ("---",reverse ls,l,r,rs) $ -}
findj ( lls , [] ) ( reverse (RightParen:rs) , [] )
findj :: ([Paren],[Paren]) -> ([Paren],[Paren]) -> Maybe ([Paren],[Paren])
findj ( [] , _ ) _ = Nothing
findj ( lls@(l:ls) , rs) ( xs , ys ) =
{- debug ((reverse ls,l,rs),(reverse xs,ys)) $ -}
case l of
LeftParen -> case xs of
(a:_:as) -> findj ( ls, RightParen:rs ) ( as , LeftParen:a:ys )
_ -> findj ( lls, [] ) ( reverse rs ++ xs , ys)
RightParen -> Just ( reverse ys ++ xs ++ reverse (LeftParen:rs) ++ ls , [] )
-------------------------------------------------------
-- * Binary trees
-- | Generates all binary trees with n nodes.
binaryTrees :: Int -> [BinTree ()]
binaryTrees = binaryTreesNaive
-- | # = Catalan(n) = \\frac { 1 } { n+1 } \\binom { 2n } { n }.
--
-- This is also the counting function for forests and nested parentheses.
countBinaryTrees :: Int -> Integer
countBinaryTrees n = binomial (2*n) n `div` (1 + fromIntegral n)
-- | Generates all binary trees with n nodes. The naive algorithm.
binaryTreesNaive :: Int -> [BinTree ()]
binaryTreesNaive 0 = [ leaf ]
binaryTreesNaive n =
[ Branch l r
| i <- [0..n-1]
, l <- binaryTreesNaive i
, r <- binaryTreesNaive (n-1-i)
]
----- binary tree zipper
data Ctx a
= Top
| L (Ctx a) (BinTree a)
| R (BinTree a) (Ctx a)
type Loc a = (BinTree a, Ctx a)
left :: Loc a -> Loc a
left (Branch l r , c) = (l , L c r)
left (Leaf _ , _) = error "left: Leaf"
right :: Loc a -> Loc a
right (Branch l r , c) = (r , R l c)
right (Leaf _ , _) = error "right: Leaf"
top :: BinTree a -> Loc a
top t = (t, Top)
up :: Loc a -> Loc a
up (t, L c r) = (Branch t r, c)
up (t, R l c) = (Branch l t, c)
up (t, Top ) = error "up: top"
upmost :: Loc a -> Loc a
upmost l@(t, Top) = l
upmost l = upmost (up l)
modify :: (BinTree a -> BinTree a) -> Loc a -> Loc a
modify f (t, c) = (f t, c)
-----
{-
-- | Generates all binary trees with n nodes.
-- Based on \"Algorithm B\" in Knuth, uses tree zippers.
fasc4A_algorithm_B :: Int -> [BinTree ()]
fasc4A_algorithm_B 0 = [ leaf ]
fasc4A_algorithm_B n = unfold1 next start where
start = nest n (\t -> Branch t leaf) leaf
killLeft (Branch _ r) = Branch leaf r
killRight (Branch l _) = Branch l leaf
next t = case findj (top t) of
Nothing -> Nothing
Just locj@(s,c) -> case findk (top s) of
lock@(u,Top) -> Just $ promote (modify killLeft locj ) lock
lock@(u,_ ) -> Just $ promote locj (modify killRight lock)
findj :: Loc () -> Maybe (Loc ())
findj (Branch (Leaf _) t , c) = case t of
Branch l r -> findj $ left (Branch t leaf , c)
Leaf _ -> Nothing
findj loc@(Leaf _ , c) = Just loc
findk :: Loc () -> Loc ()
findk loc@( Branch l (Leaf _) , _) = loc
findk loc@( Branch l r , _) = findk (right loc)
promote :: Loc () -> Loc () -> BinTree ()
promote locj lock = undefined
-}