module Library.List.Properties where

open import Library.Fun
open import Library.Nat
open import Library.List
open import Library.Equality
open import Library.Equality.Reasoning
open import Library.Logic

::-injective : ∀{A : Set} {x y : A} {xs ys : List A} -> x :: xs == y :: ys -> x == y  xs == ys
::-injective refl = refl , refl

++-length : ∀{A : Set} (xs ys : List A) -> length (xs ++ ys) == length xs + length ys
++-length []        ys = refl
++-length (_ :: xs) ys = cong succ (++-length xs ys)

++-assoc : ∀{A : Set} (xs ys zs : List A) -> xs ++ (ys ++ zs) == (xs ++ ys) ++ zs
++-assoc []        ys zs = refl
++-assoc (x :: xs) ys zs = cong (x ::_) (++-assoc xs ys zs)

++-unit-l : ∀{A : Set} (xs : List A) -> [] ++ xs == xs
++-unit-l _ = refl

++-unit-r : ∀{A : Set} (xs : List A) -> xs ++ [] == xs
++-unit-r []        = refl
++-unit-r (x :: xs) = cong (x ::_) (++-unit-r xs)

reverse-++ : ∀{A : Set} (xs ys : List A) -> reverse (xs ++ ys) == reverse ys ++ reverse xs
reverse-++ [] ys = symm (++-unit-r (reverse ys))
reverse-++ (x :: xs) ys =
  begin
    reverse ((x :: xs) ++ ys)           ==⟨⟩
    reverse (x :: (xs ++ ys))           ==⟨⟩
    reverse (xs ++ ys) ++ [ x ]         ==⟨ cong (_++ [ x ]) (reverse-++ xs ys) 
    (reverse ys ++ reverse xs) ++ [ x ] ==⟨ symm (++-assoc (reverse ys) (reverse xs) [ x ]) 
    reverse ys ++ (reverse xs ++ [ x ]) ==⟨⟩
    (reverse ys ++ reverse (x :: xs))
  end

reverse-involution : ∀{A : Set} (xs : List A) -> reverse (reverse xs) == xs
reverse-involution [] = refl
reverse-involution (x :: xs) =
  begin
    reverse (reverse (x :: xs))           ==⟨⟩
    reverse (reverse xs ++ [ x ])         ==⟨ reverse-++ (reverse xs) [ x ] 
    reverse [ x ] ++ reverse (reverse xs) ==⟨⟩
    x :: reverse (reverse xs)             ==⟨ cong (x ::_) (reverse-involution xs) 
    (x :: xs)
  end

lemma-reverse-onto : ∀{A : Set} (xs ys : List A) -> reverse-onto xs ys == reverse xs ++ ys
lemma-reverse-onto [] ys = refl
lemma-reverse-onto (x :: xs) ys =
  begin
    reverse-onto (x :: xs) ys   ==⟨⟩
    reverse-onto xs (x :: ys)   ==⟨ lemma-reverse-onto xs (x :: ys) 
    reverse xs ++ (x :: ys)     ==⟨⟩
    reverse xs ++ ([ x ] ++ ys) ==⟨ ++-assoc (reverse xs) [ x ] ys 
    (reverse xs ++ [ x ]) ++ ys ==⟨⟩
    (reverse (x :: xs) ++ ys)
  end

fast-reverse-correct : ∀{A : Set} (xs : List A) -> fast-reverse xs == reverse xs
fast-reverse-correct xs =
  begin
    fast-reverse xs    ==⟨⟩
    reverse-onto xs [] ==⟨ lemma-reverse-onto xs [] 
    reverse xs ++ []   ==⟨ ++-unit-r (reverse xs) 
    reverse xs
  end

map-compose : ∀{A B C : Set} (f : B -> C) (g : A -> B) (xs : List A) -> map f (map g xs) == map (f  g) xs
map-compose f g [] = refl
map-compose f g (x :: xs) =
  begin
    map f (map g (x :: xs))       ==⟨⟩
    map f (g x :: map g xs)       ==⟨⟩
    f (g x) :: map f (map g xs)   ==⟨⟩
    (f  g) x :: map f (map g xs) ==⟨ cong ((f  g) x ::_) (map-compose f g xs) 
    (f  g) x :: map (f  g) xs   ==⟨⟩
    map (f  g) (x :: xs)
  end

all-++ : ∀{A : Set} {xs ys : List A} (P : A -> Set) -> All P xs -> All P ys -> All P (xs ++ ys)
all-++ {xs = []} P ps qs = qs
all-++ {xs = x :: xs} P (px , ps) qs = px , all-++ P ps qs

implies-all : ∀{A : Set} {P Q : A -> Set} -> ({x : A} -> P x -> Q x) -> {xs : List A} -> All P xs -> All Q xs
implies-all imp {[]} <> = <>
implies-all imp {_ :: _} (p , ps) = imp p , implies-all imp ps