{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}
module Math.Combinat.LatticePaths where
import Data.List
import System.Random
import Math.Combinat.Classes
import Math.Combinat.Numbers
import Math.Combinat.Trees.Binary
import Math.Combinat.ASCII as ASCII
data Step
= UpStep
| DownStep
deriving (Eq,Ord,Show)
type LatticePath = [Step]
asciiPath :: LatticePath -> ASCII
asciiPath p = asciiFromLines $ transpose (go 0 p) where
go !h [] = []
go !h (x:xs) = case x of
UpStep -> ee h x : go (h+1) xs
DownStep -> ee (h-1) x : go (h-1) xs
maxh = pathHeight p
ee h x = replicate (maxh-h-1) ' ' ++ [ch x] ++ replicate h ' '
ch x = case x of
UpStep -> '/'
DownStep -> '\\'
instance DrawASCII LatticePath where
ascii = asciiPath
isValidPath :: LatticePath -> Bool
isValidPath = go 0 where
go :: Int -> LatticePath -> Bool
go !y [] = y>=0
go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }
in if y'<0 then False
else go y' ts
isDyckPath :: LatticePath -> Bool
isDyckPath = go 0 where
go :: Int -> LatticePath -> Bool
go !y [] = y==0
go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }
in if y'<0 then False
else go y' ts
pathHeight :: LatticePath -> Int
pathHeight = go 0 0 where
go :: Int -> Int -> LatticePath -> Int
go !h !y [] = h
go !h !y (t:ts) = case t of
UpStep -> go (max h (y+1)) (y+1) ts
DownStep -> go h (y-1) ts
instance HasHeight LatticePath where
height = pathHeight
instance HasWidth LatticePath where
width = length
pathEndpoint :: LatticePath -> (Int,Int)
pathEndpoint = go 0 0 where
go :: Int -> Int -> LatticePath -> (Int,Int)
go !x !y [] = (x,y)
go !x !y (t:ts) = case t of
UpStep -> go (x+1) (y+1) ts
DownStep -> go (x+1) (y-1) ts
pathCoordinates :: LatticePath -> [(Int,Int)]
pathCoordinates = go 0 0 where
go :: Int -> Int -> LatticePath -> [(Int,Int)]
go _ _ [] = []
go !x !y (t:ts) = let x' = x + 1
y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }
in (x',y') : go x' y' ts
pathNumberOfUpSteps :: LatticePath -> Int
pathNumberOfUpSteps = fst . pathNumberOfUpDownSteps
pathNumberOfDownSteps :: LatticePath -> Int
pathNumberOfDownSteps = snd . pathNumberOfUpDownSteps
pathNumberOfUpDownSteps :: LatticePath -> (Int,Int)
pathNumberOfUpDownSteps = go 0 0 where
go :: Int -> Int -> LatticePath -> (Int,Int)
go !u !d (p:ps) = case p of
UpStep -> go (u+1) d ps
DownStep -> go u (d+1) ps
go !u !d [] = (u,d)
pathNumberOfPeaks :: LatticePath -> Int
pathNumberOfPeaks = go 0 where
go :: Int -> LatticePath -> Int
go !k (x:xs@(y:_)) = go (if x==UpStep && y==DownStep then k+1 else k) xs
go !k [x] = k
go !k [ ] = k
pathNumberOfZeroTouches :: LatticePath -> Int
pathNumberOfZeroTouches = pathNumberOfTouches' 0
pathNumberOfTouches'
:: Int
-> LatticePath -> Int
pathNumberOfTouches' h = go 0 0 0 where
go :: Int -> Int -> Int -> LatticePath -> Int
go !cnt _ _ [] = cnt
go !cnt !x !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }
cnt' = if y'==h then cnt+1 else cnt
in go cnt' (x+1) y' ts
dyckPaths :: Int -> [LatticePath]
dyckPaths = map nestedParensToDyckPath . nestedParentheses
dyckPathsNaive :: Int -> [LatticePath]
dyckPathsNaive = worker where
worker 0 = [[]]
worker m = as ++ bs where
as = [ bracket p | p <- worker (m-1) ]
bs = [ bracket p ++ q | k <- [1..m-1] , p <- worker (k-1) , q <- worker (m-k) ]
bracket p = UpStep : p ++ [DownStep]
countDyckPaths :: Int -> Integer
countDyckPaths m = catalan m
nestedParensToDyckPath :: [Paren] -> LatticePath
nestedParensToDyckPath = map f where
f p = case p of { LeftParen -> UpStep ; RightParen -> DownStep }
dyckPathToNestedParens :: LatticePath -> [Paren]
dyckPathToNestedParens = map g where
g s = case s of { UpStep -> LeftParen ; DownStep -> RightParen }
boundedDyckPaths
:: Int
-> Int
-> [LatticePath]
boundedDyckPaths = boundedDyckPathsNaive
boundedDyckPathsNaive
:: Int
-> Int
-> [LatticePath]
boundedDyckPathsNaive = worker where
worker !h !m
| h<0 = []
| m<0 = []
| m==0 = [[]]
| h<=0 = []
| otherwise = as ++ bs
where
bracket p = UpStep : p ++ [DownStep]
as = [ bracket p | p <- boundedDyckPaths (h-1) (m-1) ]
bs = [ bracket p ++ q | k <- [1..m-1] , p <- boundedDyckPaths (h-1) (k-1) , q <- boundedDyckPaths h (m-k) ]
latticePaths :: (Int,Int) -> [LatticePath]
latticePaths = latticePathsNaive
latticePathsNaive :: (Int,Int) -> [LatticePath]
latticePathsNaive (x,y) = worker x y where
worker !x !y
| odd (x-y) = []
| x<0 = []
| y<0 = []
| y==0 = dyckPaths (div x 2)
| x==1 && y==1 = [[UpStep]]
| otherwise = as ++ bs
where
bracket p = UpStep : p ++ [DownStep]
as = [ UpStep : p | p <- worker (x-1) (y-1) ]
bs = [ bracket p ++ q | k <- [1..(div x 2)] , p <- dyckPaths (k-1) , q <- worker (x-2*k) y ]
countLatticePaths :: (Int,Int) -> Integer
countLatticePaths (x,y)
| even (x+y) = catalanTriangle (div (x+y) 2) (div (x-y) 2)
| otherwise = 0
touchingDyckPaths
:: Int
-> Int
-> [LatticePath]
touchingDyckPaths = touchingDyckPathsNaive
touchingDyckPathsNaive
:: Int
-> Int
-> [LatticePath]
touchingDyckPathsNaive = worker where
worker !k !m
| m == 0 = if k==0 then [[]] else []
| k <= 0 = []
| m < 0 = []
| k == 1 = [ bracket p | p <- dyckPaths (m-1) ]
| otherwise = [ bracket p ++ q | l <- [1..m-1] , p <- dyckPaths (l-1) , q <- worker (k-1) (m-l) ]
where
bracket p = UpStep : p ++ [DownStep]
countTouchingDyckPaths
:: Int
-> Int
-> Integer
countTouchingDyckPaths t n
| t==0 && n==0 = 1
| otherwise = countLatticePaths (2*n-t-1,t-1)
peakingDyckPaths
:: Int
-> Int
-> [LatticePath]
peakingDyckPaths = peakingDyckPathsNaive
peakingDyckPathsNaive
:: Int
-> Int
-> [LatticePath]
peakingDyckPathsNaive = worker where
worker !k !m
| m == 0 = if k==0 then [[]] else []
| k <= 0 = []
| m < 0 = []
| k == 1 = [ singlePeak m ]
| otherwise = as ++ bs ++ cs
where
as = [ bracket p | p <- worker k (m-1) ]
bs = [ smallHill ++ q | q <- worker (k-1) (m-1) ]
cs = [ bracket p ++ q | l <- [2..m-1] , a <- [1..k-1] , p <- worker a (l-1) , q <- worker (k-a) (m-l) ]
smallHill = [ UpStep , DownStep ]
singlePeak !m = replicate m UpStep ++ replicate m DownStep
bracket p = UpStep : p ++ [DownStep]
countPeakingDyckPaths
:: Int
-> Int
-> Integer
countPeakingDyckPaths k m
| m == 0 = if k==0 then 1 else 0
| k <= 0 = 0
| m < 0 = 0
| k == 1 = 1
| otherwise = div (binomial m k * binomial m (k-1)) (fromIntegral m)
randomDyckPath :: RandomGen g => Int -> g -> (LatticePath,g)
randomDyckPath m g0 = (nestedParensToDyckPath parens, g1) where
(parens,g1) = randomNestedParentheses m g0