\import Algebra.Meta
\import Algebra.Monoid
\import Arith.Nat
\import Data.Bool
\import Data.Or
\import Function
\import Function.Meta \hiding (#)
\import Logic
\import Logic.Meta
\import Meta
\import Order.LinearOrder
\import Order.PartialOrder
\import Order.StrictOrder
\import Paths
\import Paths.Meta
\import Set

\data List (A : \Type)
  | nil
  | \infixr 5 :: A (List A)

\func length {A : \Type} (l : List A) : Nat \elim l
  | nil => 0
  | :: a l => suc (length l)

\func \infixl 9 !! {A : \Type} (l : List A) (i : Fin (length l)) : A \elim l, i
  | :: a l, 0 => a
  | :: a l, suc i => l !! i

\func \infixr 5 ++ {A : \Type} (xs ys : List A) : List A \elim xs
  | nil => ys
  | :: a xs => a :: xs ++ ys

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

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

\lemma length_++ {A : \Type} {l l' : List A} : length (l ++ l') = length l + length l' \elim l
  | nil => idp
  | :: a l => pmap suc length_++

\func replicate {A : \Type} (n : Nat) (a : A) : List A \elim n
  | 0 => nil
  | suc n => a :: replicate n a

\lemma length_replicate {A : \Type} {n : Nat} {a : A} : length (replicate n a) = n \elim n
  | 0 => idp
  | suc n => pmap suc length_replicate

\func map {A B : \Type} (f : A -> B) (l : List A) : List B \elim l
  | nil => nil
  | :: a l => f a :: map f l

\func length_map {A B : \Type} (f : A -> B) (l : List A) : length (map f l) = length l \elim l
  | nil => idp
  | :: a l => pmap suc (length_map f l)

\func map_comp {A B C : \Type} (g : B -> C) (f : A -> B) (l : List A) : map (\lam x => g (f x)) l = map g (map f l) \elim l
  | nil => idp
  | :: a l => pmap (_ ::) (map_comp g f l)

\func headDef {A : \Type} (x : A) (xs : List A) : A \elim xs
  | nil => x
  | :: a _ => a

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

\instance ListMonoid {A : \Set} : Monoid (List A)
  | ide => nil
  | * => ++
  | ide-left => idp
  | ide-right => ++_nil
  | *-assoc => ++-assoc

\func splitAt {A : \Type} (n : Nat) (l : List A) : \Sigma (List A) (List A) \elim n, l
  | 0, l => (nil, l)
  | suc _, nil => (nil, nil)
  | suc n, :: a l =>
      \let! (l1, l2) => splitAt n l
      \in (a :: l1, l2)
  \where
    \func appendLem {A : \Type} (n : Nat) (l : List A) : take n l ++ drop n l = l \elim n, l
      | 0, l => idp
      | suc n, nil => idp
      | suc n, :: a l => pmap (a ::) (appendLem n l)

\func take {A : \Type} (n : Nat) (l : List A) => (splitAt n l).1

\func drop {A : \Type} (n : Nat) (l : List A) => (splitAt n l).2

\func replace {A : \Type} (l : List A) (i s : Nat) (r : List A) =>
  \let! (l1, l2) => splitAt i l
  \in l1 ++ r ++ drop s l2

\func slice {A : \Type} (l : List A) (i s : Nat) => take s (drop i l)
  \where
    \func appendLem {A : \Type} (l : List A) (i s : Nat) : take i l ++ slice l i s ++ drop s (drop i l) = l \elim l, i, s
      | l, 0, s => splitAt.appendLem s l
      | nil, suc i, 0 => idp
      | nil, suc i, suc s => idp
      | :: a l, suc i, s => pmap (a ::) (appendLem l i s)

\data All {A : \Type} (P : A -> \Prop) (l : List A) \elim l
  | nil => all-nil
  | :: x l => all-cons (P x) (All P l)

\lemma all-map {A B : \Type} (f : A -> B) {P : B -> \Prop} {l : List A} (a : All P (map f l)) : All (\lam a => P (f a)) l \elim l, a
  | nil, all-nil => all-nil
  | :: x l, all-cons p a => all-cons p (all-map f a)

\lemma all-implies {A : \Type} {P Q : A -> \Prop} {l : List A} (a : All P l) (p : All (\lam a => P a -> Q a) l) : All Q l \elim l, a, p
  | nil, all-nil, all-nil => all-nil
  | :: x l, all-cons c a, all-cons f p => all-cons (f c) (all-implies a p)

\lemma all-forall {A : \Type} {P : A -> \Prop} {l : List A} (p : \Pi (a : A) -> P a) : All P l \elim l
  | nil => all-nil
  | :: a l => all-cons (p a) (all-forall p)

\data All2 {A : \Type} (P : A -> A -> \Prop) (l : List A) \elim l
  | nil => all2-nil
  | :: x l => all2-cons (All (P x) l) (All2 P l)

\data AllC {A : \Type} (P : A -> A -> \Prop) (l : List A) \elim l
  | nil => allC-nil
  | :: x nil => allC-single
  | :: x (:: y l) => allC-cons (P x y) (AllC P (y :: l))

\lemma allC-tail {A : \Type} {a : A} {l : List A} {P : A -> A -> \Prop} (a : AllC P (a :: l)) : AllC P l \elim l, a
  | nil, allC-single => allC-nil
  | :: y l, allC-cons p a => a

\func count {A : DecSet} (l : List A) (a : A) : Nat \elim l
  | nil => 0
  | :: x l => \case decideEq x a \with {
    | yes _ => suc (count l a)
    | no _ => count l a
  }
  \where {
    \lemma all-diff {A : DecSet} {l : List A} {a : A} (d : All (a /=) l) : count l a = 0 \elim l, d
      | nil, all-nil => idp
      | :: x l, all-cons p d => mcases \with {
        | yes e => \case p (inv e)
        | no _ => all-diff d
      }
  }

\lemma count_perm {A : DecSet} {l l' : List A} (p : Perm l l') (a : A) : count l a = count l' a \elim l, l', p
  | nil, nil, perm-nil => idp
  | :: x l, :: _ l', perm-:: idp p => rewrite (count_perm p) idp
  | :: x (:: x' l), :: _ (:: _ _), perm-swap idp idp idp => mcases \with {
    | yes e, yes e' => rewrite (decideEq=_reduce e, decideEq=_reduce e') idp
    | yes e, no q => rewrite (decideEq=_reduce e, decideEq/=_reduce q) idp
    | no q, yes e => rewrite (decideEq/=_reduce q, decideEq=_reduce e) idp
    | no q, no q' => rewrite (decideEq/=_reduce q, decideEq/=_reduce q') idp
  }
  | l, l', perm-trans p1 p2 => count_perm p1 a *> count_perm p2 a

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

