{-# LANGUAGE CPP #-}
module Data.RangeMin.Inject (
	Injective,
	unsafeInjectRangeMin,
	unsafeInjectRangeMax,
	injectRangeMin,
	injectRangeMax) where

import Data.RangeMin.Common
import Data.RangeMin.Int
import Data.RangeMin.Vector
import Data.RangeMin.Cartesian.Spec

import qualified Data.Vector.Primitive as PV
import qualified Data.Vector.Generic as G

#define injection An order-preserving function into 'Int'.  (The ordering on the elements is	\
	equivalent to @\\ x y -> f x '<=' f y@.)
#define injectDoc(inj,other,cmp)  /O(n)/.  @'inj' f xs@ is equivalent to			\
	@'other' (\\ x y -> f x 'cmp' f y) xs@, but is frequently much faster,			\
	fusing with the input vector and converting it directly to a @'PV.Vector' 'Int'@.

{-# INLINE unsafeInjectRangeMin #-}
-- | injectDoc(unsafeInjectRangeMin,unsafeVecRangeMinBy,<=) The returned function /does not/ do bounds checks;
-- see 'unsafeIntRangeMin' for details.
unsafeInjectRangeMin :: G.Vector v a =>
	(a -> Int)	-- ^ injection
	-> v a		-- ^ The input vector.
	-> RangeMin	-- ^ A range-min function on the elements (under the above ordering)
			-- which runs in /O(1)/.

{-# INLINE unsafeInjectRangeMax #-}
-- | injectDoc(unsafeInjectRangeMax,unsafeVecRangeMinBy,>=) The returned function /does not/ do bounds checks;
-- see 'unsafeIntRangeMin' for details.
unsafeInjectRangeMax :: G.Vector v a =>
	(a -> Int)	-- ^ injection
	-> v a		-- ^ The input vector.
	-> RangeMin	-- ^ A range-max function on the elements (under the above ordering)
			-- which runs in /O(1)/.

{-# INLINE injectRangeMin #-}
-- | injectDoc(injectRangeMin,vecRangeMinBy,<=) The returned function /does/ do bounds checks; see 'intRangeMin' for details.
injectRangeMin :: G.Vector v a =>
	(a -> Int)	-- ^ injection
	-> v a		-- ^ The input vector.
	-> RangeMin	-- ^ A range-min function on the elements (under the above ordering)
			-- which runs in /O(1)/.

{-# INLINE injectRangeMax #-}
-- | injectDoc(injectRangeMax,vecRangeMinBy,>=) The returned function /does/ do bounds checks; see 'intRangeMin' for details.
injectRangeMax :: G.Vector v a =>
	(a -> Int)	-- ^ injection
	-> v a		-- ^ The input vector.
	-> RangeMin	-- ^ A range-max function on the elements (under the above ordering)
			-- which runs in /O(1)/.

unsafeInjectRangeMin inject xs = unsafeIntRangeMin (G.unstream (fmap inject (G.stream xs)))
unsafeInjectRangeMax inject = unsafeInjectRangeMin (invertValue . inject)
injectRangeMin inject xs = intRangeMin (G.unstream (fmap inject (G.stream xs)))
injectRangeMax inject = injectRangeMin (invertValue . inject)