packages feed

seqn-0.1.1.0: src/Data/Seqn/Internal/MSeq.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_HADDOCK not-home #-}

-- |
-- This is an internal module. You probably don't need to import this. Use
-- "Data.Seqn.MSeq" instead.
--
-- = WARNING
--
-- Definitions in this module allow violating invariants that would otherwise be
-- guaranteed by "Data.Seqn.MSeq". Use at your own risk!
--
module Data.Seqn.Internal.MSeq
  (
    -- * MSeq
    MSeq(..)

    -- * Construct
  , empty
  , singleton
  , fromList
  , fromRevList
  , replicate
  , replicateA
  , generate
  , generateA
  , unfoldr
  , unfoldl
  , unfoldrM
  , unfoldlM
  , concatMap
  , mfix

    -- * Convert
  , toRevList

    -- * Index
  , lookup
  , index
  , (!?)
  , (!)
  , update
  , adjust
  , insertAt
  , deleteAt

    -- * Slice
  , cons
  , snoc
  , uncons
  , unsnoc
  , take
  , drop
  , slice
  , splitAt
  , takeEnd
  , dropEnd
  , splitAtEnd

    -- * Filter
  , filter
  , mapMaybe
  , mapEither
  , filterA
  , mapMaybeA
  , mapEitherA
  , takeWhile
  , dropWhile
  , span
  , break
  , takeWhileEnd
  , dropWhileEnd
  , spanEnd
  , breakEnd

    -- * Transform
  , map
  , liftA2
  , traverse
  , imap
  , itraverse
  , reverse
  , intersperse
  , scanl
  , scanr
  , sort
  , sortBy

    -- * Search and test
  , findEnd
  , findIndex
  , findIndexEnd
  , infixIndices
  , binarySearchFind
  , isPrefixOf
  , isSuffixOf
  , isInfixOf
  , isSubsequenceOf

    -- * Zip and unzip
  , zipWith
  , zipWith3
  , zipWithM
  , zipWith3M
  , unzipWith
  , unzipWith3

    -- * Measured queries
  , summaryMay
  , summary
  , sliceSummaryMay
  , sliceSummary
  , foldlSliceSummaryComponents
  , binarySearchPrefix
  , binarySearchSuffix

    -- * Force
  , liftRnf2

    -- * Internal
  , fromMTree

    -- * Testing
  , valid
  , debugShowsPrec
  ) where

import Prelude hiding (break, concatMap, drop, dropWhile, filter, liftA2, lookup, map, replicate, reverse, scanl, scanr, span, splitAt, take, takeWhile, traverse, unzip, unzip3, zip, zip3, zipWith, zipWith3)
import qualified Control.Applicative as Ap
import Control.Applicative.Backwards (Backwards(..))
import Control.DeepSeq (NFData(..))
import Data.Bifunctor (Bifunctor(..))
import Data.Coerce (coerce)
import qualified Data.Foldable as F
import qualified Data.Foldable.WithIndex as IFo
import Data.Functor.Classes (Eq1(..), Ord1(..), Show1(..))
import Data.Functor.Const (Const(..))
import Data.Functor.Identity (Identity(..))
import Data.List.NonEmpty (NonEmpty(..))
import Data.Maybe (fromMaybe)
import qualified Data.Monoid as Monoid
import qualified Data.Primitive.Array as A
import Data.Semigroup (Semigroup(..))
import qualified Data.SamSort as Sam
import qualified GHC.Exts as X
import Text.Read (Read(..))
import qualified Text.Read as Read

import Data.Seqn.Internal.MTree (Measured(..), MTree(..))
import qualified Data.Seqn.Internal.MTree as T
import qualified Data.Seqn.Internal.Util as U
import qualified Data.Seqn.Internal.Stream as Stream
import Data.Seqn.Internal.Stream (Step(..), Stream(..))
import qualified Data.Seqn.Internal.KMP as KMP

--------
-- Seq
--------

-- | A sequence with elements of type @a@. An instance of @'Measured' a@ is
-- required for most operations.
data MSeq a
  = MTree !a !(MTree a)
  | MEmpty
-- See Note [Seq structure] in Data.Seqn.Internal.Seq

--------------
-- Instances
--------------

