{-# LANGUAGE LambdaCase #-}

-- | Bisection methods and binary search functions. They partition a half-open interval \([l, r)\)
-- into two and return either the left or the right point of the boundary.
--
-- @
-- Y Y Y Y Y N N N N N      Y: user predicate holds,
-- --------* *---------> x  N: user predicate does not hold,
--         L R              L, R: left, right point of the boundary
-- @
--
-- ==== __Example__
-- Perform index compression:
--
-- >>> import AtCoder.Extra.Bisect
-- >>> import Data.Vector.Algorithms.Intro qualified as VAI
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList ([0, 20, 10, 40, 30] :: [Int])
-- >>> let dict = VU.uniq $ VU.modify VAI.sort xs
-- >>> VU.map (lowerBound dict) xs
-- [0,2,1,4,3]
--
-- @since 1.3.0.0
module AtCoder.Extra.Bisect
  ( -- * C++-like binary search
    lowerBound,
    lowerBoundIn,
    upperBound,
    upperBoundIn,

    -- * Generic bisection method
    maxRight,
    maxRightM,
    minLeft,
    minLeftM,
  )
where

import AtCoder.Internal.Assert qualified as ACIA
import Data.Functor.Identity
import Data.Vector.Generic qualified as VG
import GHC.Stack (HasCallStack)

-- | \(O(\log n)\) Returns the maximum \(r\) where \(x_i \lt x_{ref}\) holds for \(i \in [0, r)\).
--
-- @
-- Y Y Y Y Y N N N N N      Y: x_i < x_ref
-- --------- *---------> x  N: x_i >= x_ref
--           R              R: the right boundary point returned
-- @
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [1, 1, 2, 2, 4, 4]
-- >>> lowerBound xs 1
-- 0
--
-- >>> lowerBound xs 2
-- 2
--
-- >>> lowerBound xs 3
-- 4
--
-- >>> lowerBound xs 4
-- 4
--
--
-- >>> lowerBound xs 5
-- 6
--
-- @since 1.3.0.0
{-# INLINE lowerBound #-}
lowerBound :: (HasCallStack, VG.Vector v a, Ord a) => v a -> a -> Int
lowerBound :: forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
v a -> a -> Int
lowerBound v a
vec = Int -> Int -> v a -> a -> Int
forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
Int -> Int -> v a -> a -> Int
lowerBoundIn Int
0 (v a -> Int
forall (v :: * -> *) a. Vector v a => v a -> Int
VG.length v a
vec) v a
vec

-- | \(O(\log n)\) Computes the `lowerBound` for a slice of a vector within the interval \([l, r)\).
--
-- ==== Constraints
-- - \(0 \le l \le r \le n\)
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [10, 10, 20, 20, 40, 40]
-- >>> --                            *---*---*
-- >>> lowerBoundIn 2 5 xs 10
-- 2
--
-- >>> lowerBoundIn 2 5 xs 20
-- 2
--
-- >>> lowerBoundIn 2 5 xs 30
-- 4
--
-- >>> lowerBoundIn 2 5 xs 40
-- 4
--
-- >>> lowerBoundIn 2 5 xs 50
-- 5
--
-- @since 1.3.0.0
{-# INLINE lowerBoundIn #-}
lowerBoundIn :: (HasCallStack, VG.Vector v a, Ord a) => Int -> Int -> v a -> a -> Int
lowerBoundIn :: forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
Int -> Int -> v a -> a -> Int
lowerBoundIn Int
l Int
r v a
vec a
target = HasCallStack => Int -> Int -> (Int -> Bool) -> Int
Int -> Int -> (Int -> Bool) -> Int
maxRight Int
l Int
r ((Int -> Bool) -> Int) -> (Int -> Bool) -> Int
forall a b. (a -> b) -> a -> b
$ \Int
i -> v a
vec v a -> Int -> a
forall (v :: * -> *) a.
(HasCallStack, Vector v a) =>
v a -> Int -> a
VG.! Int
i a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
target
  where
    !Int -> ()
_ = HasCallStack => String -> Int -> Int -> Int -> Int -> ()
String -> Int -> Int -> Int -> Int -> ()
ACIA.checkIntervalBounded String
"AtCoder.Extra.Bisect.lowerBoundIn" Int
l Int
r (Int -> Int -> ()) -> Int -> Int -> ()
forall a b. (a -> b) -> a -> b
$ v a -> Int
forall (v :: * -> *) a. Vector v a => v a -> Int
VG.length v a
vec

-- | \(O(\log n)\) Returns the maximum \(r\) where \(x_i \le x_{ref}\) holds for \(i \in [0, r)\).
--
-- @
-- Y Y Y Y Y N N N N N      Y: x_i <= x_ref,
-- --------- *---------> x  N: x_i > x_ref,
--           R              R: the right boundary point returned
-- @
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [10, 10, 20, 20, 40, 40]
-- >>> upperBound xs 0
-- 0
--
-- >>> upperBound xs 10
-- 2
--
-- >>> upperBound xs 20
-- 4
--
-- >>> upperBound xs 30
-- 4
--
-- >>> upperBound xs 39
-- 4
--
-- >>> upperBound xs 40
-- 6
--
-- @since 1.3.0.0
{-# INLINE upperBound #-}
upperBound :: (HasCallStack, VG.Vector v a, Ord a) => v a -> a -> Int
upperBound :: forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
v a -> a -> Int
upperBound v a
vec = Int -> Int -> v a -> a -> Int
forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
Int -> Int -> v a -> a -> Int
upperBoundIn Int
0 (v a -> Int
forall (v :: * -> *) a. Vector v a => v a -> Int
VG.length v a
vec) v a
vec

-- | \(O(\log n)\) Computes the `upperBound` for a slice of a vector within the interval \([l, r)\).
--
-- ==== Constraints
-- - \(0 \le l \le r \le n\)
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [10, 10, 20, 20, 40, 40]
-- >>> --                            *---*---*
-- >>> upperBoundIn 2 5 xs 0
-- 2
--
-- >>> upperBoundIn 2 5 xs 10
-- 2
--
-- >>> upperBoundIn 2 5 xs 20
-- 4
--
-- >>> upperBoundIn 2 5 xs 30
-- 4
--
-- >>> upperBoundIn 2 5 xs 40
-- 5
--
-- >>> upperBoundIn 2 5 xs 50
-- 5
--
-- @since 1.3.0.0
{-# INLINE upperBoundIn #-}
upperBoundIn :: (HasCallStack, VG.Vector v a, Ord a) => Int -> Int -> v a -> a -> Int
upperBoundIn :: forall (v :: * -> *) a.
(HasCallStack, Vector v a, Ord a) =>
Int -> Int -> v a -> a -> Int
upperBoundIn Int
l Int
r v a
vec a
target = HasCallStack => Int -> Int -> (Int -> Bool) -> Int
Int -> Int -> (Int -> Bool) -> Int
maxRight Int
l Int
r ((Int -> Bool) -> Int) -> (Int -> Bool) -> Int
forall a b. (a -> b) -> a -> b
$ \Int
i -> v a
vec v a -> Int -> a
forall (v :: * -> *) a.
(HasCallStack, Vector v a) =>
v a -> Int -> a
VG.! Int
i a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
target
  where
    !Int -> ()
_ = HasCallStack => String -> Int -> Int -> Int -> Int -> ()
String -> Int -> Int -> Int -> Int -> ()
ACIA.checkIntervalBounded String
"AtCoder.Extra.Bisect.upperBoundIn" Int
l Int
r (Int -> Int -> ()) -> Int -> Int -> ()
forall a b. (a -> b) -> a -> b
$ v a -> Int
forall (v :: * -> *) a. Vector v a => v a -> Int
VG.length v a
vec

-- | \(O(\log n)\) Applies the bisection method on a half-open interval \([l, r)\) and returns the
-- right boundary point.
--
-- @
-- Y Y Y Y Y N N N N N      Y: p(i) returns `true`,
-- --------- *---------> x  N: p(i) returns `false`,
--           R              R: the right boundary point returned
-- @
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [10, 10, 20, 20, 30, 30]
-- >>> let n = VU.length xs
-- >>> maxRight 0 n ((<= 20) . (xs VU.!))
-- 4
--
-- >>> maxRight 0 n ((<= 0) . (xs VU.!))
-- 0
--
-- >>> maxRight 0 n ((<= 100) . (xs VU.!))
-- 6
--
-- >>> maxRight 0 3 ((<= 20) . (xs VU.!))
-- 3
--
-- @since 1.3.0.0
{-# INLINE maxRight #-}
maxRight ::
  (HasCallStack) =>
  -- | \(l\)
  Int ->
  -- | \(r\)
  Int ->
  -- | \(p\)
  (Int -> Bool) ->
  -- | Maximum \(r' (r' \le r)\) where \(p(i)\) holds for \(i \in [l, r')\).
  Int
maxRight :: HasCallStack => Int -> Int -> (Int -> Bool) -> Int
maxRight Int
l Int
r Int -> Bool
p = Identity Int -> Int
forall a. Identity a -> a
runIdentity (Identity Int -> Int) -> Identity Int -> Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> (Int -> Identity Bool) -> Identity Int
forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
maxRightM Int
l Int
r (Bool -> Identity Bool
forall a. a -> Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Identity Bool) -> (Int -> Bool) -> Int -> Identity Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Bool
p)

-- | \(O(\log n)\) Monadic variant of `maxRight`.
--
-- @since 1.3.0.0
{-# INLINE maxRightM #-}
maxRightM :: (HasCallStack, Monad m) => Int -> Int -> (Int -> m Bool) -> m Int
maxRightM :: forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
maxRightM Int
l0 Int
r0 Int -> m Bool
p = Int -> Int -> (Int -> m Bool) -> m Int
forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
bisectImpl (Int
l0 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int
r0 Int -> m Bool
p
  where
    !Int -> ()
_ = HasCallStack => String -> Int -> Int -> Int -> ()
String -> Int -> Int -> Int -> ()
ACIA.checkInterval String
"AtCoder.Extra.Bisect.maxRightM" Int
l0 Int
r0

-- | \(O(\log n)\) Applies the bisection method on a half-open interval \([l, r)\) and returns the
-- left boundary point.
--
-- @
-- N N N N N Y Y Y Y Y      Y: p(i) returns `true`,
-- --------* ----------> x  N: p(i) returns `false`,
--         L                L: the left boundary point returned
-- @
--
-- ==== __Example__
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let xs = VU.fromList [10, 10, 20, 20, 30, 30]
-- >>> let n = VU.length xs
-- >>> minLeft 0 n ((>= 20) . (xs VU.!))
-- 2
--
-- >>> minLeft 0 n ((>= 0) . (xs VU.!))
-- 0
--
-- >>> minLeft 0 n ((>= 100) . (xs VU.!))
-- 6
--
-- @since 1.3.0.0
{-# INLINE minLeft #-}
minLeft ::
  (HasCallStack) =>
  -- | \(l\)
  Int ->
  -- | \(r\)
  Int ->
  -- | \(p\)
  (Int -> Bool) ->
  -- | Minimum \(l' (l' \ge l)\) where \(p(i)\) holds for \(i \in [l', r)\)
  Int
minLeft :: HasCallStack => Int -> Int -> (Int -> Bool) -> Int
minLeft Int
l Int
r Int -> Bool
p = Identity Int -> Int
forall a. Identity a -> a
runIdentity (Identity Int -> Int) -> Identity Int -> Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> (Int -> Identity Bool) -> Identity Int
forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
minLeftM Int
l Int
r (Bool -> Identity Bool
forall a. a -> Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Identity Bool) -> (Int -> Bool) -> Int -> Identity Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Bool
p)

-- | \(O(\log n)\) Monadic variant of `maxRight`.
--
-- @since 1.3.0.0
{-# INLINE minLeftM #-}
minLeftM :: (HasCallStack, Monad m) => Int -> Int -> (Int -> m Bool) -> m Int
minLeftM :: forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
minLeftM Int
l Int
r Int -> m Bool
p = (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int -> Int) -> m Int -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> (Int -> m Bool) -> m Int
forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
bisectImpl Int
r (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> m Bool
p
  where
    !Int -> ()
_ = HasCallStack => String -> Int -> Int -> Int -> ()
String -> Int -> Int -> Int -> ()
ACIA.checkInterval String
"AtCoder.Extra.Bisect.minLeftM" Int
l Int
r

-- | Takes an open interval (l, r) or (r, l).
{-# INLINE bisectImpl #-}
bisectImpl :: (HasCallStack, Monad m) => Int -> Int -> (Int -> m Bool) -> m Int
bisectImpl :: forall (m :: * -> *).
(HasCallStack, Monad m) =>
Int -> Int -> (Int -> m Bool) -> m Int
bisectImpl Int
l0 Int
r0 Int -> m Bool
p = Int -> Int -> m Int
inner Int
l0 Int
r0
  where
    inner :: Int -> Int -> m Int
inner Int
l Int
r
      | Int -> Int
forall a. Num a => a -> a
abs (Int
r Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
l) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
1 = Int -> m Int
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
r
      | Bool
otherwise =
          Int -> m Bool
p Int
mid m Bool -> (Bool -> m Int) -> m Int
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Bool
True -> Int -> Int -> m Int
inner Int
mid Int
r
            Bool
False -> Int -> Int -> m Int
inner Int
l Int
mid
      where
        mid :: Int
mid = (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
r) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2