module Data.RangeMin.Int.Linear (rangeMin) where
import Data.RangeMin.Int.Catalan
import Data.RangeMin.Common
import qualified Data.Vector.Primitive as UV
import qualified Data.RangeMin.Int.NearLinear as NearN
import qualified Data.RangeMin.Int.Linearithmic as Nlogn
rangeMin :: UV.Vector Int -> RM
rangeMin !xs = let
!blockMinVals = unfold (generateUnf m ((xs !) . (blockMins !)))
!multiBlockRM0
| not forceNLogN && m <= nearNCross
= NearN.rangeMin blockMinVals
| otherwise
= Nlogn.rangeMin blockMinVals
multiBlockRM !bI !bJ = blockMins ! runRM multiBlockRM0 bI (bJ bI)
in toRM $ \ i d ->
let j = i + d
bI = i `div'` bS
bJ = j `div'` bS
xI = i `mod'` bS
xJ = j `mod'` bS
in case (xI, xJ) of
(0, 0) -> multiBlockRM bI bJ
(0, _) | d < bS
-> blockMin bI 0 d
| otherwise
-> multiBlockRM bI bJ `mix` blockMin bJ 0 xJ
(_, 0) | d < bS
-> blockMin bI xI bS
| otherwise
-> blockMin bI xI bS `mix` multiBlockRM (bI + 1) bJ
_ -> case bJ bI of
0 -> blockMin bI xI xJ
1 -> blockMin bI xI bS `mix` blockMin bJ 0 xJ
_ -> blockMin bI xI bS `mix` blockMin bJ 0 xJ `mix`
multiBlockRM (bI + 1) bJ
where !n = UV.length xs
!bS = ceilLog n `div'` 4 + 1
!m = (n + bS 1) `div'` bS
!(!blockTypes, !typeRMs, !blockMins) = catalanIndexer xs bS
blockMin !b !i !j = runRM (typeRMs ! (blockTypes ! b)) i (j i) + bS * b
mix = minIndex xs