instance Eq a => Eq (MSeq a) where
  t1 == t2 = compareLength t1 t2 == EQ && stream t1 == stream t2
  {-# INLINABLE (==) #-}

-- | Lexicographical ordering
instance Ord a => Ord (MSeq a) where
  compare t1 t2 = compare (stream t1) (stream t2)
  {-# INLINABLE compare #-}

instance Show a => Show (MSeq a) where
  showsPrec _ t = shows (F.toList t)
  {-# INLINABLE showsPrec #-}

instance (Measured a, Read a) => Read (MSeq a) where
  readPrec = fmap fromList readListPrec
  {-# INLINABLE readPrec #-}

  readListPrec = Read.readListPrecDefault
  {-# INLINABLE readListPrec #-}

instance Eq1 MSeq where
  liftEq f t1 t2 = compareLength t1 t2 == EQ && liftEq f (stream t1) (stream t2)
  {-# INLINE liftEq #-}

instance Ord1 MSeq where
  liftCompare f t1 t2 = liftCompare f (stream t1) (stream t2)
  {-# INLINE liftCompare #-}

instance Show1 MSeq where
  liftShowsPrec _ sl _ t = sl (F.toList t)
  {-# INLINE liftShowsPrec #-}

-- |
-- [@length@]: \(O(1)\).
--
-- Folds are \(O(n)\).
instance Foldable MSeq where
  fold = foldMap id
  {-# INLINABLE fold #-}

  foldMap f = \case
    MTree x xs -> f x <> T.foldMap f xs
    MEmpty -> mempty
  {-# INLINE foldMap #-}

  foldMap' f = F.foldl' (\z x -> z <> f x) mempty
  {-# INLINE foldMap' #-}

  foldr f z = Stream.foldr f z . stream
  {-# INLINE foldr #-}

  foldl f z = Stream.foldr (flip f) z . streamEnd
  {-# INLINE foldl #-}

  foldl' f !z = \case
    MTree x xs -> T.foldl' f (f z x) xs
    MEmpty -> z
  {-# INLINE foldl' #-}

  foldr' f !z = \case
    MTree x xs -> f x $! T.foldr' f z xs
    MEmpty -> z
  {-# INLINE foldr' #-}

  null = \case
    MTree _ _ -> False
    MEmpty -> True

  length = \case
    MTree _ xs -> 1 + T.size xs
    MEmpty -> 0

instance Measured a => X.IsList (MSeq a) where
  type Item (MSeq a) = a
  fromList = fromList
  {-# INLINE fromList #-}

  toList = F.toList
  {-# INLINE toList #-}

instance IFo.FoldableWithIndex Int MSeq where
  ifoldMap f = \case
    MTree x xs -> f 0 x <> T.ifoldMap f 1 xs
    MEmpty -> mempty
  {-# INLINE ifoldMap #-}

  ifoldr f z = Stream.ifoldr f z 0 (+1) . stream
  {-# INLINE ifoldr #-}

  ifoldl f z = \t ->
    Stream.ifoldr (flip . f) z (length t - 1) (subtract 1) (streamEnd t)
  {-# INLINE ifoldl #-}

  ifoldr' f !z = \case
    MTree x xs -> f 0 x $! T.ifoldr' f z (T.size xs) xs
    MEmpty -> z
  {-# INLINE ifoldr' #-}

  ifoldl' f !z = \case
    MTree x xs -> T.ifoldl' f (f 0 z x) 1 xs
    MEmpty -> z
  {-# INLINE ifoldl' #-}

-- |
-- [@(<>)@]: \(O(\left| \log n_1 - \log n_2 \right|)\). Concatenates two
-- sequences.
--
-- [@stimes@]: \(O(\log c)\). @stimes c xs@ is @xs@ repeating @c@ times. If
-- @c < 0@, 'empty' is returned.
instance Measured a => Semigroup (MSeq a) where
  MTree x xs <> MTree y ys = MTree x (T.link y xs ys)
  l <> MEmpty = l
  MEmpty <> r = r
  {-# INLINABLE (<>) #-}

  stimes !c = \case
    t@(MTree x xs)
      | c <= 0 -> MEmpty
      | fromIntegral c * toi (length t) > toi (maxBound :: Int) ->
          error "MSeq.stimes: result size too large"
      | otherwise -> stimesGo x (c'-1) xs xs
      where
        c' = fromIntegral c :: Int
        toi :: Int -> Integer
        toi = fromIntegral
    MEmpty -> MEmpty
  {-# INLINABLE stimes #-}
  -- See Note [Complexity of stimes] in Data.Seqn.Internal.Seq

  sconcat (x:|xs) = mconcat (x:xs)
  {-# INLINABLE sconcat #-}

stimesGo :: Measured a => a -> Int -> MTree a -> MTree a -> MSeq a
stimesGo !x = go
  where
    go c !xs !acc
      | c <= 0 = MTree x acc
      | c `mod` 2 == 0 = go (c `div` 2) (T.bin x xs xs) acc
      | otherwise = go (c `div` 2) (T.bin x xs xs) (T.link x xs acc)
{-# INLINE stimesGo #-}

-- |
-- [@mempty@]: The empty sequence.
instance Measured a => Monoid (MSeq a) where
  mempty = MEmpty

  mconcat = concatMap id
  {-# INLINE mconcat #-} -- Inline for fusion

instance (NFData (Measure a), NFData a) => NFData (MSeq a) where
  rnf = \case
    MTree x xs -> rnf x `seq` rnf xs
    MEmpty -> ()
  {-# INLINABLE rnf #-}

--------------
-- Construct
--------------

-- | The empty sequence.
empty :: MSeq a
empty = MEmpty

-- | A singleton sequence.
singleton :: a -> MSeq a
singleton x = MTree x T.MTip

-- | \(O(n)\). Create an @MSeq@ from a list.
fromList :: Measured a => [a] -> MSeq a
fromList = ltrFinish . F.foldl' ltrPush Nil
{-# INLINE fromList #-}
-- See Note [fromList implementation]

-- | \(O(n)\). Create an @MSeq@ from a reversed list.
fromRevList :: Measured a => [a] -> MSeq a
fromRevList = rtlFinish . F.foldl' (flip rtlPush) Nil
{-# INLINE fromRevList #-}
-- See Note [fromList implementation]

-- | \(O(\log n)\). A sequence with a repeated element.
-- If the length is negative, 'empty' is returned.
replicate :: Measured a => Int -> a -> MSeq a
replicate n !x
  | n <= 0 = MEmpty
  | otherwise = stimesGo x (n-1) MTip MTip
{-# INLINABLE replicate #-}

-- | \(O(n)\). Generate a sequence from a length and an applicative action.
-- If the length is negative, 'empty' is returned.
replicateA :: (Measured a, Applicative f) => Int -> f a -> f (MSeq a)
replicateA !n m = generateA n (const m)
{-# INLINABLE replicateA #-}

-- | \(O(n)\). Generate a sequence from a length and a generator.
-- If the length is negative, 'empty' is returned.
generate :: Measured a => Int -> (Int -> a) -> MSeq a
generate =
  (coerce :: (Int -> (Int -> Identity a) -> Identity (MSeq a))
          -> Int -> (Int -> a) -> MSeq a)
  generateA
{-# INLINE generate #-}

-- | \(O(n)\). Generate a sequence from a length and an applicative generator.
-- If the length is negative, 'empty' is returned.
generateA
  :: (Measured a, Applicative f) => Int -> (Int -> f a) -> f (MSeq a)
generateA n f
  | n <= 0 = pure MEmpty
  | otherwise = Ap.liftA2 MTree (f 0) (T.generateA f 1 (n-1))
{-# INLINE generateA #-}

-- | \(O(n)\). Unfold a sequence from left to right.
unfoldr :: Measured a => (b -> Maybe (a, b)) -> b -> MSeq a
unfoldr =
  (coerce :: ((b -> Identity (Maybe (a, b))) -> b -> Identity (MSeq a))
          -> (b -> Maybe (a, b)) -> b -> MSeq a)
  unfoldrM
{-# INLINE unfoldr #-}

-- | \(O(n)\). Unfold a sequence monadically from left to right.
unfoldrM :: (Measured a, Monad m) => (b -> m (Maybe (a, b))) -> b -> m (MSeq a)
unfoldrM f = go Nil
  where
    go !b z = f z >>= \case
      Nothing -> pure $! ltrFinish b
      Just (x, z') -> go (ltrPush b x) z'
{-# INLINE unfoldrM #-}

-- | \(O(n)\). Unfold a sequence from right to left.
unfoldl :: Measured a => (b -> Maybe (b, a)) -> b -> MSeq a
unfoldl =
  (coerce :: ((b -> Identity (Maybe (b, a))) -> b -> Identity (MSeq a))
          -> (b -> Maybe (b, a)) -> b -> MSeq a)
  unfoldlM
{-# INLINE unfoldl #-}

-- | \(O(n)\). Unfold a sequence monadically from right to left.
unfoldlM :: (Measured a, Monad m) => (b -> m (Maybe (b, a))) -> b -> m (MSeq a)
unfoldlM f = go Nil
  where
    go !b z = f z >>= \case
      Nothing -> pure $! rtlFinish b
      Just (z', x) -> go (rtlPush x b) z'
{-# INLINE unfoldlM #-}

-- | \(O \left(\sum_i \log n_i \right)\).
-- Map over a @Foldable@ and concatenate the results.
concatMap :: (Measured b, Foldable f) => (a -> MSeq b) -> f a -> MSeq b
concatMap f = ltrFinish . F.foldl' g Nil
  where
    g b x = case f x of
      MEmpty -> b
      MTree y ys -> ltrPushMany b y ys
    {-# INLINE g #-}
{-# INLINE concatMap #-}
-- See Note [concatMap implementation]

-- | Monadic fixed point. See "Control.Monad.Fix".
mfix :: Measured a => (a -> MSeq a) -> MSeq a
mfix f =
  imap
    (\i _ -> let x = index i (f x) in x)
    (f (error "MSeq.mfix: f must be lazy"))
{-# INLINE mfix #-}

------------
-- Convert
------------

-- | \(O(n)\). Convert to a list in reverse.
--
-- To convert to a list without reversing, use
-- @Data.Foldable.'Data.Foldable.toList'@.
toRevList :: MSeq a -> [a]
toRevList t = X.build $ \lcons lnil -> F.foldl (flip lcons) lnil t
{-# INLINE toRevList #-}

----------
-- Index
----------

-- | \(O(\log n)\). Look up the element at an index.
lookup :: Int -> MSeq a -> Maybe a
lookup !i (MTree x xs)
  | i < 0 || T.size xs < i = Nothing
  | i == 0 = Just x
  | otherwise = Just $! T.index (i-1) xs
lookup _ MEmpty = Nothing
{-# INLINE lookup #-}

-- | \(O(\log n)\). Look up the element at an index. Calls @error@ if the index
-- is out of bounds.
index :: Int -> MSeq a -> a
index !i = \case
  MTree x xs
    | i == 0 -> x
    | otherwise -> T.index (i-1) xs
  MEmpty -> error "MSeq.index: out of bounds"

-- | \(O(\log n)\). Infix version of 'lookup'.
(!?) :: MSeq a -> Int -> Maybe a
(!?) = flip lookup
{-# INLINE (!?) #-}

-- | \(O(\log n)\). Infix version of 'index'. Calls @error@ if the index is out
-- of bounds.
(!) :: MSeq a -> Int -> a
(!) = flip index

-- | \(O(\log n)\). Update an element at an index. If the index is out of
-- bounds, the sequence is returned unchanged.
update :: Measured a => Int -> a -> MSeq a -> MSeq a
update i x t = adjust (const x) i t
{-# INLINABLE update #-}

-- | \(O(\log n)\). Adjust the element at an index. If the index is out of
-- bounds, the sequence is returned unchanged.
adjust :: Measured a => (a -> a) -> Int -> MSeq a -> MSeq a
adjust f !i t = case t of
  MTree x xs
    | i < 0 || T.size xs < i -> t
    | i == 0 -> MTree (f x) xs
    | otherwise -> MTree x (runIdentity (T.adjustF (Identity U.#. f) (i-1) xs))
  MEmpty -> MEmpty
{-# INLINE adjust #-}

-- | \(O(\log n)\). Insert an element at an index. If the index is out of
-- bounds, the element is added to the closest end of the sequence.
insertAt :: Measured a => Int -> a -> MSeq a -> MSeq a
insertAt !i y t = case t of
  MTree x xs
    | i <= 0 -> cons y t
    | otherwise -> MTree x (T.insertAt (i-1) y xs)
  MEmpty -> singleton y
{-# INLINABLE insertAt #-}

-- | \(O(\log n)\). Delete an element at an index. If the index is out of
-- bounds, the sequence is returned unchanged.
deleteAt :: Measured a => Int -> MSeq a -> MSeq a
deleteAt !i t = case t of
  MTree x xs
    | i < 0 || T.size xs < i -> t
    | i == 0 -> fromMTree xs
    | otherwise -> MTree x (T.deleteAt (i-1) xs)
  MEmpty -> MEmpty
{-# INLINABLE deleteAt #-}

----------
-- Slice
----------

-- | \(O(\log n)\). Append a value to the beginning of a sequence.
cons :: Measured a => a -> MSeq a -> MSeq a
cons x (MTree y ys) = MTree x (T.cons y ys)
cons x MEmpty = singleton x
{-# INLINABLE cons #-}

-- | \(O(\log n)\). Append a value to the end of a sequence.
snoc :: Measured a => MSeq a -> a -> MSeq a
snoc (MTree y ys) x = MTree y (T.snoc ys x)
snoc MEmpty x = singleton x
{-# INLINABLE snoc #-}

-- | \(O(\log n)\). The head and tail of a sequence.
uncons :: Measured a => MSeq a -> Maybe (a, MSeq a)
uncons (MTree x xs) = Just . (,) x $! fromMTree xs
uncons MEmpty = Nothing
{-# INLINE uncons #-}

-- | \(O(\log n)\). The init and last of a sequence.
unsnoc :: Measured a => MSeq a -> Maybe (MSeq a, a)
unsnoc (MTree x xs) = case T.unsnoc xs of
  U.SNothing -> Just (MEmpty, x)
  U.SJust (U.S2 ys y) -> Just (MTree x ys, y)
unsnoc MEmpty = Nothing
{-# INLINE unsnoc #-}

-- | \(O(\log n)\). Take a number of elements from the beginning of a sequence.
take :: Measured a => Int -> MSeq a -> MSeq a
take !i t@(MTree x xs)
  | i <= 0 = MEmpty
  | T.size xs < i = t
  | otherwise = MTree x (getConst (T.splitAtF (i-1) xs))
take _ MEmpty = MEmpty
{-# INLINABLE take #-}

-- | \(O(\log n)\). Drop a number of elements from the beginning of a sequence.
drop :: Measured a => Int -> MSeq a -> MSeq a
drop !i t@(MTree _ xs)
  | i <= 0 = t
  | T.size xs < i = MEmpty
  | otherwise = case U.unTagged (T.splitAtF (i-1) xs) of
      U.S2 x' xs' -> MTree x' xs'
drop _ MEmpty = MEmpty
{-# INLINABLE drop #-}

-- | \(O(\log n)\). The slice of a sequence between two indices (inclusive).
slice :: Measured a => (Int, Int) -> MSeq a -> MSeq a
slice (i,j) = drop i . take (j+1)
{-# INLINABLE slice #-}

-- | \(O(\log n)\). Take a number of elements from the end of a sequence.
takeEnd :: Measured a => Int -> MSeq a -> MSeq a
takeEnd n t = drop (length t - n) t
{-# INLINABLE takeEnd #-}

-- | \(O(\log n)\). Drop a number of elements from the end of a sequence.
dropEnd :: Measured a => Int -> MSeq a -> MSeq a
dropEnd n t = take (length t - n) t
{-# INLINABLE dropEnd #-}

-- | \(O(\log n)\). Split a sequence at a given index.
--
-- @splitAt n xs == ('take' n xs, 'drop' n xs)@
splitAt :: Measured a => Int -> MSeq a -> (MSeq a, MSeq a)
splitAt !i t@(MTree x xs)
  | i <= 0 = (MEmpty, t)
  | T.size xs < i = (t, MEmpty)
  | otherwise = case T.splitAtF (i-1) xs of
      U.S2 xs1 (U.S2 x' xs2) -> (MTree x xs1, MTree x' xs2)
splitAt _ MEmpty = (MEmpty, MEmpty)
{-# INLINABLE splitAt #-}

-- | \(O(\log n)\). Split a sequence at a given index from the end.
--
-- @splitAtEnd n xs == ('dropEnd' n xs, 'takeEnd' n xs)@
splitAtEnd :: Measured a => Int -> MSeq a -> (MSeq a, MSeq a)
splitAtEnd i s = splitAt (length s - i) s
{-# INLINABLE splitAtEnd #-}

-----------
-- Filter
-----------

-- | \(O(n)\). Keep elements that satisfy a predicate.
filter :: Measured a => (a -> Bool) -> MSeq a -> MSeq a
filter =
  (coerce :: ((a -> Identity Bool) -> MSeq a -> Identity (MSeq a))
          -> (a -> Bool) -> MSeq a -> MSeq a)
  filterA
{-# INLINE filter #-}

-- | \(O(n)\). Map over elements and collect the @Just@s.
mapMaybe :: Measured b => (a -> Maybe b) -> MSeq a -> MSeq b
mapMaybe =
  (coerce :: ((a -> Identity (Maybe b)) -> MSeq a -> Identity (MSeq b))
          -> (a -> Maybe b) -> MSeq a -> MSeq b)
  mapMaybeA
{-# INLINE mapMaybe #-}

-- | \(O(n)\). Map over elements and split the @Left@s and @Right@s.
mapEither
  :: (Measured b, Measured c) => (a -> Either b c) -> MSeq a -> (MSeq b, MSeq c)
mapEither =
  (coerce :: ((a -> Identity (Either b c)) -> MSeq a -> Identity (MSeq b, MSeq c))
          -> (a -> Either b c) -> MSeq a -> (MSeq b, MSeq c))
  mapEitherA
{-# INLINE mapEither #-}

-- | \(O(n)\). Keep elements that satisfy an applicative predicate.
filterA :: (Measured a, Applicative f) => (a -> f Bool) -> MSeq a -> f (MSeq a)
filterA f = mapMaybeA (\x -> fmap (\b -> if b then Just x else Nothing) (f x))
{-# INLINE filterA #-}

-- | \(O(n)\). Traverse over elements and collect the @Just@s.
mapMaybeA
  :: (Measured b, Applicative f) => (a -> f (Maybe b)) -> MSeq a -> f (MSeq b)
mapMaybeA f = \case
  MTree x xs -> Ap.liftA2 (maybe fromMTree MTree) (f x) (T.mapMaybeA f xs)
  MEmpty -> pure MEmpty
{-# INLINE mapMaybeA #-}

-- | \(O(n)\). Traverse over elements and split the @Left@s and @Right@s.
mapEitherA
  :: (Measured b, Measured c, Applicative f)
  => (a -> f (Either b c)) -> MSeq a -> f (MSeq b, MSeq c)
mapEitherA f = \case
  MTree x xs -> (\g -> Ap.liftA2 g (f x) (T.mapEitherA f xs)) $ \mx xs' ->
    case mx of
      Left x' -> unS2 $ bimap (MTree x') fromMTree xs'
      Right x' -> unS2 $ bimap fromMTree (MTree x') xs'
  MEmpty -> pure (MEmpty, MEmpty)
  where
    unS2 (U.S2 x y) = (x, y)
{-# INLINE mapEitherA #-}

-- | \(O(i + \log n)\). The longest prefix of elements that satisfy a predicate.
-- \(i\) is the length of the prefix.
takeWhile :: Measured a => (a -> Bool) -> MSeq a -> MSeq a
takeWhile p t = IFo.ifoldr (\i x z -> if p x then z else take i t) t t
{-# INLINE takeWhile #-}

-- | \(O(i + \log n)\). The remainder after removing the longest prefix of
-- elements that satisfy a predicate.
-- \(i\) is the length of the prefix.
dropWhile :: Measured a => (a -> Bool) -> MSeq a -> MSeq a
dropWhile p t = IFo.ifoldr (\i x z -> if p x then z else drop i t) MEmpty t
{-# INLINE dropWhile #-}

-- | \(O(i + \log n)\). The longest prefix of elements that satisfy a predicate,
-- together with the remainder of the sequence.
-- \(i\) is the length of the prefix.
--
-- @span p xs == ('takeWhile' p xs, 'dropWhile' p xs)@
span :: Measured a => (a -> Bool) -> MSeq a -> (MSeq a, MSeq a)
span p t = IFo.ifoldr (\i x z -> if p x then z else splitAt i t) (t, MEmpty) t
{-# INLINE span #-}

-- | \(O(i + \log n)\). The longest prefix of elements that /do not/ satisfy a
-- predicate, together with the remainder of the sequence. \(i\) is the length
-- of the prefix.
--
-- @break p == 'span' (not . p)@
break :: Measured a => (a -> Bool) -> MSeq a -> (MSeq a, MSeq a)
break p = span (not . p)
{-# INLINE break #-}

-- | \(O(i + \log n)\). The longest suffix of elements that satisfy a predicate.
-- \(i\) is the length of the suffix.
takeWhileEnd :: Measured a => (a -> Bool) -> MSeq a -> MSeq a
takeWhileEnd p t = IFo.ifoldl (\i z x -> if p x then z else drop (i+1) t) t t
{-# INLINE takeWhileEnd #-}

-- | \(O(i + \log n)\). The remainder after removing the longest suffix of
-- elements that satisfy a predicate.
-- \(i\) is the length of the suffix.
dropWhileEnd :: Measured a => (a -> Bool) -> MSeq a -> MSeq a
dropWhileEnd p t =
  IFo.ifoldl (\i z x -> if p x then z else take (i+1) t) MEmpty t
{-# INLINE dropWhileEnd #-}

-- | \(O(i + \log n)\). The longest suffix of elements that satisfy a predicate,
-- together with the remainder of the sequence.
-- \(i\) is the length of the suffix.
--
-- @spanEnd p xs == ('dropWhileEnd' p xs, 'takeWhileEnd' p xs)@
spanEnd :: Measured a => (a -> Bool) -> MSeq a -> (MSeq a, MSeq a)
spanEnd p t =
  IFo.ifoldl (\i z x -> if p x then z else splitAt (i+1) t) (MEmpty, t) t
{-# INLINE spanEnd #-}

-- | \(O(i + \log n)\). The longest suffix of elements that /do not/ satisfy a
-- predicate, together with the remainder of the sequence.
-- \(i\) is the length of the suffix.
--
-- @breakEnd p == 'spanEnd' (not . p)@
breakEnd :: Measured a => (a -> Bool) -> MSeq a -> (MSeq a, MSeq a)
breakEnd p = spanEnd (not . p)
{-# INLINE breakEnd #-}

--------------
-- Transform
--------------

-- Note [Functor MSeq]
-- ~~~~~~~~~~~~~~~~~~~
-- MSeq cannot be a Functor because of the Measured constraint on the element
-- type. So class methods which require Functor are provided as standalone.

-- | \(O(n)\). Map over a sequence.
map :: Measured b => (a -> b) -> MSeq a -> MSeq b
map =
  (coerce :: ((a -> Identity b) -> MSeq a -> Identity (MSeq b))
          -> (a -> b) -> MSeq a -> MSeq b)
  traverse
{-# INLINE map #-}

-- | \(O(n_1 n_2)\). Cartesian product of two sequences.
liftA2 :: Measured c => (a -> b -> c) -> MSeq a -> MSeq b -> MSeq c
liftA2 f t1 t2 = case t2 of
  MEmpty -> MEmpty
  MTree x MTip -> map (`f` x) t1
  _ -> concatMap (\x -> map (f x) t2) t1
{-# INLINE liftA2 #-}

-- | \(O(n)\). Traverse a sequence.
traverse
  :: (Measured b, Applicative f) => (a -> f b) -> MSeq a -> f (MSeq b)
traverse f = \case
  MEmpty -> pure MEmpty
  MTree x xs -> Ap.liftA2 MTree (f x) (T.traverse f xs)
{-# INLINE traverse #-}

-- | \(O(n)\). Map over a sequence with index.
imap :: Measured b => (Int -> a -> b) -> MSeq a -> MSeq b
imap =
  (coerce :: ((Int -> a -> Identity b) -> MSeq a -> Identity (MSeq b))
          -> (Int -> a -> b) -> MSeq a -> MSeq b)
  itraverse
{-# INLINE imap #-}

-- | \(O(n)\). Traverse a sequence with index.
itraverse
  :: (Measured b, Applicative f) => (Int -> a -> f b) -> MSeq a -> f (MSeq b)
itraverse f = \case
  MEmpty -> pure MEmpty
  MTree x xs -> Ap.liftA2 MTree (f 0 x) (T.itraverse f 1 xs)
{-# INLINE itraverse #-}

-- | \(O(n)\). Reverse a sequence.
reverse :: Measured a => MSeq a -> MSeq a
reverse (MTree x xs) = case T.uncons (rev xs) of
  U.SNothing -> MTree x MTip
  U.SJust (U.S2 x' xs') -> MTree x' (T.snoc xs' x)
  where
    rev T.MTip = T.MTip
    rev (T.MBin sz _ y l r) = T.binn sz y (rev r) (rev l)
reverse MEmpty = MEmpty
{-# INLINABLE reverse #-}

-- | \(O(n)\). Intersperse an element between the elements of a sequence.
intersperse :: Measured a => a -> MSeq a -> MSeq a
intersperse y (MTree x xs) = case T.unsnoc (go xs) of
  U.SNothing -> error "intersperse: impossible"
  U.SJust (U.S2 xs' _) -> MTree x xs'
  where
    yt = T.singleton y
    go T.MTip = yt
    go (T.MBin sz _ z l r) = T.binn (sz*2+1) z (go l) (go r)
    -- No need to balance, x <= 3y => 2x+1 <= 3(2y+1)
intersperse _ MEmpty = MEmpty
{-# INLINABLE intersperse #-}

-- | \(O(n)\). Like 'Data.Foldable.foldl'' but keeps all intermediate values.
scanl :: Measured b => (b -> a -> b) -> b -> MSeq a -> MSeq b
scanl f !z0 =
  cons z0 .
  flip U.evalSState z0 .
  traverse (\x -> U.sState (\z -> let z' = f z x in U.S2 z' z'))
{-# INLINE scanl #-}
-- See Note [SState for scans] in Data.Seqn.Internal.Seq

-- | \(O(n)\). Like 'Data.Foldable.foldr'' but keeps all intermediate values.
scanr :: Measured b => (a -> b -> b) -> b -> MSeq a -> MSeq b
scanr f !z0 =
  flip snoc z0 .
  flip U.evalSState z0 .
  forwards .
  traverse
    (\x -> Backwards (U.sState (\z -> let z' = f x z in U.S2 z' z')))
{-# INLINE scanr #-}

-- | \(O(n \log n)\). Sort a sequence.
sort :: (Ord a, Measured a) => MSeq a -> MSeq a
sort = sortBy compare
{-# INLINABLE sort #-}

-- | \(O(n \log n)\). Sort a sequence using a comparison function.
sortBy :: Measured a => (a -> a -> Ordering) -> MSeq a -> MSeq a
sortBy cmp xs = imap (\i _ -> A.indexArray xa i) xs
  where
    n = length xs
    xa = A.createArray n errorElement $ \ma@(A.MutableArray ma#) -> do
      IFo.ifoldr (\i x z -> A.writeArray ma i x *> z) (pure ()) xs
      Sam.sortArrayBy cmp ma# 0 n
{-# INLINABLE sortBy #-}
-- See Note [Inlinable sortBy] in Data.Seqn.Internal.Seq

--------------------
-- Search and test
--------------------

-- | \(O(n)\). The last element satisfying a predicate.
--
-- To get the first element, use @Data.Foldable.'Data.Foldable.find'@.
findEnd :: (a -> Bool) -> MSeq a -> Maybe a
findEnd f =
  Monoid.getLast . foldMap (\x -> Monoid.Last (if f x then Just x else Nothing))
{-# INLINE findEnd #-}

-- | \(O(n)\). The index of the first element satisfying a predicate.
findIndex :: (a -> Bool) -> MSeq a -> Maybe Int
findIndex f =
  Monoid.getFirst .
  IFo.ifoldMap (\i x -> Monoid.First (if f x then Just i else Nothing))
{-# INLINE findIndex #-}

-- | \(O(n)\). The index of the last element satisfying a predicate.
findIndexEnd :: (a -> Bool) -> MSeq a -> Maybe Int
findIndexEnd f =
  Monoid.getLast .
  IFo.ifoldMap (\i x -> Monoid.Last (if f x then Just i else Nothing))
{-# INLINE findIndexEnd #-}

-- | \(O(n_1 + n_2)\). Indices in the second sequence where the first sequence
-- begins as a substring. Includes overlapping occurences.
infixIndices :: Eq a => MSeq a -> MSeq a -> [Int]
infixIndices t1 t2
  | null t1 = [0 .. length t2]
  | compareLength t1 t2 == GT = []
  | otherwise =
    let n1 = length t1
        t1a = infixIndicesMkArray n1 t1
        !(!mt, !m0) = KMP.build t1a
    in X.build $ \lcons lnil ->
         let f !i x k !m = case KMP.step mt m x of
               (b,m') ->
                 if b
                 then lcons (i-n1+1) (k m')
                 else k m'
         in IFo.ifoldr f (\ !_ -> lnil) t2 m0
{-# INLINE infixIndices #-} -- Inline for fusion

infixIndicesMkArray :: Int -> MSeq a -> A.Array a
infixIndicesMkArray !n !t = A.createArray n errorElement $ \ma ->
  IFo.ifoldr (\i x z -> A.writeArray ma i x *> z) (pure ()) t

-- | \(O(\log n)\). Binary search for an element in a sequence.
--
-- Given a function @f@ this function returns an arbitrary element @x@, if it
-- exists, such that @f x = EQ@. @f@ must be monotonic on the sequence—
-- specifically @fmap f@ must result in a sequence which has many (possibly
-- zero) @LT@s, followed by many @EQ@s, followed by many @GT@s.
binarySearchFind :: (a -> Ordering) -> MSeq a -> Maybe a
binarySearchFind f = \case
  MEmpty -> Nothing
  MTree x xs -> case f x of
    LT -> go xs
    EQ -> Just x
    GT -> Nothing
  where
    go MTip = Nothing
    go (MBin _ _ y l r) = case f y of
      LT -> go r
      EQ -> Just y
      GT -> go l
{-# INLINE binarySearchFind #-}

-- | \(O(\min(n_1,n_2))\). Whether the first sequence is a prefix of the second.
isPrefixOf :: Eq a => MSeq a -> MSeq a -> Bool
isPrefixOf t1 t2 =
  compareLength t1 t2 /= GT && Stream.isPrefixOf (stream t1) (stream t2)
{-# INLINABLE isPrefixOf #-}

-- | \(O(\min(n_1,n_2))\). Whether the first sequence is a suffix of the second.
isSuffixOf :: Eq a => MSeq a -> MSeq a -> Bool
isSuffixOf t1 t2 =
  compareLength t1 t2 /= GT && Stream.isPrefixOf (streamEnd t1) (streamEnd t2)
{-# INLINABLE isSuffixOf #-}

-- | \(O(n_1 + n_2)\). Whether the first sequence is a substring of the second.
isInfixOf :: Eq a => MSeq a -> MSeq a -> Bool
isInfixOf t1 t2 = foldr (\_ _ -> True) False (infixIndices t1 t2)
{-# INLINABLE isInfixOf #-}

-- | \(O(n_1 + n_2)\). Whether the first sequence is a subsequence of the second.
isSubsequenceOf :: Eq a => MSeq a -> MSeq a -> Bool
isSubsequenceOf t1 t2 =
  compareLength t1 t2 /= GT && Stream.isSubsequenceOf (stream t1) (stream t2)
{-# INLINABLE isSubsequenceOf #-}

------------------
-- Zip and unzip
------------------

-- | \(O(\min(n_1,n_2))\). Zip two sequences with a function. The result is
-- as long as the shorter sequence.
zipWith :: Measured c => (a -> b -> c) -> MSeq a -> MSeq b -> MSeq c
zipWith =
  (coerce :: ((a -> b -> Identity c) -> MSeq a -> MSeq b -> Identity (MSeq c))
          -> (a -> b -> c) -> MSeq a -> MSeq b -> MSeq c)
  zipWithM
{-# INLINE zipWith #-}

-- | \(O(\min(n_1,n_2,n_3))\). Zip three sequences with a function. The result
-- is as long as the shortest sequence.
zipWith3
  :: Measured d => (a -> b -> c -> d) -> MSeq a -> MSeq b -> MSeq c -> MSeq d
zipWith3 =
  (coerce :: ((a -> b -> c -> Identity d) -> MSeq a -> MSeq b -> MSeq c -> Identity (MSeq d))
          -> (a -> b -> c -> d) -> MSeq a -> MSeq b -> MSeq c -> MSeq d)
  zipWith3M
{-# INLINE zipWith3 #-}

-- | \(O(\min(n_1,n_2))\). Zip two sequences with a monadic function.
zipWithM
  :: (Measured c, Monad m) => (a -> b -> m c) -> MSeq a -> MSeq b -> m (MSeq c)
zipWithM f t1 t2 = zipWithStreamM f t1 (stream t2)
{-# INLINE zipWithM #-}

-- | \(O(\min(n_1,n_2,n_3))\). Zip three sequences with a monadic function.
zipWith3M
  :: (Measured d, Monad m)
  => (a -> b -> c -> m d) -> MSeq a -> MSeq b -> MSeq c -> m (MSeq d)
zipWith3M f t1 t2 t3 =
  zipWithStreamM
    (\x (U.S2 y z) -> f x y z)
    t1
    (Stream.zipWith U.S2 (stream t2) (stream t3))
{-# INLINE zipWith3M #-}

zipWithStreamM
  :: (Measured c, Monad m)
  => (a -> b -> m c) -> MSeq a -> Stream b -> m (MSeq c)
zipWithStreamM f t strm = case t of
  MEmpty -> pure MEmpty
  MTree x xs -> case strm of
    Stream step s -> case step s of
      Done -> pure MEmpty
      Yield y s1 ->
        Ap.liftA2 MTree (f x y) (T.zipWithStreamM f xs (Stream step s1))
{-# INLINE zipWithStreamM #-}

-- | \(O(n)\). Map over a sequence and unzip the result.
unzipWith
  :: (Measured b, Measured c)
  => (a -> (b, c)) -> MSeq a -> (MSeq b, MSeq c)
unzipWith f t = case t of
  MTree x xs ->
    case (f x, T.unzipWithA (Identity U.#. f) xs) of
      ((x1,x2), Identity (U.S2 xs1 xs2)) ->
        let !t1 = MTree x1 xs1
            !t2 = MTree x2 xs2
        in (t1,t2)
  MEmpty -> (MEmpty, MEmpty)
{-# INLINE unzipWith #-}

-- | \(O(n)\). Map over a sequence and unzip the result.
unzipWith3
  :: (Measured b, Measured c, Measured d)
  => (a -> (b, c, d)) -> MSeq a -> (MSeq b, MSeq c, MSeq d)
unzipWith3 f t = case t of
  MTree x xs ->
    case (f x, T.unzipWith3A (Identity U.#. f) xs) of
      ((x1,x2,x3), Identity (U.S3 xs1 xs2 xs3)) ->
        let !t1 = MTree x1 xs1
            !t2 = MTree x2 xs2
            !t3 = MTree x3 xs3
        in (t1,t2,t3)
  MEmpty -> (MEmpty, MEmpty, MEmpty)
{-# INLINE unzipWith3 #-}

---------------------
-- Measured queries
---------------------

-- | \(O(1)\). The summary is the fold of measures of all elements in the
-- sequence. Returns @Nothing@ if the sequence is empty.
--
-- @summaryMay == 'foldMap' (Just . 'measure')@
summaryMay :: Measured a => MSeq a -> Maybe (Measure a)
summaryMay t = case t of
  MTree x xs -> Just $! measure x T.<<> xs
  MEmpty -> Nothing
{-# INLINE summaryMay #-}

-- | \(O(1)\). The summary is the fold of measures of all elements in the
-- sequence.
--
-- @summary == 'foldMap' 'measure'@
summary :: (Measured a, Monoid (Measure a)) => MSeq a -> Measure a
summary t = fromMaybe mempty (summaryMay t)
{-# INLINABLE summary #-}

-- | \(O(\log n)\). The summary of a slice of the sequence. The slice is
-- indicated by its bounds (inclusive).
--
-- @sliceSummaryMay lu == 'summaryMay' . 'slice' lu@
--
-- @since 0.1.1.0
sliceSummaryMay :: Measured a => (Int, Int) -> MSeq a -> Maybe (Measure a)
sliceSummaryMay (!ql, !qu) t = case t of
  MEmpty -> Nothing
  MTree x xs
    | ql > qu || qu < 0 || length t - 1 < ql -> Nothing
    | otherwise -> Just $! foldlMap1SliceSummaryComponents id (<>) ql qu x xs
{-# INLINE sliceSummaryMay #-}

-- | \(O(\log n)\). The summary of a slice of the sequence. The slice is
-- indicated by its bounds (inclusive).
--
-- @sliceSummary lu == 'summary' . 'slice' lu@
--
-- @since 0.1.1.0
sliceSummary
  :: (Measured a, Monoid (Measure a)) => (Int, Int) -> MSeq a -> Measure a
sliceSummary lu t = fromMaybe mempty (sliceSummaryMay lu t)
{-# INLINABLE sliceSummary #-}

-- | Strict left fold over measures covering a slice. These measures are
-- summaries of \(O(\log n)\) adjacent slices which form the requested slice
-- when concatenated.
--
-- @foldlSliceSummaryComponents (<>) mempty == 'sliceSummary'@
--
-- This function is useful when
--
-- * Some property of the summary of a slice is desired.
-- * It is expensive to compute the summary, i.e. @(<>)@ for @Measure a@ is
--   expensive.
-- * It is possible, and cheaper, to compute the property given components
--   of the summary of the slice.
--
-- ==== __Examples__
--
-- One use case for this is order statistic queries on a slice, such as counting
-- the number of elements less than some value.
--
-- It requires a @Multiset@ structure as outlined below, which can be
-- implemented using sorted arrays/balanced binary trees.
--
-- @
-- data Multiset a
-- singleton :: Ord a => a -> MultiSet a -- O(1)
-- (<>) :: Ord a => Multiset a -> Multiset a -> Multiset a -- O(n1 + n2)
-- countLessThan :: Ord a => a -> Multiset a -> Int -- O(log n)
-- @
--
-- @
-- import Data.Seqn.MSeq (Measured, MSeq)
-- import qualified Data.Seqn.MSeq as MSeq
--
-- newtype Elem a = Elem a deriving Show
--
-- instance Ord a => Measured (Elem a) where
--   type Measure (Elem a) = Multiset a
--   measure (Elem x) = singleton x
--
-- -- | O(n log n).
-- fromList :: Ord a => [a] -> MSeq (Elem a)
-- fromList = MSeq.fromList . map Elem
--
-- -- | O(log^2 n).
-- countLessThanInSlice :: Ord a => a -> (Int, Int) -> MSeq (Elem a) -> Int
-- countLessThanInSlice k =
--   MSeq.foldlSliceSummaryComponents (\\acc xs -> acc + countLessThan k xs) 0
-- @
--
-- @since 0.1.1.0
foldlSliceSummaryComponents
  :: Measured a => (b -> Measure a -> b) -> b -> (Int, Int) -> MSeq a -> b
foldlSliceSummaryComponents f !z (!ql, !qu) t = case t of
  MEmpty -> z
  MTree x xs
    | ql > qu || qu < 0 || length t - 1 < ql -> z
    | otherwise -> foldlMap1SliceSummaryComponents (f z) f ql qu x xs
{-# INLINE foldlSliceSummaryComponents #-}

-- Precondition: slice (ql, qu) (Tree x0 xs0) is non-empty
foldlMap1SliceSummaryComponents
  :: Measured a
  => (Measure a -> b) -> (b -> Measure a -> b)
  -> Int -> Int -> a -> MTree a -> b
foldlMap1SliceSummaryComponents f g !ql !qu x0 xs0
  | ql <= 0 && 0 <= qu = go (f (measure x0)) 1 xs0
  | otherwise = go1 1 xs0
  where
    go1 !i (MBin sz v y l r)
      | ql <= i && k <= qu = f v
      | ql < j && i <= qu =
          if ql <= j && j <= qu
          then go (g (go1 i l) (measure y)) (j+1) r
          else go1 i l
      | ql <= j && j <= qu = go (f (measure y)) (j+1) r
      | otherwise = go1 (j+1) r
      where
        k = i + sz - 1
        j = i + T.size l
    go1 _ MTip = error "MSeq.foldlMap1SliceSummaryComponents: impossible"

    go !z !i (MBin sz v x l r)
      | qu < i || k < ql = z
      | ql <= i && k <= qu = g z v
      | ql <= j && j <= qu = go (g (go z i l) (measure x)) (j+1) r
      | otherwise = go (go z i l) (j+1) r
      where
        k = i + sz - 1
        j = i + T.size l
    go z _ MTip = z
{-# INLINE foldlMap1SliceSummaryComponents #-}

-- | \(O(\log n)\). Perform a binary search on the summaries of the non-empty
-- prefixes of the sequence.
--
-- @binarySearchPrefix p xs@ for a monotonic predicate @p@ returns two adjacent
-- indices @i@ and @j@, @0 <= i < j < length xs@.
--
-- * @i@ is the greatest index such that
--   @p (fromJust (summaryMay (take (i+1) xs)))@
--   is @False@, or @Nothing@ if there is no such index.
-- * @j@ is the least index such that
--   @p (fromJust (summaryMay (take (j+1) xs)))@
--   is @True@, or @Nothing@ if there is no such index.
--
-- ==== __Examples__
--
-- @
-- import "Data.Monoid" (Sum(..))
--
-- newtype Elem = E Int deriving Show
--
-- instance Measured Elem where
--   type Measure Elem = Sum Int
--   measure (E x) = Sum x
-- @
--
-- >>> let xs = fromList [E 1, E 2, E 3, E 4]
--
-- The summaries of the prefixes of @xs@ by index are:
--
-- * @0: measure (E 1) = Sum 1@.
-- * @1: measure (E 1) <> measure (E 2) = Sum 3@.
-- * @2: measure (E 1) <> measure (E 2) <> measure (E 3) = Sum 6@.
-- * @3: measure (E 1) <> measure (E 2) <> measure (E 3) <> measure (E 4) = Sum 10@.
--
-- >>> binarySearchPrefix (> Sum 4) xs
-- (Just 1,Just 2)
--
-- @
--                  ╭──────────┬──────────┬──────────┬──────────╮
-- index:           │        0 │        1 │        2 │        3 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- prefix summary:  │    Sum 1 │    Sum 3 │    Sum 6 |   Sum 10 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- (> Sum 4):       │    False │    False │     True │     True │
--                  ╰──────────┴──────────┴──────────┴──────────╯
-- result:                       ( Just 1 ,   Just 2 )
-- @
--
-- >>> binarySearchPrefix (> Sum 20) xs
-- (Just 3,Nothing)
--
-- @
--                  ╭──────────┬──────────┬──────────┬──────────╮
-- index:           │        0 │        1 │        2 │        3 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- prefix summary:  │    Sum 1 │    Sum 3 │    Sum 6 |   Sum 10 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- (> Sum 20):      │    False │    False │    False │    False │
--                  ╰──────────┴──────────┴──────────┴──────────╯
-- result:                                             ( Just 3 ,  Nothing )
-- @
--
binarySearchPrefix
  :: Measured a => (Measure a -> Bool) -> MSeq a -> (Maybe Int, Maybe Int)
binarySearchPrefix p = \case
  MTree x xs
    | p v -> (Nothing, Just 0)
    | p (v T.<<> xs) -> let !i = go 1 v xs in (Just (i-1), Just i)
    | otherwise -> let !i = T.size xs in (Just i, Nothing)
    where
      v = measure x
  MEmpty -> (Nothing, Nothing)
  where
    go !i !vup = \case
      MBin _ _ x l r
        | p v -> go i vup l
        | p v' -> i + T.size l
        | otherwise -> go (i + T.size l + 1) v' r
        where
          v = vup T.<<> l
          v' = v <> measure x
      MTip -> error "MSeq.binarySearchPrefix: bad p"
{-# INLINE binarySearchPrefix #-}

-- | \(O(\log n)\). Perform a binary search on the summaries of the non-empty
-- suffixes of the sequence.
--
-- @binarySearchSuffix p xs@ for a monotonic predicate @p@ returns two adjacent
-- indices @i@ and @j@, @0 <= i < j < length xs@.
--
-- * @i@ is the greatest index such that
--   @p (fromJust (summaryMay (drop i xs)))@ is
--   @True@, or @Nothing@ if there is no such index.
-- * @j@ is the least index such that
--   @p (fromJust (summaryMay (drop j xs)))@ is
--   @False@, or @Nothing@ if there is no such index
--
-- ==== __Examples__
--
-- @
-- import "Data.Monoid" (Sum(..))
--
-- newtype Elem = E Int deriving Show
--
-- instance Measured Elem where
--   type Measure Elem = Sum Int
--   measure (E x) = Sum x
-- @
--
-- >>> let xs = fromList [E 1, E 2, E 3, E 4]
--
-- The summaries of the suffixes of @xs@ by index are:
--
-- * @0: measure (E 1) <> measure (E 2) <> measure (E 3) <> measure (E 4) = Sum 10@.
-- * @1: measure (E 2) <> measure (E 3) <> measure (E 4) = Sum 9@.
-- * @2: measure (E 3) <> measure (E 4) = Sum 7@.
-- * @3: measure (E 4) = Sum 4@.
--
-- >>> binarySearchSuffix (> Sum 4) xs
-- (Just 2,Just 3)
--
-- @
--                  ╭──────────┬──────────┬──────────┬──────────╮
-- index:           │        0 │        1 │        2 │        3 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- suffix summary:  │   Sum 10 │    Sum 9 │    Sum 7 |    Sum 4 │
--                  ├──────────┼──────────┼──────────┼──────────┤
-- (> Sum 4):       │     True │     True │     True │    False │
--                  ╰──────────┴──────────┴──────────┴──────────╯
-- result:                                  ( Just 2 ,   Just 3 )
-- @
--
-- >>> binarySearchSuffix (> Sum 20) xs
-- (Nothing,Just 0)
--
-- @
--                           ╭──────────┬──────────┬──────────┬──────────╮
-- index:                    │        0 │        1 │        2 │        3 │
--                           ├──────────┼──────────┼──────────┼──────────┤
-- suffix summary:           │   Sum 10 │    Sum 9 │    Sum 7 |    Sum 4 │
--                           ├──────────┼──────────┼──────────┼──────────┤
-- (> Sum 20):               │    False │    False │    False │    False │
--                           ╰──────────┴──────────┴──────────┴──────────╯
-- result:         ( Nothing ,   Just 0 )
-- @
--
binarySearchSuffix
  :: Measured a => (Measure a -> Bool) -> MSeq a -> (Maybe Int, Maybe Int)
binarySearchSuffix p = \case
  MTree x xs -> case xs of
    MBin _ rv rx rl rr
      | p rv -> let !i = goR rx rl rr
                in if i == 0
                   then let !j = T.size xs in (Just j, Nothing)
                   else let !j = T.size xs - i in (Just j, Just (j+1))
      | p v -> (Just 0, Just 1)
      | otherwise -> (Nothing, Just 0)
      where
        v = measure x <> rv
    MTip
      | p (measure x) -> (Just 0, Nothing)
      | otherwise -> (Nothing, Just 0)
  MEmpty -> (Nothing, Nothing)
  where
    goR !x !l r = case r of
      MBin rsz rv rx rl rr
        | p rv -> goR rx rl rr
        | p v -> rsz
        | otherwise -> go (1 + rsz) v l
        where
          v = measure x <> rv
      MTip
        | p v -> 0
        | otherwise -> go 1 v l
        where
          v = measure x
    go !i !vup = \case
      MBin _ _ x l r
        | p v -> go i vup r
        | p v' -> i + T.size r
        | otherwise -> go (1 + T.size r + i) v' l
        where
          v = r T.<>> vup
          v' = measure x <> v
      MTip -> error "MSeq.binarySearchSuffix: bad p"
{-# INLINE binarySearchSuffix #-}

----------
-- Force
----------

-- | Reduce a sequence to normal form, given functions to reduce its contents.
liftRnf2 :: (Measure a -> ()) -> (a -> ()) -> MSeq a -> ()
liftRnf2 g f = \case
  MTree x xs -> f x `seq` T.liftRnf2 g f xs
  MEmpty -> ()
{-# INLINE liftRnf2 #-}

--------
-- Util
--------

fromMTree :: Measured a => MTree a -> MSeq a
fromMTree t = case T.uncons t of
  U.SNothing -> MEmpty
  U.SJust (U.S2 x xs) -> MTree x xs
{-# INLINE fromMTree #-}

-- See Note [compareLength]
compareLength :: MSeq a -> MSeq b -> Ordering
compareLength l r = case l of
  MTree _ xs -> case r of
    MTree _ ys -> compareSize xs ys
    MEmpty -> GT
  MEmpty -> case r of
    MTree _ _ -> LT
    MEmpty -> EQ
{-# INLINE compareLength #-}

compareSize :: MTree a -> MTree b -> Ordering
compareSize l r = case l of
  MBin szl _ _ _ _ -> case r of
    MBin szr _ _ _ _ -> compare szl szr
    MTip -> GT
  MTip -> case r of
    MBin _ _ _ _ _ -> LT
    MTip -> EQ
{-# INLINE compareSize #-}

----------
-- Build
----------

-- WARNING
--
-- The functions below are similar but they should not be mixed together! All of
-- them operate on Stack, but what the Stack means is not the same between
-- functions.
--
-- left-to-right, 1 element at a time: ltrPush, ltrFinish
-- left-to-right, many elements at a time: ltrPushMany, ltrFinish
-- right-to-left, 1 element at a time: rtlPush, rtlFinish

-- See Note [fromList implementation] in Data.Seqn.Internal.Seq
-- See Note [concatMap implementation] in Data.Seqn.Internal.Seq

ltrPush :: Measured a => Stack a -> a -> Stack a
ltrPush stk y = case stk of
  Push x MTip stk' -> ltrPushLoop stk' x 1 (T.singleton y)
  _ -> Push y MTip stk
{-# INLINABLE ltrPush #-}

ltrPushLoop :: Measured a => Stack a -> a -> Int -> MTree a -> Stack a
ltrPushLoop stk y !ysz ys = case stk of
  Push x xs@(MBin xsz _ _ _ _) stk'
    | xsz == ysz -> ltrPushLoop stk' x sz (T.binn sz y xs ys)
    where
      sz = xsz+xsz+1
  _ -> Push y ys stk
{-# INLINABLE ltrPushLoop #-}

rtlPush :: Measured a => a -> Stack a -> Stack a
rtlPush x = \case
  Push y MTip stk' -> rtlPushLoop x 1 (T.singleton y) stk'
  stk -> Push x MTip stk
{-# INLINABLE rtlPush #-}

rtlPushLoop :: Measured a => a -> Int -> MTree a -> Stack a -> Stack a
rtlPushLoop x !xsz xs = \case
  Push y ys@(MBin ysz _ _ _ _) stk'
    | xsz == ysz -> rtlPushLoop x sz (T.binn sz y xs ys) stk'
    where
      sz = xsz+xsz+1
  stk -> Push x xs stk
{-# INLINABLE rtlPushLoop #-}

ltrPushMany :: Measured a => Stack a -> a -> MTree a -> Stack a
ltrPushMany stk y ys = case stk of
  Push x xs stk'
    | ysz > xsz `div` 2 -> ltrPushManyLoop stk' x xsz xs y ysz ys
    | otherwise -> Push y ys stk
    where
      xsz = 1 + T.size xs
      ysz = 1 + T.size ys
  Nil -> Push y ys Nil
{-# INLINABLE ltrPushMany #-}

ltrPushManyLoop
  :: Measured a
  => Stack a -> a -> Int -> MTree a -> a -> Int -> MTree a -> Stack a
ltrPushManyLoop stk y !ysz ys z !zsz zs = case stk of
  Push x xs@(MBin xsz1 _ _ _ _) stk'
    | xsz < zsz
    -> ltrPushManyLoop stk' x (xsz + ysz) (T.link y xs ys) z zsz zs
    | yzsz > xsz `div` 2
    -> ltrPushManyLoop stk' x xsz xs y yzsz (T.link z ys zs)
    | otherwise
    -> Push y (T.link z ys zs) stk
    where
      xsz = 1+xsz1
      yzsz = ysz+zsz
  _ -> Push y (T.link z ys zs) stk
{-# INLINABLE ltrPushManyLoop #-}

ltrFinish :: Measured a => Stack a -> MSeq a
ltrFinish = wrapUpStack
  MEmpty
  U.S2
  (\(U.S2 y ys) x xs -> U.S2 x (T.link y xs ys))
  (\(U.S2 y ys) -> MTree y ys)
{-# INLINABLE ltrFinish #-}

rtlFinish :: Measured a => Stack a -> MSeq a
rtlFinish = wrapUpStack
  MEmpty
  U.S2
  (\(U.S2 x xs) y ys -> U.S2 x (T.link y xs ys))
  (\(U.S2 x xs) -> MTree x xs)
{-# INLINABLE rtlFinish #-}

-----------
-- Stream
-----------

-- See Note [Streams] in Data.Seqn.Internal.Seq

stream :: MSeq a -> Stream a
stream !t = Stream step s
  where
    s = case t of
      MTree x xs -> Push x xs Nil
      MEmpty -> Nil
    step = \case
      Nil -> Done
      Push x xs stk -> let !stk' = down xs stk in Yield x stk'
    {-# INLINE [0] step #-}
{-# INLINE stream #-}

streamEnd :: MSeq a -> Stream a
streamEnd !t = Stream step s
  where
    s = case t of
      MTree x xs -> Push x xs Nil
      MEmpty -> Nil
    step = \case
      Nil -> Done
      Push x xs stk -> case rDown x xs stk of
        U.S2 y stk' -> Yield y stk'
    {-# INLINE [0] step #-}
{-# INLINE streamEnd #-}

down :: MTree a -> Stack a -> Stack a
down (MBin _ _ x l r) stk = down l (Push x r stk)
down MTip stk = stk

rDown :: a -> MTree a -> Stack a -> U.S2 a (Stack a)
rDown !y (MBin _ _ x l r) stk = rDown x r (Push y l stk)
rDown y MTip stk = U.S2 y stk

----------
-- Stack
----------

-- This is used in various places. What it stores depends on the specific use
-- case.
data Stack a
  = Push !a !(MTree a) !(Stack a)
  | Nil

wrapUpStack
  :: c -- empty
  -> (a -> MTree a -> b) -- initial
  -> (b -> a -> MTree a -> b) -- fold fun
  -> (b -> c) -- finish
  -> Stack a
  -> c
wrapUpStack z0 f0 f fin = go
  where
    go Nil = z0
    go (Push x xs stk) = go1 (f0 x xs) stk
    go1 !z Nil = fin z
    go1 z (Push x xs stk) = go1 (f z x xs) stk
{-# INLINE wrapUpStack #-}

------------
-- Testing
------------

valid :: (Measured a, Eq (Measure a)) => MSeq a -> Bool
valid = \case
  MTree _ xs -> T.valid xs
  MEmpty -> True

debugShowsPrec :: (Show a, Show (Measure a)) => Int -> MSeq a -> ShowS
debugShowsPrec p = \case
  MTree x xs ->
    showParen (p > 10) $
      showString "MTree " .
      showsPrec 11 x .
      showString " " .
      T.debugShowsPrec 11 xs
  MEmpty -> showString "MEmpty"

----------
-- Error
----------

errorElement :: a
errorElement = error "MSeq: errorElement"