module Haskell.Prim.List where

open import Haskell.Prim
open import Haskell.Prim.Bool
open import Haskell.Prim.Tuple
open import Haskell.Prim.Int


--------------------------------------------------
-- List

map : (a  b)  List a  List b
map f []       = []
map f (x  xs) = f x  map f xs

infixr 5 _++_
_++_ :  {@0 } {@0 a : Set }  List a  List a  List a
[]       ++ ys = ys
(x  xs) ++ ys = x  xs ++ ys

filter : (a  Bool)  List a  List a
filter p []       = []
filter p (x  xs) = if p x then x  filter p xs else filter p xs

scanl : (b  a  b)  b  List a  List b
scanl f z []       = z  []
scanl f z (x  xs) = z  scanl f (f z x) xs

scanr : (a  b  b)  b  List a  List b
scanr f z [] = z  []
scanr f z (x  xs) =
  case scanr f z xs of λ where
    []          [] -- impossible
    qs@(q  _)  f x q  qs

scanl1 : (a  a  a)  List a  List a
scanl1 f []       = []
scanl1 f (x  xs) = scanl f x xs

scanr1 : (a  a  a)  List a  List a
scanr1 f []       = []
scanr1 f (x  []) = x  []
scanr1 f (x  xs) =
  case scanr1 f xs of λ where
    []          [] -- impossible
    qs@(q  _)  f x q  qs

takeWhile : (a  Bool)  List a  List a
takeWhile p [] = []
takeWhile p (x  xs) = if p x then x  takeWhile p xs else []

dropWhile : (a  Bool)  List a  List a
dropWhile p [] = []
dropWhile p (x  xs) = if p x then dropWhile p xs else x  xs

span : (a  Bool)  List a  List a × List a
span p [] = [] , []
span p (x  xs) = if p x then first (x ∷_) (span p xs)
                         else ([] , x  xs)

break : (a  Bool)  List a  List a × List a
break p = span (not  p)

zipWith : (a  b  c)  List a  List b  List c
zipWith f []       _        = []
zipWith f _        []       = []
zipWith f (x  xs) (y  ys) = f x y  zipWith f xs ys

zip : List a  List b  List (a × b)
zip = zipWith _,_

zipWith3 : (a  b  c  d)  List a  List b  List c  List d
zipWith3 f []       _        _        = []
zipWith3 f _        []       _        = []
zipWith3 f _        _        []       = []
zipWith3 f (x  xs) (y  ys) (z  zs) = f x y z  zipWith3 f xs ys zs

zip3 : List a  List b  List c  List (a × b × c)
zip3 = zipWith3 _,_,_

unzip : List (a × b)  List a × List b
unzip []              = [] , []
unzip ((x , y)  xys) = (x ∷_) *** (y ∷_) $ unzip xys

unzip3 : List (a × b × c)  List a × List b × List c
unzip3 []                   = [] , [] , []
unzip3 ((x , y , z)  xyzs) =
  case unzip3 xyzs of λ where
    (xs , ys , zs)  x  xs , y  ys , z  zs

takeNat : Nat  List a  List a
takeNat n       [] = []
takeNat zero    xs = []
takeNat (suc n) (x  xs) = x  takeNat n xs

take : (n : Int)  @0  IsNonNegativeInt n   List a  List a
take n xs = takeNat (intToNat n) xs

dropNat : Nat  List a  List a
dropNat n       [] = []
dropNat zero    xs = xs
dropNat (suc n) (_  xs) = dropNat n xs

drop : (n : Int)  @0  IsNonNegativeInt n   List a  List a
drop n xs = dropNat (intToNat n) xs

splitAtNat : (n : Nat)  List a  List a × List a
splitAtNat _       []       = [] , []
splitAtNat 0       xs       = [] , xs
splitAtNat (suc n) (x  xs) = first (x ∷_) (splitAtNat n xs)

splitAt : (n : Int)  @0  IsNonNegativeInt n   List a  List a × List a
splitAt n xs = splitAtNat (intToNat n) xs

head : (xs : List a)  @0  NonEmpty xs   a
head (x  _) = x

tail : (xs : List a)  @0  NonEmpty xs   List a
tail (_  xs) = xs

last : (xs : List a)  @0  NonEmpty xs   a
last (x  [])         = x
last (_  xs@(_  _)) = last xs

init : (xs : List a)  @0  NonEmpty xs   List a
init (x  [])         = []
init (x  xs@(_  _)) = x  init xs