\import Algebra.Meta
\import Arith.Nat
\import Data.Bool
\import Data.Fin
\import Data.List(List, length)
\import Data.Maybe
\import Data.Or
\import Function
\import Function.Meta ($)
\import Logic
\import Meta
\import Order.PartialOrder
\import Paths
\import Paths.Meta
\import Set
\import Set.Fin

\func mkArray {A : \Type} {n : Nat} (f : Fin n -> A) => \new Array A n f

\func arrayExt {A : \Type} {n : Nat} {l l' : Array A n} (p : \Pi (j : Fin n) -> l j = l' j) : l = l'
  => path (\lam i => \new Array A n (\lam j => p j i))

\func tail {A : \Type} (l : Array A) : Array A \elim l
  | nil => nil
  | _ :: a => a

\func taild {n : Nat} {A : Fin (suc n) -> \Type} (a : DArray A) : DArray (\lam j => A (suc j)) \elim a
  | _ :: a => a

\func array-unext {A : \Type} {n : Nat} {l l' : Array A n} (p : l = {Array A} l') (j : Fin n) : l j = l' j \elim n, l, l', j
  | suc n, a :: l, a1 :: l', 0 => unhead p
  | suc n, a :: l, a1 :: l', suc j => array-unext (pmap tail p) j

\func len=0 {A : \Type} {l : Array A} (p : l.len = 0) : l = nil \elim l
  | nil => idp

\func map {A B : \Type} (f : A -> B) (as : Array A) : Array B \cowith
  | len => as.len
  | at i => f (as i)

\func map_:: {A B : \Type} (f : A -> B) {a : A} {l : Array A} : map f (a :: l) = f a :: map f l => idp

\func cong_:: {A : \Type} {a a' : A} (p : a = a') {l l' : Array A} (q : l = l') : a :: l = a' :: l'
  => path \lam i => p i :: q i

\func \infixr 5 ++' {A : \Type} (xs : Array A) {n : Nat} (ys : Array A n) : Array A (xs.len Nat.+ n) \elim xs
  | nil => ys
  | a :: xs => a :: xs ++' ys
  \where {
    \func ++'_index-left {A : \Type} {l : Array A} {n : Nat} {m : Array A n} {i : Fin l.len} : (l ++' m) (fin-inc i) = l i \elim l, i
      | a :: l, 0 => idp
      | a :: l, suc i => ++'_index-left

    \func ++'_index-right {A : \Type} {l : Array A} {n : Nat} {m : Array A n} {i : Fin n} : (l ++' m) (fin-raise i) = m i \elim l
      | nil => idp
      | a :: l => ++'_index-right

    \func split-index {n m : Nat} (i : Fin (n Nat.+ m))
      : Or (\Sigma (j : Fin n) (i = fin-inc j)) (\Sigma (j : Fin m) (i = fin-raise j))
      => rewriteI (OrFin.aux.ret_f i) $ cases (OrFin.aux.f i) \with {
        | inl j => inl (j,idp)
        | inr j => inr (j,idp)
      }
  }

\func ++'-split {A : \Type} {n m : Nat} {l : Array A (n Nat.+ m)} : l = (\new Array A n \lam i => l (fin-inc i)) ++' (\new Array A m \lam j => l (fin-raise j)) \elim n, l
  | 0, l => idp
  | suc n, a :: l => cong ++'-split