\func group {A : DecSet} (l : List A) : List (\Sigma A Nat) \elim l
  | nil => nil
  | :: a l => \case group l \with {
    | nil => (a,1) :: nil
    | :: (a',n) l' => \case decideEq a a' \with {
      | yes _ => (a', suc n) :: l'
      | no _ => (a,1) :: (a',n) :: l'
    }
  }

\open Sort

\lemma group-sorted {A : DecSet} {P : Preorder A} {l : List A} (s : Sorted l) : Sorted (map __.1 (group l)) \elim l, s
  | nil, sorted-nil => sorted-nil
  | :: x l, sorted-cons x<=l s => mcases {1} {arg addPath} \with {
    | nil, _ => sorted-cons <=-refl sorted-nil
    | :: (a',n) l', p =>
      \have s' => rewriteF p (group-sorted s)
      \in mcases \with {
        | yes _ => s'
        | no _ => sorted-cons (transport (x <=) (head-lem p) x<=l) s'
      }
  }
  \where {
    \lemma head-lem {A : DecSet} {l : List A} {a x : A} {n : Nat} {l' : List (\Sigma A Nat)} (p : group l = (a,n) :: l') : headDef x l = a \elim l
      | nil => \case p
      | :: y l => mcases {1} p _ \with {
        | nil, p' => pmap (\lam s => (headDef (a,n) s).1) p'
        | :: z l'', p' => mcases p' _ \with {
          | yes e, p'' => e *> pmap (\lam s => (headDef (a,n) s).1) p''
          | no q, p'' => pmap (\lam s => (headDef (a,n) s).1) p''
        }
      }
  }

\lemma group-diff {A : DecSet} {P : Poset A} {l : List A} (s : Sorted l) : All2 (__.1 /= __.1) (group l) \elim l, s
  | nil, sorted-nil => all2-nil
  | :: x l, sorted-cons x<=l s => mcases {1} {arg addPath} \with {
    | nil, _ => all2-cons all-nil all2-nil
    | :: ((a',n)) l', p =>
      \have (all2-cons t t') => rewriteF p (group-diff s)
      \in mcases \with {
        | yes _ => all2-cons t t'
        | no q =>
          \have | s' => rewriteF p (group-sorted s)
                | (all-cons _ r) => diff-lem s' a' a' (transport (x <=) (group-sorted.head-lem p) x<=l) q
          \in all2-cons (all-cons q (all-map __.1 r)) (all2-cons t t')
      }
  }
  \where {
    \lemma diff-lem {A : DecSet} {P : Poset A} {l : List A} (s : Sorted l) {x : A} (y z : A) (p : x <= headDef y l) (q : x /= headDef z l) : All (x /=) l \elim l, s
      | nil, sorted-nil => all-nil
      | :: a l, sorted-cons a<=l s => all-cons q (diff-lem s _ _ (p <=∘ a<=l) (\lam t => q (<=-antisymmetric p (transportInv (a <=) t a<=l))))
  }

\lemma group_count-lem {A : DecSet} {P : Poset A} {l : List A} (s : Sorted l) : All (\lam p => p.2 = count l p.1) (group l) \elim l, s
  | nil, sorted-nil => all-nil
  | :: a l, sorted-cons a<=l s => mcases {2} {arg addPath} \with {
    | nil, p => all-cons (rewrite (decideEq=_reduce idp, group_nil-lem p) idp) all-nil
    | :: (a',n) l', p =>
      \have (all-cons t t') => rewriteF p (group_count-lem s)
      \in mcases {2} \with {
        | yes e =>
          \have (all2-cons t'' _) => rewriteF p (group-diff s)
          \in all-cons (rewrite (decideEq=_reduce e) $ pmap suc t) $ all-implies t' $ all-implies t'' $ all-forall $ \lam q c d => rewrite (decideEq/=_reduce (\lam e' => c (inv e *> e'))) d
        | no q =>
          \have (all-cons _ t'') : All (\lam x => a /= x.1) ((a',n) :: l') => all-map __.1 (group-diff.diff-lem (rewriteF p $ group-sorted s) a a (transport (a <=) (group-sorted.head-lem p) a<=l) q)
          \in all-cons (rewrite (decideEq=_reduce idp) $ pmap suc $ inv $ count.all-diff $ group-diff.diff-lem s a a a<=l $ rewrite (group-sorted.head-lem p) q) $
                all-cons (rewrite (decideEq/=_reduce q) t) $ all-implies t' $ all-implies t'' $ all-forall $ \lam y c d => rewrite (decideEq/=_reduce c) d
      }
  }
  \where {
    \lemma group_nil-lem {A : DecSet} {l : List A} (p : group l = nil) : l = nil \elim l
      | nil => idp
      | :: a l => mcases {1} p _ \with {
        | nil, ()
        | :: ((a',n)) l', p' => mcases p' _ \with {
          | yes p1, ()
          | no n1, ()
        }
      }
  }

\module Sort \where {
  \data Perm {A : \Type} (xs ys : List A) \elim xs, ys
    | nil, nil => perm-nil
    | :: x xs, :: y ys => perm-:: (x = y) (Perm xs ys)
    | :: x (:: x' xs), :: y (:: y' ys) => perm-swap (x = y') (x' = y) (xs = ys)
    | xs, ys => perm-trans {zs : List A} (Perm xs zs) (Perm zs ys)
    \where {
      \func perm-refl {A : \Type} {xs : List A} : Perm xs xs \elim xs
        | nil => perm-nil
        | :: a l => perm-:: idp perm-refl

      \func perm-sym {A : \Type} {xs ys : List A} (p : Perm xs ys) : Perm ys xs \elim xs, ys, p
        | nil, nil, perm-nil => perm-nil
        | :: x xs, :: y ys, perm-:: p p1 => perm-:: (inv p) (perm-sym p1)
        | :: x (:: x' xs), :: y (:: y' ys), perm-swap p p1 p2 => perm-swap (inv p1) (inv p) (inv p2)
        | xs, ys, perm-trans p1 p2 => perm-trans (perm-sym p2) (perm-sym p1)

      \func perm-head {A : \Type} {a : A} {xs ys : List A} : Perm (a :: xs ++ ys) (xs ++ a :: ys) \elim xs
        | nil => perm-refl
        | :: a1 xs => perm-trans (perm-swap idp idp idp) (perm-:: idp perm-head)

      \lemma perm_length {A : \Type} {xs ys : List A} (p : Perm xs ys) : length xs = length ys \elim xs, ys, p
        | nil, nil, perm-nil => idp
        | x :: xs, y :: ys, perm-:: _ q => pmap suc (perm_length q)
        | x :: (x' :: xs), y :: (y' :: _), perm-swap _ _ idp => idp
        | xs, ys, perm-trans p1 p2 => perm_length p1 *> perm_length p2
    }

  \data Sorted {A : Preorder} (xs : List A) \elim xs
    | nil => sorted-nil
    | :: x xs => sorted-cons (x A.<= headDef x xs) (Sorted xs)
    \where {
      \lemma allSorted {A : Preorder} {a1 a2 : A} (l1 l2 : List A) (s : Sorted (a1 :: l1 ++ a2 :: l2)) : a1 A.<= a2
        => aux a1 (a1 :: l1) l2 s
        \where
          \lemma aux {A : Preorder} (a' : A) {a : A} (l1 l2 : List A) (s : Sorted (l1 ++ a :: l2)) : headDef a' (l1 ++ a :: l2) A.<= a \elim l1, s
            | nil, sorted-cons _ s => <=-refl
            | :: a1 l1, sorted-cons a1<=h s => <=-transitive a1<=h (aux a1 l1 l2 s)

      \lemma headSorted {A : Preorder} {l1 l2 : List A} (s : Sorted (l1 ++ l2)) : Sorted l1 \elim l1, s
        | nil, _ => sorted-nil
        | :: a l1, sorted-cons e s => sorted-cons (\case \elim l1, \elim e \with {
          | nil, _ => <=-refl
          | :: a1 l1, e => e
        }) (headSorted s)

      \lemma tailSorted {A : Preorder} (l1 l2 : List A) (s : Sorted (l1 ++ l2)) : Sorted l2 \elim l1, s
        | nil, s => s
        | :: a l1, sorted-cons _ s => tailSorted l1 l2 s
    }

  \open LinearOrder

  \module Insertion \where {
    \func sort {A : Dec} (xs : List A) : List A
      | nil => nil
      | :: a xs => insert a (sort xs)
      \where {
        \func insert {A : Dec} (a : A) (xs : List A) : List A \elim xs
          | nil => a :: nil
          | :: x xs => \case dec<_<= x a \with {
            | inl x<a => x :: insert a xs
            | inr a<=x => a :: x :: xs
          }
      }

    \open sort

    \lemma sort-sorted {A : Dec} (xs : List A) : Sorted (sort xs) \elim xs
      | nil => sorted-nil
      | :: a xs => insert-sorted a (sort-sorted xs)
      \where {
        \lemma insert-sorted {A : Dec} (a : A) {xs : List A} (s : Sorted xs) : Sorted (sort.insert a xs) \elim xs, s
          | nil, _ => sorted-cons <=-refl sorted-nil
          | :: a1 xs, sorted-cons p s1 \as s2 => mcases \with {
            | inl a1<a => sorted-cons (\case \elim xs, \elim p \with {
              | nil, _ => <=-less a1<a
              | :: a2 xs, a1<=a2 => mcases \with {
                | inl _ => a1<=a2
                | inr _ => <=-less a1<a
              }
            }) (insert-sorted a s1)
            | inr a<=a1 => sorted-cons a<=a1 s2
          }
      }

    \func sort-perm {A : Dec} (xs : List A) : Perm xs (sort xs) \elim xs
      | nil => perm-nil
      | :: a l => perm-trans (perm-:: idp (sort-perm l)) (insert-perm a (sort l))
      \where {
        \func insert-perm {A : Dec} (a : A) (xs : List A) : Perm (a :: xs) (insert a xs) \elim xs
          | nil => perm-:: idp perm-nil
          | :: b xs => mcases \with {
            | inl b<=a => perm-trans (perm-swap idp idp idp) (perm-:: idp (insert-perm a xs))
            | inr a<=b => Perm.perm-refl
          }

        \lemma insert-comm {A : Dec} (a a' : A) (l : List A) : insert a (insert a' l) = insert a' (insert a l)
          => \case trichotomy a a' \with {
            | less a<a' => aux a<a' l
            | equals a=a' => rewrite a=a' idp
            | greater a>a' => inv (aux a>a' l)
          } \where {
            \lemma aux {A : Dec} {a a' : A} (a<a' : a < a') (l : List A) : insert a (insert a' l) = insert a' (insert a l) \elim l
              | nil => mcases (idp <|> contradiction)
              | :: a1 l => repeat {3} mcases (idp <|> contradiction <|> pmap (a1 ::) (aux a<a' l))
          }
      }

    \lemma perm_sort {A : Dec} {xs ys : List A} (p : Perm xs ys) : sort xs = sort ys \elim xs, ys, p
      | nil, nil, perm-nil => idp
      | x :: xs, y :: ys, perm-:: p q => pmap2 insert p (perm_sort q)
      | x :: (x' :: xs), _ :: (_ :: _), perm-swap idp idp idp => sort-perm.insert-comm x x' (sort xs)
      | xs, ys, perm-trans p1 p2 => perm_sort p1 *> perm_sort p2

    \lemma sorted_sort {A : Dec} {xs : List A} (s : Sorted xs) : sort xs = xs \elim xs, s
      | nil, sorted-nil => idp
      | x :: xs, sorted-cons p s => pmap (insert x) (sorted_sort s) *> \case \elim xs, \elim p \with {
        | nil, p => idp
        | a :: xs, x<=a => mcases \with {
          | inl a<x => absurd $ <-irreflexive (<-transitive-left a<x x<=a)
          | inr x<=a => idp
        }
      }
  }

  \module RedBlack \where {
    \func sort {A : Dec} (l : List A) => rbTreeToList (aux l rbLeaf) nil
      \where {
        \data Color | red | black

        \data RBTree (A : \Type) | rbLeaf | rbBranch Color (RBTree A) A (RBTree A)

        \func rbTreeToList {A : \Type} (t : RBTree A) (r : List A) : List A \elim t
          | rbLeaf => r
          | rbBranch _ t1 a t2 => rbTreeToList t1 (a :: rbTreeToList t2 r)

        \func aux {A : Dec} (l : List A) (\strict r : RBTree A) : RBTree A \elim l
          | nil => r
          | :: a l => aux l (repaint (insert a r))

        \func repaint {A : \Type} (t : RBTree A) : RBTree A
          | rbBranch red l a r => rbBranch black l a r
          | t => t

        \func insert {A : Dec} (a : A) (t : RBTree A) : RBTree A \elim t
          | rbLeaf => rbBranch red rbLeaf a rbLeaf
          | rbBranch c l1 a1 l2 => \case dec<_<= a1 a \with {
            | inl a1<a => balanceRight c l1 a1 (insert a l2)
            | inr a<=a1 => balanceLeft c (insert a l1) a1 l2
          }

        \func balanceLeft {A : \Type} (c : Color) (\strict l : RBTree A) (v : A) (r : RBTree A) : RBTree A \elim c, l
          | black, rbBranch red (rbBranch red a x b) y c => rbBranch red (rbBranch black a x b) y (rbBranch black c v r)
          | black, rbBranch red a x (rbBranch red b y c) => rbBranch red (rbBranch black a x b) y (rbBranch black c v r)
          | c, a => rbBranch c a v r

        \func balanceRight {A : \Type} (c : Color) (l : RBTree A) (v : A) (\strict r : RBTree A) : RBTree A \elim c, r
          | black, rbBranch red (rbBranch red b y c) z d => rbBranch red (rbBranch black l v b) y (rbBranch black c z d)
          | black, rbBranch red b y (rbBranch red c z d) => rbBranch red (rbBranch black l v b) y (rbBranch black c z d)
          | c, b => rbBranch c l v b
      }

    \open sort

    \func toList {A : \Type} (t : RBTree A) : List A \elim t
      | rbLeaf => nil
      | rbBranch _ t1 a t2 => toList t1 ++ a :: toList t2
      \where {
        \lemma =rbTreeToList {A : \Set} (t : RBTree A) : rbTreeToList t nil = toList t
          => aux t nil *> ++_nil
          \where
            \lemma aux {A : \Set} (t : RBTree A) (l : List A) : rbTreeToList t l = toList t ++ l \elim t
              | rbLeaf => idp
              | rbBranch c t1 a t2 => aux t1 (a :: rbTreeToList t2 l) *> pmap (toList t1 ++ a :: __) (aux t2 l) *> inv ++-assoc
      }

      \lemma sort=insert {A : Dec} (l : List A) : sort l = Insertion.sort l
        => toList.=rbTreeToList _ *> aux=makeTree l nil *> pmap (\lam s => toList (makeTree s)) ++_nil *> toList_mkTree l
        \where {
          \func makeTree {A : Dec} (l : List A) : RBTree A \elim l
            | nil => rbLeaf
            | :: a l => repaint (insert a (makeTree l))

          \lemma toList_repaint {A : \Set} (t : RBTree A) : toList (repaint t) = toList t \elim t
            | rbLeaf => idp
            | rbBranch red t1 a t2 => idp
            | rbBranch black t1 a t2 => idp

          \lemma toList_balanceLeft {A : \Set} (c : Color) (l : RBTree A) (v : A) (r : RBTree A)
            : toList (balanceLeft c l v r) = toList (rbBranch c l v r)
            => mcases {balanceLeft} equation

          \lemma toList_balanceRight {A : \Set} (c : Color) (l : RBTree A) (v : A) (r : RBTree A)
            : toList (balanceRight c l v r) = toList (rbBranch c l v r)
            => mcases {balanceRight} equation

          \lemma insert_++-left {A : Dec} {a a1 : A} (a<=a1 : a <= a1) {l1 l2 : List A} (s : Sorted (l1 ++ a1 :: l2))
            : Insertion.sort.insert a (l1 ++ a1 :: l2) = Insertion.sort.insert a l1 ++ a1 :: l2 \elim l1, s
            | nil, _ => rewrite (dec<=_reduce a<=a1) idp
            | :: a2 l1, sorted-cons _ s => mcases \with {
              | inl a2<a => pmap (a2 ::) (insert_++-left a<=a1 s)
              | inr a<=a2 => idp
            }

          \lemma insert_++-right {A : Dec} {a a1 : A} (a1<a : a1 < a) {l1 l2 : List A} (s : Sorted (l1 ++ a1 :: l2))
            : Insertion.sort.insert a (l1 ++ a1 :: l2) = l1 ++ a1 :: Insertion.sort.insert a l2 \elim l1, s
            | nil, _ => rewrite (dec<_reduce a1<a) idp
            | :: a' l1, sorted-cons _ s1 \as s2 => rewrite (dec<_reduce (<-transitive-right (Sorted.allSorted l1 l2 s2) a1<a)) (pmap (a' ::) (insert_++-right a1<a s1))

          \lemma toList_insert' {A : Dec} (a : A) (t : RBTree A) (s : Sorted (toList t)) : toList (insert a t) = Insertion.sort.insert a (toList t) \elim t
            | rbLeaf => idp
            | rbBranch c t1 a1 t2 => mcases \with {
              | inl a1<a => toList_balanceRight _ _ _ _ *> pmap (toList t1 ++ a1 :: __) (toList_insert' a t2 (Sorted.tailSorted (a1 :: nil) _ (Sorted.tailSorted _ _ s))) *> inv (insert_++-right a1<a s)
              | inr a<=a1 => toList_balanceLeft _ _ _ _ *> pmap (__ ++ a1 :: toList t2) (toList_insert' a t1 (Sorted.headSorted s)) *> inv (insert_++-left a<=a1 s)
            }

          \lemma toList_mkTree {A : Dec} (l : List A) : toList (makeTree l) = Insertion.sort l \elim l
            | nil => idp
            | :: a l => toList_repaint (insert a (makeTree l)) *> toList_insert' a (makeTree l) (rewrite toList_mkTree (Insertion.sort-sorted l)) *> pmap (Insertion.sort.insert a) (toList_mkTree l)

          \lemma makeTree-sorted {A : Dec} (l : List A) : Sorted (toList (makeTree l))
            => rewrite toList_mkTree (Insertion.sort-sorted l)

          \lemma toList_insert {A : Dec} (a : A) (l : List A) : toList (insert a (makeTree l)) = Insertion.sort.insert a (toList (makeTree l))
            => toList_insert' a (makeTree l) (makeTree-sorted l)

          \lemma makeTree_insert {A : Dec} (a : A) (l l' : List A) : toList (makeTree (l ++ a :: l')) = toList (repaint (insert a (makeTree (l ++ l')))) \elim l
            | nil => idp
            | :: a' l => toList_repaint _ *> toList_insert a' _ *> pmap (Insertion.sort.insert a') (makeTree_insert a l l' *> toList_repaint _ *> toList_insert a _) *> Insertion.sort-perm.insert-comm a' a _ *> inv (
                           toList_repaint _ *>
                           toList_insert' a _ (transportInv Sorted (toList_repaint _ *> toList_insert a' _) (Insertion.sort-sorted.insert-sorted a' (makeTree-sorted _))) *>
                           pmap (Insertion.sort.insert a) (toList_repaint _ *> toList_insert a' _))

          \lemma aux=makeTree {A : Dec} (l l' : List A) : toList (aux l (makeTree l')) = toList (makeTree (l ++ l')) \elim l
            | nil => idp
            | :: a l => aux=makeTree l (a :: l') *> makeTree_insert a l l'
        }
  }
}

\func contains {A : DecSet} (l : List A) (a : A) : Bool \elim l
  | nil => false
  | b :: l => \case decideEq b a \with {
    | yes _ => true
    | no _ => contains l a
  }

\func union {A : DecSet} (l l' : List A) : List A \elim l
  | nil => l'
  | a :: l => if (contains l' a) (union l l') (a :: union l l')

\truncated \data InList {A : \Type} (a : A) (l : List A) : \Prop \elim l
  | a' :: l => { here (a = a') | there (InList a l) }
  \where {
    \func \infix 4 ~ {A : \Type} (l l' : List A) => \Pi (a : A) -> InList a l <-> InList a l'

    \lemma ~_:: {A : \Type} {a : A} {l l' : List A} (p : l ~ l') : a :: l ~ a :: l'
      => \lam b => (InList_:: (p b).1, InList_:: (p b).2)
      \where
        \lemma InList_:: {A : \Type} {a b : A} {l l' : List A} (p : InList b l -> InList b l') (c : InList b (a :: l)) : InList b (a :: l') \elim c
          | here q => here q
          | there c => there (p c)
  }

\lemma InList_++ {A : \Type} {a : A} {l l' : List A} (p : InList a (l ++ l')) : InList a l || InList a l' \elim l, p
  | nil, p => byRight p
  | a' :: l, here p => byLeft (here p)
  | a' :: l, there p => ||.map there id (InList_++ p)

\lemma InList_++-left {A : \Type} {a : A} {l l' : List A} (p : InList a l) : InList a (l ++ l') \elim l, p
  | a' :: l, here p => here p
  | a' :: l, there p => there (InList_++-left p)

\lemma InList_++-right {A : \Type} {a : A} {l l' : List A} (p : InList a l') : InList a (l ++ l') \elim l
  | nil => p
  | a' :: l => there (InList_++-right p)

\open InList

\lemma contains_InList {A : DecSet} {a : A} {l : List A} : (contains l a = true) <-> InList a l \elim l
  | nil => ((\case __), \case __)
  | a' :: l => (\lam p => cases (decideEq a' a, p) \with {
    | yes e, _ => here (inv e)
    | no _, p => there (contains_InList.1 p)
  }, \lam p => mcases \with {
    | yes _ => idp
    | no q => \case \elim p \with {
      | here p => absurd $ q (inv p)
      | there p => contains_InList.2 p
    }
  })

\lemma union~++ {A : DecSet} {l l' : List A} : union l l' ~ l ++ l' \elim l
  | nil => \lam a => (id,id)
  | a :: l => cases (contains l' a arg addPath) \with {
    | false, _ => ~_:: union~++
    | true, q => \lam b => (\lam c => there ((union~++ b).1 c), \case \elim __ \with {
      | here p => (union~++ b).2 $ InList_++-right $ rewrite p (contains_InList.1 q)
      | there p => (union~++ b).2 p
    })
  }