module Data.List.HT.Private where
import Data.List  as List  (find, transpose, unfoldr, isPrefixOf,
                            findIndices, foldl', )
import Data.Maybe as Maybe (fromMaybe, catMaybes, )
import Data.Maybe.HT       (toMaybe, )
import Control.Monad       (guard, msum, )
import Data.Tuple.HT       (mapPair, mapFst, mapSnd, forcePair, )
import qualified Data.List.Key.Private   as Key
import qualified Data.List.Match.Private as Match
import Prelude hiding (unzip, break, span, )
inits :: [a] -> [[a]]
inits xt =
   [] :
   case xt of
      [] -> []
      x:xs -> map (x:) (inits xs)
inits98 :: [a] -> [[a]]
inits98 []     = [[]]
inits98 (x:xs) = [[]] ++ map (x:) (inits98 xs)
inits98' :: [a] -> [[a]]
inits98' =
   foldr (\x prefixes -> [] : map (x:) prefixes) [[]]
tails :: [a] -> [[a]]
tails xt =
   uncurry (:) $
   case xt of
      [] -> ([],[])
      xxs@(_:xs) -> (xxs, tails xs)
tails' :: [a] -> [[a]]
tails' = fst . breakAfter null . iterate tail
tails98            :: [a] -> [[a]]
tails98 []         = [[]]
tails98 xxs@(_:xs) = xxs : tails98 xs
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy = Key.groupBy
group :: (Eq a) => [a] -> [[a]]
group = groupBy (==)
unzip :: [(a,b)] -> ([a],[b])
unzip =
   forcePair .
   foldr (\ (x,y) ~(xs,ys) -> (x:xs,y:ys)) ([],[])
partition :: (a -> Bool) -> [a] -> ([a], [a])
partition p =
   forcePair .
   foldr
      (\x ~(y,z) ->
         if p x
           then (x : y, z)
           else (y, x : z))
      ([],[])
span, break :: (a -> Bool) -> [a] -> ([a],[a])
span p =
   let recourse xt =
          forcePair $
          fromMaybe ([],xt) $
          do (x,xs) <- viewL xt
             guard $ p x
             return $ mapFst (x:) $ recourse xs
   in  recourse
break p =  span (not . p)
chop :: (a -> Bool) -> [a] -> [[a]]
chop p =
   uncurry (:) .
   foldr (\ x ~(y,ys) -> if p x then ([],y:ys) else ((x:y),ys) ) ([],[])
chop' :: (a -> Bool) -> [a] -> [[a]]
chop' p =
   let recourse =
          uncurry (:) .
          mapSnd (switchL [] (const recourse)) .
          break p
   in  recourse
chopAtRun :: (Eq a) => (a -> Bool) -> [a] -> [[a]]
chopAtRun p =
   let recourse [] = [[]]
       recourse y =
          let (z,zs) = break p (dropWhile p y)
          in z : recourse zs
   in  recourse
breakAfter :: (a -> Bool) -> [a] -> ([a], [a])
breakAfter p =
   let recourse [] = ([],[])
       recourse (x:xs) =
          mapFst (x:) $
          if p x
            then ([],xs)
            else recourse xs
   in  forcePair . recourse
segmentAfter :: (a -> Bool) -> [a] -> [[a]]
segmentAfter p =
   uncurry (:) .
   foldr
      (\x ~(y,ys) ->
         mapFst (x:) $
         if p x then ([],y:ys) else (y,ys))
      ([],[])
propSegmentAfterConcat :: Eq a => (a -> Bool) -> [a] -> Bool
propSegmentAfterConcat p xs =
   concat (segmentAfter p xs) == xs
propSegmentAfterNumSeps :: (a -> Bool) -> [a] -> Bool
propSegmentAfterNumSeps p xs =
   length (filter p xs) == length (tail (segmentAfter p xs))
propSegmentAfterLasts :: (a -> Bool) -> [a] -> Bool
propSegmentAfterLasts p =
   all (p . last) . init . segmentAfter p
propSegmentAfterInits :: (a -> Bool) -> [a] -> Bool
propSegmentAfterInits p =
   all (all (not . p) . init) . init . segmentAfter p
propSegmentAfterInfinite :: (a -> Bool) -> a -> [a] -> Bool
propSegmentAfterInfinite p x =
   flip seq True . (!!100) . concat . segmentAfter p . cycle . (x:)
segmentBefore :: (a -> Bool) -> [a] -> [[a]]
segmentBefore p =
   uncurry (:) .
   foldr
      (\ x ~(y,ys) ->
         let xs = x:y
         in  if p x then ([],xs:ys) else (xs,ys))
      ([],[])
propSegmentBeforeConcat :: Eq a => (a -> Bool) -> [a] -> Bool
propSegmentBeforeConcat p xs =
   concat (segmentBefore p xs) == xs
propSegmentBeforeNumSeps :: (a -> Bool) -> [a] -> Bool
propSegmentBeforeNumSeps p xs =
   length (filter p xs) == length (tail (segmentBefore p xs))
propSegmentBeforeHeads :: (a -> Bool) -> [a] -> Bool
propSegmentBeforeHeads p =
   all (p . head) . tail . segmentBefore p
propSegmentBeforeTails :: (a -> Bool) -> [a] -> Bool
propSegmentBeforeTails p =
   all (all (not . p) . tail) . tail . segmentBefore p
propSegmentBeforeInfinite :: (a -> Bool) -> a -> [a] -> Bool
propSegmentBeforeInfinite p x =
   flip seq True . (!!100) . concat . segmentBefore p . cycle . (x:)
removeEach :: [a] -> [(a, [a])]
removeEach =
   map (\(ys, pivot, zs) -> (pivot,ys++zs)) . splitEverywhere
splitEverywhere :: [a] -> [([a], a, [a])]
splitEverywhere xs =
   map
      (\(y, zs0) ->
         case zs0 of
            z:zs -> (y,z,zs)
            [] -> error "splitEverywhere: empty list")
      (init (zip (inits xs) (tails xs)))
splitLast :: [a] -> ([a], a)
splitLast [] = error "splitLast: empty list"
splitLast [x] = ([], x)
splitLast (x:xs) =
   let (xs', lastx) = splitLast xs in (x:xs', lastx)
propSplitLast :: Eq a => [a] -> Bool
propSplitLast xs =
   splitLast xs  ==  (init xs, last xs)
viewL :: [a] -> Maybe (a, [a])
viewL (x:xs) = Just (x,xs)
viewL []     = Nothing
viewR :: [a] -> Maybe ([a], a)
viewR =
   foldr (\x -> Just . forcePair . maybe ([],x) (mapFst (x:))) Nothing
propViewR :: Eq a => [a] -> Bool
propViewR xs =
   maybe True
      ((init xs, last xs) == )
      (viewR xs)
switchL :: b -> (a -> [a] -> b) -> [a] -> b
switchL n _ [] = n
switchL _ j (x:xs) = j x xs
switchL' :: b -> (a -> [a] -> b) -> [a] -> b
switchL' n j =
   maybe n (uncurry j) . viewL
switchR :: b -> ([a] -> a -> b) -> [a] -> b
switchR n j =
   maybe n (uncurry j) . viewR
propSwitchR :: Eq a => [a] -> Bool
propSwitchR xs =
   switchR True (\ixs lxs -> ixs == init xs && lxs == last xs) xs
dropWhileRev :: (a -> Bool) -> [a] -> [a]
dropWhileRev p =
   foldr (\x xs -> if p x && null xs then [] else x:xs) []
dropWhileRev' :: (a -> Bool) -> [a] -> [a]
dropWhileRev' p =
   concat . init . segmentAfter (not . p)
takeWhileRev :: (a -> Bool) -> [a] -> [a]
takeWhileRev p =
   last . segmentAfter (not . p)
takeWhileRev' :: (a -> Bool) -> [a] -> [a]
takeWhileRev' p =
   (\xs -> if fst (head xs)
             then map snd xs
             else []) .
   last . Key.aux groupBy (==) p
takeWhileRev'' :: (a -> Bool) -> [a] -> [a]
takeWhileRev'' p =
   foldl (\xs x -> if p x then xs++[x] else []) []
maybePrefixOf :: Eq a => [a] -> [a] -> Maybe [a]
maybePrefixOf (x:xs) (y:ys) = guard (x==y) >> maybePrefixOf xs ys
maybePrefixOf [] ys = Just ys
maybePrefixOf _  [] = Nothing
partitionMaybe :: (a -> Maybe b) -> [a] -> ([b], [a])
partitionMaybe f =
   forcePair .
   foldr
      (\x -> maybe (mapSnd (x:)) (\y -> mapFst (y:)) (f x))
      ([],[])
takeWhileJust :: [Maybe a] -> [a]
takeWhileJust =
   foldr (\x acc -> maybe [] (:acc) x) []
unzipEithers :: [Either a b] -> ([a], [b])
unzipEithers =
   forcePair .
   foldr (either (\x -> mapFst (x:)) (\y -> mapSnd (y:))) ([],[])
sieve, sieve', sieve'', sieve''' :: Int -> [a] -> [a]
sieve k =
   unfoldr (\xs -> toMaybe (not (null xs)) (head xs, drop k xs))
sieve' k = map head . sliceVertical k
sieve'' k x = map (x!!) [0,k..(length x1)]
sieve''' k = map head . takeWhile (not . null) . iterate (drop k)
propSieve :: Eq a => Int -> [a] -> Bool
propSieve n x =
   sieve n x == sieve'  n x   &&
   sieve n x == sieve'' n x
sliceHorizontal, sliceHorizontal', sliceHorizontal'', sliceHorizontal''' ::
   Int -> [a] -> [[a]]
sliceHorizontal n =
   map (sieve n) . take n . iterate (drop 1)
sliceHorizontal' n =
   foldr (\x ys -> let y = last ys in Match.take ys ((x:y):ys)) (replicate n [])
sliceHorizontal'' n =
   reverse . foldr (\x ~(y:ys) -> ys ++ [x:y]) (replicate n [])
sliceHorizontal''' n =
   take n . transpose . takeWhile (not . null) . iterate (drop n)
propSliceHorizontal :: Eq a => Int -> [a] -> Bool
propSliceHorizontal n x =
   sliceHorizontal n x == sliceHorizontal'   n x &&
   sliceHorizontal n x == sliceHorizontal''  n x &&
   sliceHorizontal n x == sliceHorizontal''' n x
sliceVertical, sliceVertical' :: Int -> [a] -> [[a]]
sliceVertical n =
   map (take n) . takeWhile (not . null) . iterate (drop n)
      
sliceVertical' n =
   unfoldr (\x -> toMaybe (not (null x)) (splitAt n x))
propSliceVertical :: Eq a => Int -> [a] -> Bool
propSliceVertical n x =
   take 100000 (sliceVertical n x) == take 100000 (sliceVertical' n x)
propSlice :: Eq a => Int -> [a] -> Bool
propSlice n x =
   
   sliceHorizontal n x == transpose (sliceVertical  n x)  &&
   sliceVertical  n x == transpose (sliceHorizontal n x)
search :: (Eq a) => [a] -> [a] -> [Int]
search sub str = findIndices (isPrefixOf sub) (tails str)
replace :: Eq a => [a] -> [a] -> [a] -> [a]
replace src dst =
   let recourse [] = []
       recourse str@(s:ss) =
          fromMaybe
             (s : recourse ss)
             (fmap ((dst++) . recourse) $
              maybePrefixOf src str)
   in  recourse
markSublists :: (Eq a) => [a] -> [a] -> [Maybe [a]]
markSublists sub ys =
   let ~(hd', rest') =
          foldr (\c ~(hd, rest) ->
                   let xs = c:hd
                   in  case maybePrefixOf sub xs of
                         Just suffix -> ([], Nothing : Just suffix : rest)
                         Nothing -> (xs, rest)) ([],[]) ys
   in  Just hd' : rest'
replace' :: (Eq a) => [a] -> [a] -> [a] -> [a]
replace' src dst xs =
   concatMap (fromMaybe dst) (markSublists src xs)
propReplaceId :: (Eq a) => [a] -> [a] -> Bool
propReplaceId xs ys =
   replace xs xs ys == ys
propReplaceCycle :: (Eq a) => [a] -> [a] -> Bool
propReplaceCycle xs ys =
   replace xs ys (cycle xs) == cycle ys
replace'' :: (Eq a) => [a] -> [a] -> [a] -> [a]
replace'' src dst =
    foldr (\x xs -> let y=x:xs
                    in  if isPrefixOf src y
                          then dst ++ drop (length src) y
                          else y) []
multiReplace :: Eq a => [([a], [a])] -> [a] -> [a]
multiReplace dict =
   let recourse [] = []
       recourse str@(s:ss) =
          fromMaybe
             (s : recourse ss)
             (msum $
              map (\(src,dst) ->
                      fmap ((dst++) . recourse) $
                      maybePrefixOf src str) dict)
   in  recourse
multiReplace' :: Eq a => [([a], [a])] -> [a] -> [a]
multiReplace' dict =
   let recourse [] = []
       recourse str@(s:ss) =
          maybe
             (s : recourse ss)
             (\(src, dst) -> dst ++ recourse (Match.drop src str))
             (find (flip isPrefixOf str . fst) dict)
   in  recourse
propMultiReplaceSingle :: Eq a => [a] -> [a] -> [a] -> Bool
propMultiReplaceSingle src dst x =
   replace src dst x == multiReplace [(src,dst)] x
shear :: [[a]] -> [[a]]
shear =
   map catMaybes .
   shearTranspose .
   transposeFill
transposeFill :: [[a]] -> [[Maybe a]]
transposeFill =
   unfoldr (\xs ->
      toMaybe (not (null xs))
         (mapSnd (dropWhileRev null) $ unzipCons xs))
unzipCons :: [[a]] -> ([Maybe a], [[a]])
unzipCons =
   unzip .
   map ((\my -> (fmap fst my, maybe [] snd my)) . viewL)
unzipConsSkew :: [[a]] -> ([Maybe a], [[a]])
unzipConsSkew =
   let aux [] [] = ([],[])  
       aux xs ys = mapSnd (xs:) $ prep ys
       prep =
          forcePair .
          switchL ([],[])
             (\y ys ->
                let my = viewL y
                in  mapFst (fmap fst my :) $
                    aux (maybe [] snd my) ys)
   in  prep
shear' :: [[a]] -> [[a]]
shear' xs@(_:_) =
   let (y:ys,zs) = unzip (map (splitAt 1) xs)
       zipConc (a:as) (b:bs) = (a++b) : zipConc as bs
       zipConc [] bs = bs
       zipConc as [] = as
   in  y : zipConc ys (shear' (dropWhileRev null zs))
              
shear' [] = []
shearTranspose :: [[a]] -> [[a]]
shearTranspose =
   foldr zipConsSkew []
zipConsSkew :: [a] -> [[a]] -> [[a]]
zipConsSkew xt yss =
   uncurry (:) $
   case xt of
      x:xs -> ([x], zipCons xs yss)
      [] -> ([], yss)
zipCons :: [a] -> [[a]] -> [[a]]
zipCons (x:xs) yt =
   let (y,ys) = switchL ([],[]) (,) yt
   in  (x:y) : zipCons xs ys
zipCons [] ys = ys
zipCons' :: [a] -> [[a]] -> [[a]]
zipCons' (x:xs) (y:ys) = (x:y) : zipCons' xs ys
zipCons' [] ys = ys
zipCons' xs [] = map (:[]) xs
outerProduct :: (a -> b -> c) -> [a] -> [b] -> [[c]]
outerProduct f xs ys = map (flip map ys . f) xs
takeWhileMulti :: [a -> Bool] -> [a] -> [a]
takeWhileMulti [] _  = []
takeWhileMulti _  [] = []
takeWhileMulti aps@(p:ps) axs@(x:xs) =
   if p x
      then x : takeWhileMulti aps xs
      else takeWhileMulti ps axs
takeWhileMulti' :: [a -> Bool] -> [a] -> [a]
takeWhileMulti' ps xs =
   concatMap fst (tail
      (scanl (flip span . snd) (undefined,xs) ps))
propTakeWhileMulti :: (Eq a) => [a -> Bool] -> [a] -> Bool
propTakeWhileMulti ps xs =
   takeWhileMulti ps xs == takeWhileMulti' ps xs
foldl'r, foldl'rStrict, foldl'rNaive ::
   (b -> a -> b) -> b -> (c -> d -> d) -> d -> [(a,c)] -> (b,d)
foldl'r f b0 g d0 =
   mapFst ($b0) .
   foldr (\(a,c) ~(k,d) -> (\b -> k $! f b a, g c d)) (id,d0)
foldl'rStrict f b0 g d0 =
   mapFst ($b0) .
   foldr (\(a,c) ~(k,d) -> ((,) $! (\b -> k $! f b a)) $! g c d) (id,d0)
foldl'rNaive f b g d xs =
   mapPair (foldl' f b, foldr g d) $ unzip xs
propFoldl'r :: (Eq b, Eq d) =>
   (b -> a -> b) -> b -> (c -> d -> d) -> d -> [(a,c)] -> Bool
propFoldl'r f b g d xs =
   foldl'r f b g d xs == foldl'rNaive f b g d xs
iterateUntilCycle :: (Eq a) => (a -> a) -> a -> [a]
iterateUntilCycle f a =
   let as = iterate f a
   in  (a:) $ map fst $
       takeWhile (uncurry (/=)) $
       zip (tail as) (concatMap (\ai->[ai,ai]) as)
iterateUntilCycleP :: (Eq a) => (a -> a) -> a -> [a]
iterateUntilCycleP f a =
   let as = iterate f a
   in  map fst $
       takeWhile (\(a1,(a20,a21)) -> a1/=a20 && a1/=a21) $
       zip as (pairs (tail as))
pairs :: [t] -> [(t, t)]
pairs [] = []
pairs (_:[]) = error "pairs: odd number of elements"
pairs (x0:x1:xs) = (x0,x1) : pairs xs
rotate, rotate', rotate'' :: Int -> [a] -> [a]
rotate n x =
   Match.take x (drop (mod n (length x)) (cycle x))
rotate' n x =
   uncurry (flip (++))
           (splitAt (mod n (length x)) x)
rotate'' n x =
   Match.take x (drop n (cycle x))
propRotate :: Eq a => Int -> [a] -> Bool
propRotate n x =
   rotate n x == rotate'  n x &&
   rotate n x == rotate'' n x
mergeBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
mergeBy = Key.mergeBy
allEqual :: Eq a => [a] -> Bool
allEqual = and . mapAdjacent (==)
isAscending :: (Ord a) => [a] -> Bool
isAscending = and . isAscendingLazy
isAscendingLazy :: (Ord a) => [a] -> [Bool]
isAscendingLazy = mapAdjacent (<=)
mapAdjacent :: (a -> a -> b) -> [a] -> [b]
mapAdjacent f xs = zipWith f xs (tail xs)
range :: Num a => Int -> [a]
range n = take n (iterate (+1) 0)
padLeft :: a -> Int -> [a] -> [a]
padLeft  c n xs = replicate (n  length xs) c ++ xs
padRight, padRight1 :: a -> Int -> [a] -> [a]
padRight  c n xs = take n $ xs ++ repeat c
padRight1 c n xs = xs ++ replicate (n  length xs) c
iterateAssociative :: (a -> a -> a) -> a -> [a]
iterateAssociative op a =
   foldr (\pow xs -> pow : concatMap (\x -> [x, op x pow]) xs)
         undefined (iterate (\x -> op x x) a)
iterateLeaky :: (a -> a -> a) -> a -> [a]
iterateLeaky op x =
   let merge (a:as) b = a : merge b as
       merge _ _ = error "iterateLeaky: an empty list cannot occur"
       sqrs = map (\y -> op y y) z
       z = x : merge sqrs (map (op x) sqrs)
   in  z