\func map_++' {A B : \Type} (f : A -> B) {l : Array A} {n : Nat} {l' : Array A n} : map f (l ++' l') = map f l ++' map f l' \elim l
  | nil => idp
  | :: a l => path (\lam i => f a :: map_++' f i)

\func map2_++' {A B C : \Type} (f : A -> B -> C) {n : Nat} {l1 : Array A n} {l2 : Array B n} {m : Nat} {l1' : Array A m} {l2' : Array B m}
  : mkArray (\lam j => f (++' l1 l1' j) (++' l2 l2' j)) = mkArray (\lam j => f (l1 j) (l2 j)) ++' mkArray (\lam j => f (l1' j) (l2' j)) \elim n, l1, l2
  | 0, nil, nil => idp
  | suc n, a1 :: l1, a2 :: l2 => path (\lam i => f a1 a2 :: map2_++' f i)

\func \infixr 5 ++ {A : \Type} (xs ys : Array A) : Array A \elim xs
  | nil => ys
  | :: a xs => a :: xs ++ ys
  \where {
    \func index-left {A : \Type} {l m : Array A} (i : Fin l.len) : Fin (DArray.len {l ++ m}) \elim l, i
      | a :: l, 0 => 0
      | a :: l, suc i => suc (index-left i)

    \func ++_index-left {A : \Type} {l m : Array A} (i : Fin l.len) : (l ++ m) (index-left i) = l i \elim l, i
      | a :: l, 0 => idp
      | a :: l, suc i => ++_index-left i

    \lemma index-left-nat {A : \Type} {l m : Array A} {i : Fin l.len} : index-left {A} {l} {m} i = {Nat} i \elim l, i
      | a :: l, 0 => idp
      | a :: l, suc i => pmap suc index-left-nat

    \lemma index-left-inj {A : \Type} {l m : Array A} : IsInj (index-left {A} {l} {m})
      => \lam p => nat_fin_= $ inv index-left-nat *> p *> index-left-nat

    \func index-right {A : \Type} {l m : Array A} (i : Fin m.len) : Fin (DArray.len {l ++ m}) \elim l
      | nil => i
      | a :: l => suc (index-right i)

    \func ++_index-right {A : \Type} {l m : Array A} {i : Fin m.len} : (l ++ m) (index-right i) = m i \elim l
      | nil => idp
      | a :: l => ++_index-right

    \lemma index-right-nat {A : \Type} {l m : Array A} {i : Fin m.len} : index-right {A} {l} i = {Nat} l.len Nat.+ i \elim l
      | nil => idp
      | a :: l => pmap suc index-right-nat

    \lemma index-right-inj {A : \Type} {l m : Array A} : IsInj (index-right {A} {l} {m})
      => \lam p => nat_fin_= $ NatSemiring.cancel-left l.len $ inv index-right-nat *> p *> index-right-nat

    \lemma index-left/=right {A : \Type} {l m m' : Array A} {i : Fin l.len} {j : Fin m'.len} : index-left {A} {l} {m} i /= {Nat} index-right {A} {l} j
      => \lam p => NatSemiring.<-irreflexive $ (fin_< i) NatSemiring.<∘l rewrite (inv index-left-nat *> p *> index-right-nat) (NatSemiring.<=_+ <=-refl zero<=_)

    \func split-index {A : \Type} {l m : Array A} (i : Fin (DArray.len {l ++ m}))
      : Or (\Sigma (j : Fin l.len) (i = index-left j)) (\Sigma (j : Fin m.len) (i = index-right j)) \elim l, i
      | nil, i => inr (i,idp)
      | a :: l, 0 => inl (0,idp)
      | a :: l, suc i => \case split-index i \with {
        | inl r => inl (suc r.1, \lam i => suc (r.2 i))
        | inr r => inr (r.1, \lam i => suc (r.2 i))
      }

    \func ++-all {A : \Type} {P : A -> \Type} {l l' : Array A} (Pl : \Pi (i : Fin l.len) -> P (l i)) (Pl' : \Pi (i : Fin l'.len) -> P (l' i)) (j : Fin (l ++ l').len) : P ((l ++ l') j)
      => \case ++.split-index j \with {
        | inl (i,p) => rewrite (p,++_index-left) (Pl i)
        | inr (i,p) => rewrite (p,++_index-right) (Pl' i)
      }

    \func index-big {A : \Type} {ls : Array (Array A)} (i : Fin ls.len) (j : Fin (DArray.len {ls i})) : Fin (DArray.len {Big (++) nil ls}) \elim ls, i
      | l :: ls, 0 => index-left j
      | l :: ls, suc i => index-right (index-big i j)

    \func Big++-index {A : \Type} {ls : Array (Array A)} {i : Fin ls.len} {j : Fin (DArray.len {ls i})} : Big (++) nil ls (index-big i j) = ls i j \elim ls, i
      | ls :: l, 0 => ++_index-left j
      | ls :: l, suc i => ++_index-right *> Big++-index
  }

\func ++_++' {A : \Type} {l : Array A} {n : Nat} {l' : Array A n} : l ++ l' = l ++' l' \elim l
  | nil => idp
  | a :: l => pmap (a ::) ++_++'

\func map_++ {A B : \Type} (f : A -> B) {l l' : Array A} : map f (l ++ l') = map f l ++ map f l' \elim l
  | nil => idp
  | :: a l => pmap (f a ::) (map_++ f)

\func ++_nil {A : \Type} {l : Array A} : l ++ nil = l \elim l
  | nil => idp
  | :: a l => pmap (a ::) ++_nil

\lemma len_++ {A : \Type} {l l' : Array A} : DArray.len {l ++ l'} = l.len Nat.+ l'.len \elim l
  | nil => idp
  | a :: l => pmap suc len_++

\func unhead {A : \Type} {a a' : A} {l l' : Array A} (p : a :: l = a' :: l') : a = a'
  => pmap (headDef a) p
  \where
    \func headDef {A : \Type} (def : A) (l : Array A) : A \elim l
      | nil => def
      | a :: _ => a

\func untail {A : \Type} {a a' : A} {l l' : Array A} (p : a :: l = a' :: l') : l = l'
  => pmap (tailDef l) p
  \where
    \func tailDef {A : \Type} (def : Array A) (l : Array A) : Array A \elim l
      | nil => def
      | _ :: l => l

\func ++-cancel-left {A : \Type} {l l1 l2 : Array A} (p : l ++ l1 = l ++ l2) : l1 = l2 \elim l
  | nil => p
  | :: a l => ++-cancel-left (untail p)

\func ++-cancel-right {A : \Type} {l l1 l2 : Array A} (p : l1 ++ l = l2 ++ l) : l1 = l2 \elim l1, l2
  | nil, nil => idp
  | nil, :: a l2 => absurd (contr p)
  | :: a l1, nil => absurd (contr (inv p))
  | :: a1 l1, :: a2 l2 => path (\lam i => unhead p i :: ++-cancel-right (untail p) i)
  \where
    \lemma contr {A : \Type} {l l' : Array A} {a : A} (p : l = a :: l' ++ l) : Empty
      => \case NatSemiring.cancel-right {0} {suc l'.len} l.len (pmap (DArray.len {__}) p *> pmap suc len_++)

\func ++-assoc {A : \Type} {xs ys zs : Array A} : (xs ++ ys) ++ zs = xs ++ (ys ++ zs) \elim xs
  | nil => idp
  | :: x xs => pmap (x ::) ++-assoc

\func replicate_+ {A : \Type} {n m : Nat} {a : A} : replicate (n Nat.+ m) a = replicate n a ++ replicate m a \elim n
  | 0 => idp
  | suc n => pmap (a ::) replicate_+

\func Index {A : \Type} (x : A) (l : Array A) => \Sigma (i : Fin l.len) (l i = x)

\func index-left {A : \Type} {x : A} {l l' : Array A} (i : Index x l) : Index x (l ++ l') \elim l, i
  | nil, ((), _)
  | :: y l, (0, e) => (0, e)
  | :: y l, (suc i, e) => \have (j,p) => index-left (i, e) \in (suc j, p)

\func index-right {A : \Type} {x : A} {l l' : Array A} (i : Index x l') : Index x (l ++ l') \elim l
  | nil => i
  | :: y l => \have (j,p) => index-right i \in (suc j, p)

\func filter {A : \Type} (p : A -> Bool) (l : Array A) : Array A \elim l
  | nil => nil
  | :: a l => if (p a) (a :: filter p l) (filter p l)

\lemma filter-sat {A : \Type} (p : A -> Bool) (l : Array A) (i : Fin (DArray.len {filter p l})) : p (filter p l i) = true \elim l
  | :: a l => cases (p a arg addPath, i) \with {
    | false, _, i => filter-sat p l i
    | true, q, 0 => q
    | true, q, suc i => filter-sat p l i
  }

\func filter_true {A : \Type} {p : A -> Bool} {l : Array A} (c : \Pi (j : Fin l.len) -> p (l j) = true) : filter p l = l \elim l
  | nil => idp
  | a :: l => rewrite (c 0) $ pmap (a ::) $ filter_true \lam j => c (suc j)

\func filter_false {A : \Type} {p : A -> Bool} {l : Array A} (c : \Pi (j : Fin l.len) -> p (l j) = false) : filter p l = nil \elim l
  | nil => idp
  | a :: l => rewrite (c 0) $ filter_false \lam j => c (suc j)

\func filter-index {A : \Type} (p : A -> Bool) (l : Array A) (i : Fin l.len) (px : p (l i) = true) : Index (l i) (filter p l) \elim l, i
  | nil, ()
  | :: a l, 0 => rewrite px (0, idp)
  | :: a l, suc i => mcases {if} \with {
    | true => \have (j,q) => filter-index p l i px \in (suc j, q)
    | false => filter-index p l i px
  }

\func Big {A B : \Type} (op : A -> B -> B) (b : B) (l : Array A) : B \elim l
  | nil => b
  | :: a l => op a (Big op b l)

\func Big++_map {A B : \Type} (f : A -> B) {ls : Array (Array A)} : Big (++) nil (map (map f) ls) = map f (Big (++) nil ls) \elim ls
  | nil => idp
  | l :: ls => pmap (_ ++) (Big++_map f) *> inv (map_++ f)

\func Big++-split {A : \Type} {l : Array A} : l = Big (++) nil (map (`:: nil) l) \elim l
  | nil => idp
  | a :: l => pmap (a ::) Big++-split

\func filterMap {A B : \Type} (f : A -> Maybe B) (l : Array A) : Array B \elim l
  | nil => nil
  | :: a l => \case f a \with {
    | nothing => filterMap f l
    | just b => b :: filterMap f l
  }

\func filterMap-index {A B : \Type} (f : A -> Maybe B) (l : Array A) {b : B} {j : Fin l.len} (p : f (l j) = just b) : Index b (filterMap f l) \elim l, j
  | :: a l, 0 => rewrite p (0,idp)
  | :: a l, suc j => mcases \with {
    | nothing => filterMap-index f l p
    | just _ => \have (i,q) => filterMap-index f l p \in (suc i, q)
  }

\func indexed (l : DArray) : DArray (\lam j => \Sigma (Fin l.len) (l.A j)) \cowith
  | at j => (j, l j)

\func indexed' (l : DArray) : Array (\Sigma (j : Fin l.len) (l.A j)) \cowith
  | len => l.len
  | at j => (j, l j)

\func replicate {A : \Type} (n : Nat) (a : A)
  => \new Array A n (\lam _ => a)

\func keep {A : \Type} {P : A -> \Prop} (D : \Pi (a : A) -> Dec (P a)) (l : Array A) : Array A \elim l
  | nil => nil
  | a :: l => \case D a \with {
    | yes _ => a :: keep D l
    | no _ => keep D l
  }
  \where {
    \lemma satisfies {A : \Type} {P : A -> \Prop} (D : \Pi (a : A) -> Dec (P a)) {l : Array A} {i : Fin (DArray.len {keep D l})} : P (keep D l i) \elim l
      | a :: l => cases (D a, i) \with {
        | yes e, 0 => e
        | yes e, suc i => satisfies D
        | no n, i => satisfies D
      }

    \func element {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} {j : Fin l.len} (p : P (l j)) : Index (l j) (keep D l)
      => rewrite keep=remove (remove.element $ later \lam q => q p)

    \func preimage {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} (i : Fin (DArray.len {keep D l})) : \Sigma (j : Fin l.len) (keep D l i = l j) \elim l
      | nil => (i,idp)
      | a :: l => cases (D a, i) \with {
        | yes e, 0 => (0,idp)
        | yes e, suc i => \let t => preimage i \in (suc t.1, t.2)
        | no q, i => \let t => preimage i \in (suc t.1, t.2)
      }

    \lemma no-repeats {A : \Type} {l : Array A} (p : \Pi {i j : Fin l.len} -> l i = l j -> i = j) {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {i j : Fin (DArray.len {keep D l})} (q : keep D l i = keep D l j) : i = j \elim l
      | a :: l => cases (D a, i, j, q) \with {
        | no _, i, j, q => no-repeats (\lam {i'} {j'} s => pmap (fpred i') $ p {suc i'} {suc j'} s) q
        | yes _, 0, 0, q => idp
        | yes s, 0, suc j, q => \let t => preimage j \in \case p {0} {suc t.1} (q *> t.2)
        | yes _, suc i, 0, q => \let t => preimage i \in \case p {suc t.1} {0} (inv t.2 *> q)
        | yes s, suc i, suc j, q => pmap fsuc (no-repeats (\lam {i'} {j'} s => pmap (fpred i') $ p {suc i'} {suc j'} s) q)
      }
  }

\func keep_++ {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l l' : Array A} : keep D (l ++ l') = keep D l ++ keep D l' \elim l
  | nil => idp
  | a :: l => mcases \with {
    | yes p => pmap (a ::) keep_++
    | no n => keep_++
  }

\func keep-all {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} (p : \Pi (j : Fin l.len) -> P (l j)) : keep D l = l \elim l
  | nil => idp
  | a :: l => rewrite (dec_yes_reduce (p 0)) $ pmap (a ::) $ keep-all \lam j => p (suc j)

\func keep-none {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} (p : \Pi (j : Fin l.len) -> Not (P (l j))) : keep D l = nil \elim l
  | nil => idp
  | a :: l => rewrite (dec_no_reduce (p 0)) $ keep-none (\lam j => p (suc j))

\func keep-unique {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} {j : Fin l.len} (Pj : P (l j)) (nPj : \Pi (i : Fin l.len) -> P (l i) -> i = j) : keep D l = l j :: nil \elim l, j
  | a :: l, 0 => rewrite (dec_yes_reduce Pj) $ pmap (a ::) {_} {nil} $ keep-none \lam i Pi => \case nPj (suc i) Pi
  | a :: l, suc j => rewrite (dec_no_reduce \lam Pa => \case nPj 0 Pa) $ keep-unique Pj \lam i Pi => unfsuc $ nPj (suc i) Pi

\func remove {A : \Type} {P : A -> \Prop} (D : \Pi (a : A) -> Dec (P a)) (l : Array A) : Array A \elim l
  | nil => nil
  | a :: l => \case D a \with {
    | yes _ => remove D l
    | no _ => a :: remove D l
  }
  \where {
    \lemma no-element {A : \Type} {P : A -> \Prop} (D : \Pi (a : A) -> Dec (P a)) {l : Array A} {i : Fin (DArray.len {remove D l})} (p : P (remove D l i)) : Empty \elim l
      | nil => \case i
      | a :: l => cases (D a, i, p) \with {
        | yes e, i, p => no-element D p
        | no q, 0, p => q p
        | no q, suc i, p => no-element D p
      }

    \func element {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} {j : Fin l.len} (p : Not (P (l j))) : Index (l j) (remove D l) \elim l
      | a :: l => \case \elim j, \elim p \with {
        | 0, p => mcases \with {
          | yes e => absurd (p e)
          | no q => (0,idp)
        }
        | suc j, p => mcases \with {
          | yes e => element p
          | no q => \let t => element p
                    \in (suc t.1, t.2)
        }
      }

    \func preimage {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} (i : Fin (DArray.len {remove D l})) : \Sigma (j : Fin l.len) (remove D l i = l j) \elim l
      | nil => (i,idp)
      | a :: l => cases (D a, i) \with {
        | yes e, i => \let t => preimage i \in (suc t.1, t.2)
        | no q, 0 => (0,idp)
        | no q, suc i => \let t => preimage i \in (suc t.1, t.2)
      }

    \lemma no-repeats {A : \Type} {l : Array A} (p : \Pi {i j : Fin l.len} -> l i = l j -> i = j) {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {i j : Fin (DArray.len {remove D l})} (q : remove D l i = remove D l j) : i = j \elim l
      | nil => \case i
      | a :: l => cases (D a, i, j, q) \with {
        | yes _, i, j, q => no-repeats (\lam {i'} {j'} s => pmap (fpred i') $ p {suc i'} {suc j'} s) q
        | no _, 0, 0, q => idp
        | no s, 0, suc j, q => \let t => preimage j \in \case p {0} {suc t.1} (q *> t.2)
        | no _, suc i, 0, q => \let t => preimage i \in \case p {suc t.1} {0} (inv t.2 *> q)
        | no s, suc i, suc j, q => pmap fsuc (no-repeats (\lam {i'} {j'} s => pmap (fpred i') $ p {suc i'} {suc j'} s) q)
      }

    \func equals {A : \Type} {P Q : A -> \Prop} (pq : \Pi {a : A} -> P a -> Q a) (qp : \Pi {a : A} -> Q a -> P a)
                 {DP : \Pi (a : A) -> Dec (P a)} {DQ : \Pi (a : A) -> Dec (Q a)} {l : Array A} : remove DP l = remove DQ l \elim l
      | nil => idp
      | a :: l => cases (DP a) \with {
        | yes e => rewrite (dec_yes_reduce (pq e)) (equals pq qp)
        | no p => rewrite (dec_no_reduce \lam q => p (qp q)) $ pmap (a ::) (equals pq qp)
      }
  }

\func remove-none {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} (p : \Pi (j : Fin l.len) -> Not (P (l j))) : remove D l = l \elim l
  | nil => idp
  | a :: l => rewrite (dec_no_reduce (p 0)) $ pmap (a ::) $ remove-none \lam j => p (suc j)

\func remove_map {A B : \Type} (f : A -> B) {P : B -> \Prop} {D : \Pi (b : B) -> Dec (P b)} {l : Array A} : remove D (map f l) = map f (remove (\lam a => D (f a)) l) \elim l
  | nil => idp
  | a :: l => mcases \with {
    | yes p => remove_map f
    | no q => pmap (f a ::) (remove_map f)
  }

\func keep=remove {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} : keep D l = remove (\lam a => NotDec (D a)) l \elim l
  | nil => idp
  | a :: l => cases (D a) \with {
    | yes e => rewrite (dec_no_reduce $ later \lam p => p e) $ pmap (a ::) keep=remove
    | no q => rewrite (dec_yes_reduce q) keep=remove
  }

\func remove=keep {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} : remove D l = keep (\lam a => NotDec (D a)) l \elim l
  | nil => idp
  | a :: l => cases (D a) \with {
    | yes e => rewrite (dec_no_reduce $ later \lam p => p e) $ remove=keep
    | no q => rewrite (dec_yes_reduce q) $ pmap (a ::) remove=keep
  }

\func remove_remove {A : \Type} {P Q : A -> \Prop} {DP : \Pi (a : A) -> Dec (P a)} {DQ : \Pi (a : A) -> Dec (Q a)} {l : Array A}
  : remove DP (remove DQ l) = remove (\lam a => Dec_|| (DP a) (DQ a)) l \elim l
  | nil => idp
  | a :: l => mcases \with {
    | yes q, yes _x => remove_remove
    | yes q, no p => absurd $ p (byRight q)
    | no q, yes e => mcases \with {
      | yes p => remove_remove
      | no p => absurd $ ||.rec' p q e
    }
    | no q, no r => mcases \with {
      | yes p => absurd $ r (byLeft p)
      | no n => pmap (a ::) remove_remove
    }
  }

\func remove-swap {A : \Type} {P Q : A -> \Prop} {DP : \Pi (a : A) -> Dec (P a)} {DQ : \Pi (a : A) -> Dec (Q a)} {l : Array A}
  : remove DP (remove DQ l) = remove DQ (remove DP l)
  => remove_remove *> remove.equals (\lam {a} => ||.rec' byRight byLeft) (\lam {a} => ||.rec' byRight byLeft) *> inv remove_remove

\func keep_remove=keep {A : \Type} {P Q : A -> \Prop} {DP : \Pi (a : A) -> Dec (P a)} {DQ : \Pi (a : A) -> Dec (Q a)} (PQ : \Pi {a : A} -> P a -> Q a -> Empty) {l : Array A} : keep DP (remove DQ l) = keep DP l \elim l
  | nil => idp
  | a :: l => mcases \with {
    | yes q, yes p => absurd (PQ p q)
    | yes q, no n => keep_remove=keep PQ
    | no n, yes p => rewrite (dec_yes_reduce p) $ pmap (a ::) (keep_remove=keep PQ)
    | no n, no n1 => rewrite (dec_no_reduce n1) (keep_remove=keep PQ)
  }

\lemma remove<= {A : \Type} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} : DArray.len {remove D l} <= l.len \elim l
  | nil => <=-refl
  | a :: l => mcases \with {
    | yes p => remove<= <=∘ id<=suc
    | no n => suc<=suc remove<=
  }

\lemma count_remove_yes {A : DecSet} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} {a : A} (Pa : P a) : count (remove D l) a = 0 \elim l
  | nil => idp
  | x :: l => mcases \with {
    | yes p => count_remove_yes Pa
    | no q => mcases \with {
      | yes p => absurd $ q $ transport P p Pa
      | no _ => count_remove_yes Pa
    }
  }

\lemma count_remove_no {A : DecSet} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l : Array A} {a : A} (Pa : Not (P a)) : count (remove D l) a = count l a \elim l
  | nil => idp
  | x :: l => mcases \with {
    | yes p, yes q => absurd $ Pa $ transportInv P q p
    | yes p, no q => count_remove_no Pa
    | no p, yes q => rewrite (decideEq=_reduce q) $ pmap suc $ count_remove_no Pa
    | no p, no q => rewrite (decideEq/=_reduce q) $ count_remove_no Pa
  }

\lemma count_remove {A : DecSet} {P : A -> \Prop} {D : \Pi (a : A) -> Dec (P a)} {l l' : Array A} {a : A} (p : count l a = count l' a)
  : count (remove D l) a = count (remove D l') a
  => \case D a \with {
    | yes Pa => count_remove_yes Pa *> inv (count_remove_yes Pa)
    | no Pa => count_remove_no Pa *> p *> inv (count_remove_no Pa)
  }

\func removeElem {A : DecSet} (a : A) (l : Array A) : Array A
  => remove (decideEq a) l

\func remove1 {A : DecSet} (a : A) {n : Nat} (l : Array A (suc n)) : Array A n \elim n, l
  | 0, l => nil
  | suc n, a' :: l => \case decideEq a a' \with {
    | yes _ => l
    | no _ => a' :: remove1 a l
  }

\func remove1-surj {A : DecSet} (a : A) {n : Nat} (l : Array A (suc n)) (i : Fin n) : \Sigma (j : Fin (suc n)) (remove1 a l i = l j) \elim n, l
  | 0, b :: l => \case i
  | suc n, b :: l => mcases \with {
    | yes p => (suc i, idp)
    | no q => \case \elim i \with {
      | 0 => (0, idp)
      | suc i => \have t => remove1-surj a l i \in (suc t.1, t.2)
    }
  }

\lemma remove1/= {A : DecSet} {a : A} {n : Nat} {l : Array A (suc n)} (inj : IsInj l) {j : Fin n} : remove1 a l j /= a \elim n, l
  | 0, b :: l => \case j
  | suc n, b :: l => mcases \with {
    | yes p => \lam q => \case inj {suc j} {0} (q *> p)
    | no q => \case \elim j \with {
      | 0 => \lam p => q (inv p)
      | suc j => remove1/= \lam {i} {j} p => pmap (fpred i) $ inj {suc i} {suc j} p
    }
  }

\lemma remove1-inj {A : DecSet} {a : A} {n : Nat} {l : Array A (suc n)} (inj : IsInj l) {i j : Fin n} (p : remove1 a l i = remove1 a l j) : i = j \elim n, l
  | 0, b :: l => \case i
  | suc n, b :: l => cases (decideEq a b, p) \with {
    | yes _, p => pmap (fpred i) $ inj {suc i} {suc j} p
    | no q, p => \case \elim i, \elim j, \elim p \with {
      | 0, 0, p => idp
      | 0, suc j, p => \have t => remove1-surj a l j \in \case inj {suc t.1} {0} $ inv (p *> t.2)
      | suc i, 0, p => \have t => remove1-surj a l i \in \case inj {0} {suc t.1} $ inv p *> t.2
      | suc i, suc j, p => pmap fsuc $ remove1-inj (\lam {i} {j} p => pmap (fpred i) $ inj {suc i} {suc j} p) p
    }
  }

\func nub {A : DecSet} (l : Array A) : Array A \elim l
  | nil => nil
  | a :: l => a :: removeElem a (nub l)

\func nub-isSurj {A : DecSet} (l : Array A) (i : Fin l.len) : \Sigma (j : Fin (DArray.len {nub l})) (nub l j = l i) \elim l, i
  | a :: l, 0 => (0,idp)
  | a :: l, suc i =>
    \case decideEq a (l i) \with {
      | yes e => (0,e)
      | no q =>
        \have | t => nub-isSurj l i
              | s => remove-isSurj a (nub l) t.1 \lam p => q $ inv p *> t.2
        \in (suc s.1, s.2 *> t.2)
    }
  \where {
    \func remove-isSurj {A : DecSet} (a : A) (l : Array A) (i : Fin l.len) (p : l i /= a) : \Sigma (j : Fin (DArray.len {removeElem a l})) (removeElem a l j = l i) \elim l
      | nil => \case i
      | a' :: l => unfold removeElem $ mcases \with {
        | yes e => cases (i,p) \with {
          | 0, p => absurd $ p (inv e)
          | suc i, p => remove-isSurj a l i p
        }
        | no q => cases (i,p) \with {
          | 0, p => (0,idp)
          | suc i, p => \let t => remove-isSurj a l i p \in (suc t.1, t.2)
        }
      }
  }

\func nub-preimage {A : DecSet} {l : Array A} (j : Fin (DArray.len {nub l})) : \Sigma (i : Fin l.len) (nub l j = l i) \elim l, j
  | a :: l, 0 => (0, idp)
  | a :: l, suc j => \have | (k,p) => remove.preimage j
                           | (i,q) => nub-preimage k
                     \in (suc i, p *> q)

\lemma nub-isInj {A : DecSet} {l : Array A} {i j : Fin (DArray.len {nub l})} (p : nub l i = nub l j) : i = j \elim l, i, j
  | a :: l, 0, 0 => idp
  | a :: l, 0, suc j => absurd $ remove.no-element (decideEq a) p
  | a :: l, suc i, 0 => absurd (remove.no-element (decideEq a) (inv p))
  | a :: l, suc i, suc j => pmap fsuc $ remove.no-repeats nub-isInj p

\func nub_id {A : DecSet} {l : Array A} (inj : IsInj l) : nub l = l \elim l
  | nil => idp
  | a :: l => pmap (a ::) $ remove-none (\lam j p => \case inj {0} {suc (nub-preimage j).1} $ p *> (nub-preimage j).2) *> nub_id \lam p => unfsuc (inj p)

\func insert {A : \Type} (a : A) (l : Array A) (j : Fin (suc l.len)) : Array A (suc l.len) \elim l, j
  | nil, j => a :: nil
  | b :: l, 0 => a :: b :: l
  | b :: l, suc j => b :: insert a l j

\func insert_zro {A : \Type} {n : Nat} {a : A} {l : Array A n} : insert a l 0 = a :: l \elim n, l
  | 0, nil => idp
  | suc n, b :: l => idp

\func insert-index {A : \Type} {a : A} {l : Array A} {j : Fin (suc l.len)} : insert a l j j = a \elim l, j
  | nil, 0 => idp
  | b :: l, 0 => idp
  | b :: l, suc j => insert-index

\func skip {A : \Type} {n : Nat} (l : Array A (suc n)) (k : Fin (suc n)) : Array A n \elim n, l, k
  | n, _ :: l, 0 => l
  | suc n, a :: l, suc k => a :: skip l k
  \where {
    \func newIndex {n : Nat} {j i : Fin (suc n)} (p : i /= j) : Fin n \elim n, j, i
      | _, 0, 0 => absurd (p idp)
      | suc n, 0, suc i => i
      | suc n, suc j, 0 => 0
      | suc n, suc j, suc i => suc $ newIndex (\lam q => p (pmap fsuc q))

    \open NatOrder

    \lemma newIndex_< {n : Nat} {i j k : Fin (suc n)} {pj : j /= i} {pk : k /= i} (q : j < k) : newIndex pj < newIndex pk \elim n, i, j, k, q
      | _, 0, 0, _, _ => absurd (pj idp)
      | _, 0, _, 0, _ => absurd (pk idp)
      | suc n, 0, suc j, suc k, suc<suc q => q
      | suc n, suc i, 0, suc k, q => zero<suc
      | suc n, suc i, suc j, suc k, suc<suc q => suc<suc (newIndex_< q)
  }

\func skip_++' {A : \Type} {n m : Nat} {l : Array A (suc n)} {l' : Array A m} {k : Fin (suc n)} : skip (l ++' l') (fin-inc k) = skip l k ++' l' \elim n, l, k
  | 0, a :: nil, 0 => exts \lam j => skip_0
  | suc n, a :: l, 0 => idp
  | suc n, a :: l, suc k => cong skip_++'

\func skipExt {A : \Type} {n : Nat} {l l' : Array A (suc n)} {k : Fin (suc n)} (p : \Pi (i : Fin (suc n)) -> i /= k -> l i = l' i) : skip l k = skip l' k \elim n, l, l', k
  | 0, l, l', 0 => idp
  | suc n, a :: l, a' :: l', 0 => arrayExt \lam j => p (suc j) (\case __)
  | suc n, a :: l, a' :: l', suc k => pmap2 (::) (p 0 (\case __)) $ skipExt \lam i q => p (suc i) (fsuc/= q)

\func skip_0 {A : \Type} {n : Nat} {l : Array A (suc n)} {j : Fin n} : skip l 0 j = l (suc j) \elim n, l
  | 0, a :: l => idp
  | suc n, a :: l => idp

\func skip_map {A B : \Type} (f : A -> B) {n : Nat} {l : Array A (suc n)} {k : Fin (suc n)} : map f (skip l k) = skip (map f l) k \elim n, l, k
  | 0, a :: l, 0 => idp
  | suc n, a :: l, 0 => idp
  | suc n, a :: l, suc k => path (\lam i => f a :: skip_map f i)

\func replace {A : \Type} (l : Array A) (i : Fin l.len) (a : A) : Array A l.len \elim l, i
  | b :: l, 0 => a :: l
  | b :: l, suc i => b :: replace l i a

\func replace-index {A : \Type} {l : Array A} {i : Fin l.len} {a : A} : replace l i a i = a \elim l, i
  | b :: l, 0 => idp
  | b :: l, suc i => replace-index

\func replace-notIndex {A : \Type} {l : Array A} {i j : Fin l.len} (i/=j : i /= {Nat} j) {a : A} : replace l i a j = l j \elim l, i, j
  | b :: l, 0, 0 => absurd (i/=j idp)
  | b :: l, 0, suc j => idp
  | b :: l, suc i, 0 => idp
  | b :: l, suc i, suc j => replace-notIndex \lam p => i/=j (pmap suc p)

\func replace_insert {A : \Type} {l : Array A} {i : Fin (suc l.len)} {a b : A} : replace (insert a l i) i b = insert b l i \elim l, i
  | nil, 0 => idp
  | c :: l, 0 => idp
  | c :: l, suc i => cong replace_insert

\func skip_replace_= {A : \Type} {n : Nat} {l : Array A (suc n)} {i : Fin (suc n)} {a : A}
  : skip (replace l i a) i = skip l i \elim n, l, i
  | 0, b :: l, 0 => idp
  | suc n, b :: l, 0 => idp
  | suc n, b :: l, suc i => path (\lam i => b :: skip_replace_= i)

\func skip_replace_/= {A : \Type} {n : Nat} {l : Array A (suc n)} {j i : Fin (suc n)} {a : A} (p : j /= i)
  : skip (replace l j a) i = replace (skip l i) (skip.newIndex p) a \elim n, l, j, i
  | _, _, 0, 0 => absurd (p idp)
  | suc n, b :: l, 0, suc 0 => idp
  | suc n, b :: l, 0, suc (suc i) => idp
  | suc n, b :: l, suc j, 0 => idp
  | suc n, b :: l, suc j, suc i => cong (skip_replace_/= _)

\func skip-index {A : \Type} {n : Nat} {l : Array A (suc n)} {i j : Fin (suc n)} {p : j /= i} : skip l i (skip.newIndex p) = l j \elim n, l, i, j
  | _, _, 0, 0 => absurd (p idp)
  | suc n, a :: l, 0, suc j => idp
  | suc n, a :: l, suc i, 0 => idp
  | suc n, a :: l, suc i, suc j => skip-index

\func skip_replicate {A : \Type} {n : Nat} (a : A) {k : Fin (suc n)} : skip (replicate (suc n) a) k = replicate n a \elim n, k
  | 0, 0 => idp
  | suc n, 0 => idp
  | suc n, suc k => path (\lam i => a :: skip_replicate a i)

\func map_replace {A B : \Type} (f : A -> B) {l : Array A} {i : Fin l.len} {a : A}
  : map f (replace l i a) = replace (map f l) i (f a) \elim l, i
  | b :: l, 0 => idp
  | b :: l, suc i => path (\lam i => f b :: map_replace f i)

\func insert_skip {A : \Type} {n : Nat} {l : Array A (suc n)} {k : Fin (suc n)} {a : A}
  : insert a (skip l k) k = replace l k a \elim n, l, k
  | 0, b :: nil, 0 => idp
  | suc n, b :: l, 0 => idp
  | suc n, b :: l, suc k => cong insert_skip

\func skip_insert_= {A : \Type} {n : Nat} {a : A} {l : Array A n} {j : Fin (suc n)} : skip (insert a l j) j = l \elim n, l, j
  | 0, nil, 0 => idp
  | suc n, b :: l, 0 => idp
  | suc n, b :: l, suc j => cong skip_insert_=

\func map_insert {A B : \Type} (f : A -> B) {a : A} {l : Array A} {j : Fin (suc l.len)}
  : map f (insert a l j) = insert (f a) (map f l) j \elim l, j
  | nil, 0 => idp
  | b :: l, 0 => idp
  | b :: l, suc j => path (\lam i => f b :: map_insert f i)

\func count {A : DecSet} (l : Array A) (a : A) : Nat \elim l
  | nil => 0
  | x :: l => \case decideEq a x \with {
    | yes _ => suc (count l a)
    | no _ => count l a
  }

\lemma count<= {A : DecSet} {l : Array A} {a : A} : count l a <= l.len \elim l
  | nil => <=-refl
  | x :: l => mcases \with {
    | yes p => suc<=suc count<=
    | no q => count<= <=∘ id<=suc
  }

\lemma count_++ {A : DecSet} {l l' : Array A} {a : A} : count (l ++ l') a = count l a Nat.+ count l' a \elim l
  | nil => idp
  | x :: l => mcases \with {
    | yes p => pmap suc count_++
    | no q => count_++
  }

\lemma count_Big++ {A : DecSet} {ls : Array (Array A)} {a : A} : count (Big (++) nil ls) a = NatSemiring.BigSum (map (count __ a) ls) \elim ls
  | nil => idp
  | l :: ls => count_++ *> pmap (_ Nat.+) count_Big++

\lemma count-all {A : DecSet} {l : Array A} {a : A} (p : \Pi (j : Fin l.len) -> l j = a) : count l a = l.len \elim l
  | nil => idp
  | x :: l => rewrite (decideEq=_reduce $ inv $ p 0) $ pmap suc $ count-all \lam j => p (suc j)

\lemma count-none {A : DecSet} {l : Array A} {a : A} (p : \Pi (j : Fin l.len) -> l j /= a) : count l a = 0 \elim l
  | nil => idp
  | x :: l => rewrite (decideEq/=_reduce $ /=-sym $ p 0) $ count-none \lam j => p (suc j)

\lemma keep_count {A : DecSet} {l : Array A} {a : A} : keep (decideEq a) l = replicate (count l a) a \elim l
  | nil => idp
  | x :: l => mcases \with {
    | yes p => rewriteI p $ pmap (a ::) keep_count
    | no q => keep_count
  }

\func find {A : \Type} (P : A -> Decide) (l : Array A) : Or (\Sigma (j : Fin l.len) (P (l j)) (\Pi {i : Fin l.len} -> i NatOrder.< j -> P (l i) -> Empty)) (\Pi {j : Fin l.len} -> P (l j) -> Empty) \elim l
  | nil => inr \lam {j} => \case j
  | a :: l => \case decide {P a} \with {
    | yes e => inl (0, e, \case __)
    | no q => \case find P l \with {
      | inl e => inl (suc e.1, e.2, \lam {i} => \case \elim i \with {
        | 0 => \lam _ => q
        | suc i => \lam (NatOrder.suc<suc p) => e.3 p
      })
      | inr p => inr \lam {j} => \case \elim j \with {
        | 0 => q
        | suc j => p
      }
    }
  }

\sfunc index-dec {A : DecSet} (l : Array A) (a : A) : Or (Index a l) (Not (Index a l))
  => \case find (EqualityDecide A __ a) l \with {
       | inl p => inl (p.1,p.2)
       | inr q => inr \lam p => q p.2
     }

\func repeats-dec {A : DecSet} (l : Array A) : Or (\Sigma (i j : Fin l.len) (i /= j) (l i = l j)) (IsInj l) \elim l
  | nil => inr \lam {i} => \case i
  | a :: l => \case index-dec l a \with {
    | inl e => inl (suc e.1, 0, (\case __), e.2)
    | inr q => \case repeats-dec l \with {
      | inl e => inl (suc e.1, suc e.2, fsuc/= e.3, e.4)
      | inr p => inr \lam {i} {j} => \case \elim i, \elim j \with {
        | 0, 0 => \lam _ => idp
        | 0, suc j => \lam r => absurd $ q (j, inv r)
        | suc i, 0 => \lam r => absurd $ q (i, r)
        | suc i, suc j => \lam r => pmap fsuc (p r)
      }
    }
  }

\func forall {A : \Type} (p : A -> Bool) (l : Array A) : Bool \elim l
  | nil => true
  | a :: l => p a and forall p l

\lemma forall-char {A : \Type} {p : A -> Bool} {l : Array A} (s : forall p l = true) (j : Fin l.len) : p (l j) = true \elim l, j
  | a :: l, 0 => (and.toSigma s).1
  | a :: l, suc j => forall-char (and.toSigma s).2 j

\func fit {A : \Type} (a : A) {n : Nat} (l : Array A) : Array A n \elim n, l
  | 0, _ => nil
  | suc n, nil => replicate (suc n) a
  | suc n, b :: l => b :: fit a l

\func fit_< {A : \Type} {a : A} {n : Nat} (l : Array A) {j : Fin n} (p : j NatOrder.< l.len) : fit a l j = l (toFin j p) \elim n, l, j, p
  | suc n, b :: l, 0, _ => idp
  | suc n, b :: l, suc j, NatOrder.suc<suc p => fit_< _ p

\func fit_>= {A : \Type} {a : A} {n : Nat} (l : Array A) {j : Fin n} (p : l.len <= j) : fit a l j = a \elim n, l, j
  | suc n, nil, j => idp
  | suc n, b :: l, 0 => absurd linarith
  | suc n, b :: l, suc j => fit_>= l (suc<=suc.conv p)

\func fit_<' {A : \Type} {a : A} {n : Nat} (l : Array A) {j : Fin l.len} (p : j NatOrder.< n) : fit a l (toFin j p) = l j \elim n, l, j, p
  | suc n, b :: l, 0, _ => idp
  | suc n, b :: l, suc j, NatOrder.suc<suc p => fit_<' _ p

\func fit_map {A B : \Type} (f : A -> B) {a : A} {b : B} (p : f a = b) {n : Nat} {l : Array A} : map f (fit a {n} l) = fit b (map f l) \elim n, l
  | 0, l => idp
  | suc n, nil => pmap (replicate (suc n)) p
  | suc n, a' :: l => path (\lam i => f a' :: fit_map f p i)

\func array/= {A : DecSet} {n : Nat} {l l' : Array A n} (p : l /= l') : \Sigma (j : Fin n) (l j /= l' j) \elim n, l, l'
  | 0, nil, nil => absurd (p idp)
  | suc n, a :: l, a' :: l' => \case decideEq a a' \with {
    | yes a=a' => \have (j,q) => array/= (\lam s => p $ pmap2 (::) a=a' s)
                  \in (suc j, q)
    | no a/=a' => (0, a/=a')
  }

\func toList {A : \Type} (l : Array A) : List A \elim l
  | nil => List.nil
  | a :: l => a List.:: toList l

\lemma toList_length {A : \Type} {l : Array A} : length (toList l) = l.len \elim l
  | nil => idp
  | a :: l => pmap suc toList_length

\func fromList {A : \Type} (l : List A) : Array A (length l) \elim l
  | List.nil => nil
  | a List.:: l => a :: fromList l

\func fromList_toList {A : \Type} {l : Array A} : fromList (toList l) = l \elim l
  | nil => idp
  | a :: l => pmap (a ::) fromList_toList

\func take {A : \Type} (n : Nat) (l : Array A) (p : n <= l.len) : Array A n \elim n, l
  | 0, l => nil
  | suc n, nil => absurd (p NatOrder.zero<suc)
  | suc n, a :: l => a :: take n l (suc<=suc.conv p)

\func take-index {A : \Type} {n : Nat} {l : Array A} {p : n <= l.len} {j : Fin n} : take n l p j = l (fin-inc_<= p j) \elim n, l, j
  | suc n, nil, j => absurd (p NatOrder.zero<suc)
  | suc n, a :: l, 0 => idp
  | suc n, a :: l, suc j => take-index

\func singleAt {A : \Type} {n : Nat} (j : Fin n) (value def : A) : Array A n \cowith
  | at i => \case decideEq j i \with {
    | yes _ => value
    | no _ => def
  }
  \where \protected {
    \func alt {A : \Type} {n : Nat} (j : Fin n) (value def : A) : Array A n \elim n, j
      | suc n, 0 => value :: replicate n def
      | suc n, suc j => def :: alt j value def

    \sfunc char {A : \Type} {n : Nat} {j : Fin n} {value def : A} : singleAt j value def = alt j value def \elim n, j
      | suc n, 0 => idp
      | suc n, suc j => exts (\case \elim __ \with {
        | 0 => idp
        | suc k => mcases {2} \with {
          | yes p => rewrite (decideEq=_reduce $ pmap fsuc p) idp
          | no q => rewrite (decideEq/=_reduce $ fsuc/= q) idp
        }
      }) *> pmap {Array A n} (def :: __) char
  }