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

-- import Control.Monad
-- import Data.RangeMin.ST
-- import Data.RangeMin.Common
import Data.RangeMin.Common
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as UV
-- import qualified Data.Vector.Fusion.Stream as S
import qualified Data.RangeMin.Int.Quadratic as N2 
import qualified Data.RangeMin.Int.Linearithmic as Nlogn
-- import qualified Data.Vector.Generic as G
-- import qualified Data.RangeMin.IPVector as IP
import qualified Data.Vector.Primitive as PV
-- import Data.RangeMin.IPVector (IP (..))
import Data.RangeMin.Int.Catalan.Combinators
import Data.RangeMin.Int.Catalan.Table
import Prelude hiding (drop, read)

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

{-# INLINE [0] catalanIndex #-}
catalanIndex :: UV.Vector Int -> Int -> IP
catalanIndex !vec !bS = catalans `seq` scan 0 (bS-1) 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' (q-1) (cum + catp q) xs
			| otherwise
			  = scan (y+1) (p-1) q cum mi (vy :< xs0)
		scan' !q !cum ys
			= scan (y+1) (p-1) 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