{-# LANGUAGE BangPatterns, RecordWildCards #-}
module Data.RangeMin.Int.Catalan (CatType, catalanIndexer) where

import Data.RangeMin.Common
import qualified Data.RangeMin.Fusion as F
import qualified Data.RangeMin.Int.Quadratic as N2 
import qualified Data.RangeMin.Int.Linearithmic as Nlogn
import Data.RangeMin.Int.Catalan.Combinators
import Data.RangeMin.Int.Catalan.Table
import Prelude hiding (drop, read)

data IL = {-# UNPACK #-} !Value :< IL | Nil

-- | There are @'catalan' n n@ distinct binary trees of size @n@.
-- A value of type 'CatType' is the index of a particular binary
-- tree in the range @0..'catalan' n n - 1@.
type CatType = Int

data C = C {-# UNPACK #-} !CatType {-# UNPACK #-} !Index {-# UNPACK #-} !Value

catalanIndexer :: PVector Value -> Length -> (PVector CatType, VVector RM, PVector Index, PVector Value)
catalanIndexer !xs !bS = (catIxs0, typeRMs0, minIxs0, minVals0)
  where	!n = vlength xs
	!m = (n + bS - 1) `div'` bS
	!mm = m - 1
	rmAlgo = N2.rangeMin
	lastBlock = drop (bS * mm) xs
	block b = slice (b * bS) bS xs
	!(!catIxs0, !minIxs0, !minVals0) = catalans `seq`
	  let off b (C cum mi vmi) = (cum, mi + bS * b, vmi)
	      !lastCat = off mm (catalanIndex lastBlock) 
	      catalanIndex !vec = scan 0 (bS-1) bS 0 0 maxBound Nil where
		!n = vlength vec
		scan !y !p !q !cum !mi !vmi !ys
		  | y < n	= let
			vy = vec ! y
			catp = catalan p
			scan' !q !cum xs0@(vx :< xs)
			  | vx > vy
			    = scan' (q-1) (cum + catp q) xs
			  | otherwise
			    = scan (y+1) (p-1) q cum mi vmi (vy :< xs0)
			scan' !q !cum ys
			  = scan (y+1) (p-1) q cum y vy (vy :< ys)
			in scan' q cum ys
		  | otherwise = C cum mi vmi
	  in F.unzip3 (F.generate mm (\ b -> off b (catalanIndex (block b))) `F.snoc` lastCat)
	!typeRMs0 = let lastMin = (catIxs0 ! mm, rmAlgo lastBlock) in 
	  equivClasses (catalan bS bS) (F.generate (m-1) (\ b -> (catIxs0 ! b, rmAlgo (block b))) `F.snoc` lastMin)