module Data.RangeMin.Int.Catalan (catalanIndexer) where
import Data.RangeMin.Common
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as UV
import qualified Data.RangeMin.Int.Quadratic as N2
import qualified Data.RangeMin.Int.Linearithmic as Nlogn
import qualified Data.Vector.Primitive as PV
import Data.RangeMin.Int.Catalan.Combinators
import Data.RangeMin.Int.Catalan.Table
import Prelude hiding (drop, read)
data IL = !Int :< IL | Nil
catalanIndex :: UV.Vector Int -> Int -> IP
catalanIndex !vec !bS = catalans `seq` scan 0 (bS1) bS 0 0 Nil where
!n = UV.length vec
scan !y !p !q !cum !mi !ys
| y == n = IP cum mi
| otherwise = let
vy = vec ! y
catp = catalan p
scan' !q !cum xs0@(vx :< xs)
| vx > vy
= scan' (q1) (cum + catp q) xs
| otherwise
= scan (y+1) (p1) q cum mi (vy :< xs0)
scan' !q !cum ys
= scan (y+1) (p1) q cum y (vy :< ys)
in scan' q cum ys
catalanIndexer :: UV.Vector Int -> Int -> (PV.Vector Int, V.Vector RM, PV.Vector Int)
catalanIndexer !xs !bS = catalans `seq` (catIxs0, typeRMs0, minIxs0)
where !typeRMs0 = equivClasses cat $ V.generate m (\ b ->
(catIxs0 ! b, if b == mm then rmAlgo lastBlock else rmAlgo (block b)))
lastBlock = drop (bS * mm) xs
block b = slice (bS * b) bS xs
cat = catalan bS bS
off b (IP cum mi) = IP cum (mi + bS * b)
catIxs0 :: PV.Vector Int
!(!catIxs0, !minIxs0) = unzipIPM $ unfoldM $ generateUnf m
(\ b -> off b (catalanIndex (if b == mm then lastBlock else block b) bS))
!n = UV.length xs
!m = (n + bS 1) `div'` bS
!mm = m 1
rmAlgo | not forceBlockN2 && bS > n2Cross
= Nlogn.rangeMin
| otherwise
= N2.rangeMin