open import Paths
open import Arith::Fin
open import Algebra::Formula

open data List (A : Type) : Type
| nil
| infixr :< A (List A)
  tighter =

variable A B C : Type

def length (l : List A) : Nat
| nil ⇒ 0
| _ :< lsuc (length l)

def infix !! (l : List A) (i : Fin (length l)) : A
| a :< _, fzeroa
| _ :< l, fsuc il !! i

overlap def infixr ++ : BinOp (List A)
| nil, ysys
| xs, nilxs
| a :< xs, ysa :< (xs ++ ys)
tighter =

def concat (l : List (List A)) : List A
| nilnil
| xs :< xssxs ++ concat xss

private def ++-assoc-lemma : Associative {A := List A} (++)
| nil, _, _ ⇒ idp
| x :< xs, _, _ ⇒ pmap (x :<) ++-assoc
inline def ++-assoc {xs ys zs : List A} ⇒ ++-assoc-lemma xs ys zs

def ++nil {l : List A} : l ++ nil = l
| {A}, {nil} ⇒ idp
| {A}, {a :< l} ⇒ pmap (a :<) ++nil

def map (f : AB) (l : List A) : List B
| f, nilnil
| f, a :< lf a :< map f l

def lengthMap (f : AB) (l : List A) : length (map f l) = length l
| f, nilidp
| f, a :< lpmap suc (lengthMap f l)

def mapComp (g : BC) (f : AB) (l : List A) : map (\xg (f x)) l = map g (map f l)
| _, _, nilidp
| g, f, a :< lpmap (g _ :<) (mapComp g f l)

def mapId (l : List A) : map (\xx) l = l
| nilidp
| a :< lpmap (a :<) (mapId l)

def headDef (x : A) (xs : List A) : A
| x, nilx
| _, a :< _ ⇒ a

def splitAt (n : Nat) (l : List A) : Σ (List A) ** (List A)
| 0, l ⇒ (nil, l)
| suc n, nil ⇒ (nil, nil)
| suc n, a :< llet
  // No pattern let yet, sorry!
  | t := splitAt n l
  in (a :< t.1, t.2)

def take (n : Nat) (l : List A) ⇒ (splitAt n l).1

def drop (n : Nat) (l : List A) ⇒ (splitAt n l).2

def replace (l : List A) (i s : Nat) (r : List A) ⇒
  let
  | t := splitAt i l
  in t.1 ++ r ++ drop s t.2

def splitAt-appendLem (n : Nat) (l : List A) : take n l ++ drop n l = l
| 0, lidp
| suc n, nilidp
| suc n, a :< lpmap (a :<) (splitAt-appendLem n l)

def slice (l : List A) (i s : Nat) ⇒ take s (drop i l)

def slice-appendLem (l : List A) (i s : Nat)
   : take i l ++ (slice l i s ++ drop s (drop i l)) = l
| l, 0, ssplitAt-appendLem s l
| nil, suc i, 0 ⇒ idp
| nil, suc i, suc sidp
| a :< l, suc i, spmap (a :<) (slice-appendLem l i s)