\import Arith.Nat
\import Data.List
\import Data.Or
\import Logic
\import Logic.Meta
\import Paths
\import Paths.Meta
\import Set
\import Set.Fin

\func Index {A : \Type} (list : List A) : \Type => Fin (length list)

\cons fzero {n : Nat} : Fin (suc n) => 0

\cons fsuc {n : Nat} (x : Fin n) : Fin (suc n) => suc x

\func fpred {n : Nat} (def : Fin n) (x : Fin (suc n)) : Fin n \elim x
  | 0 => def
  | suc x => x

\lemma unfsuc {n : Nat} {x y : Fin n} (p : suc x = suc y) : x = y
  => pmap (fpred x) p

\lemma fsuc/= {n : Nat} {x y : Fin n} (p : x /= y) : fsuc x /= fsuc y
  => \lam q => p (unfsuc q)

\lemma nat_fin_= {n : Nat} {x y : Fin n} (p : x = {Nat} y) : x = y \elim n, x, y
  | suc n, 0, 0 => idp
  | suc n, 0, suc y => \case p
  | suc n, suc x, 0 => \case p
  | suc n, suc x, suc y => pmap fsuc (nat_fin_= (pmap pred p))

\lemma fin_nat_/= {n : Nat} {x y : Fin n} (p : x /= y) : (x : Nat) /= y
  => \lam q => p (nat_fin_= q)

\lemma fsuc_fpred {n : Nat} {x : Fin (suc n)} {d : Fin n} (p : x /= 0) : fsuc (fpred d x) = x \elim x
  | 0 => absurd (p idp)
  | suc x => idp

\func last-fin (n : Nat) : Fin (suc n) \elim n
  | 0 => 0
  | suc n => suc (last-fin n)

\func fin-eq-dec {n : Nat} (x y : Fin n) : Dec (x = y) \elim n, x, y
  | suc n, 0, 0 => yes idp
  | suc n, 0, suc y => no contradiction
  | suc n, suc x, 0 => no contradiction
  | suc n, suc x, suc y => \case fin-eq-dec x y \with {
    | yes e => yes (pmap fsuc e)
    | no n1 => no (\lam p => n1 (FinSet.suc-isInj x y p))
  }

\func expand-fin-left {A : \Type} {a b : List A} (i : Index a) : Index (a ++ b) \elim a, i
  | :: a _, 0 => 0
  | :: _ _, suc i => suc (expand-fin-left i)
  \where {
    \func correct {A : \Type} {a b : List A} (i : Index a) : (a ++ b) !! expand-fin-left i = a !! i \elim a, i
      | :: a a1, 0 => idp
      | :: a a1, suc i => correct i
  }

\func expand-fin-right {A : \Type} {a b : List A} (i : Index b) : Index (a ++ b) \elim a
  | nil => i
  | :: a a1 => suc (expand-fin-right i)
  \where {
    \func correct {A : \Type} {a b : List A} (i : Index b) : (a ++ b) !! expand-fin-right i = b !! i \elim a
      | nil => idp
      | :: a a1 => correct i
  }

\func partial-fin-induction
  {A : \Type} {a b : List A} (C : Index (a ++ b) -> \Type)
  (proof-for-prefix : \Pi (i : Index a) -> C (expand-fin-left i))
  (proof-for-suffix : \Pi (i : Index b) -> C (expand-fin-right i))
  (index : Index (a ++ b)) : C index =>
  \case fin-list-decide index \with {
    | inl (i, p) => rewrite p (proof-for-prefix i)
    | inr (j, p) => rewrite p (proof-for-suffix j)
  }
  \where {
    \func fin-list-decide {A : \Type} {a b : List A} (index : Index (a ++ b)) :
      (\Sigma (i : Index a) (index = expand-fin-left i)) `Or` (\Sigma (j : Index b) (index = expand-fin-right j))
    \elim a, index
      | nil, index => inr (index, idp)
      | :: a a1, 0 => inl (0, idp)
      | :: a a1, suc index => \case fin-list-decide index \with {
        | inl (i, eq) => inl (suc i, pmap fsuc eq)
        | inr (j, eq) => inr (j, pmap fsuc eq)
      }
  }

\func modular-function
  {n : Nat}
  {C : Fin n -> \Type}
  (f g : \Pi (i : Fin n) -> C i)
  (delim : Fin (suc n))
  (i : Fin n)
  : C i
\elim n, delim, i
  | suc n, 0, i => f i
  | suc n, suc delim, 0 => g 0
  | suc n, suc delim, suc i => modular-function (\lam i => f (suc i)) (\lam i => g (suc i)) delim i
  \where {
    \func pure-left-modular
      {n : Nat}
      {C : Fin n -> \Type}
      (f g : \Pi (i : Fin n) -> C i)
      : modular-function f g 0 = f => ext (unext f g)
      \where {
        \func unext
          {n : Nat}
          {C : Fin n -> \Type}
          (f g : \Pi (i : Fin n) -> C i)
          (i : Fin n) : modular-function f g 0 i = f i \elim n, i
          | suc n, i => idp
      }

    \func modular-bridge
      {n : Nat}
      {C : Fin n -> \Type}
      (f g : \Pi (i : Fin n) -> C i)
      (delim : Fin n)
      (eq : f delim = g delim) : modular-function f g delim = modular-function f g (suc delim) =>
      ext (unext f g delim eq)
      \where {
        \func unext
          {n : Nat}
          {C : Fin n -> \Type}
          (f g : \Pi (i : Fin n) -> C i)
          (delim : Fin n)
          (eq : f delim = g delim)
          (j : Fin n)
          : modular-function f g delim j = modular-function f g (suc delim) j
        \elim n, delim, j
          | suc n, 0, 0 => eq
          | suc n, 0, suc j => rewrite pure-left-modular.unext idp
          | suc n, suc delim, 0 => idp
          | suc n, suc delim, suc j => unext (\lam i => f (suc i)) (\lam i => g (suc i)) delim eq j
      }

    \func pure-right-modular
      {n : Nat}
      {C : Fin n -> \Type}
      (f g : \Pi (i : Fin n) -> C i) : modular-function f g (last-fin n) = g => ext (unext C f g)
      \where {
        \func unext
          {n : Nat}
          (C : Fin n -> \Type)
          (f g : \Pi (i : Fin n) -> C i)
          (i : Fin n)
          : modular-function f g (last-fin n) i = g i \elim n, i
          | suc n, 0 => idp
          | suc n, suc i => unext (\lam i => C (suc i)) (\lam i => f (suc i)) (\lam i => g (suc i)) i
      }
  }

\func modular-induction
  {n : Nat} {C : Fin n -> \Type}
  (Q : (\Pi (i : Fin n) -> C i) -> \Type)
  (f g : \Pi (i : Fin n) -> C i)
  (start : Q f)
  (step : \Pi (delim : Fin n) (Q (modular-function f g delim)) -> Q (modular-function f g (suc delim)))
  : Q g =>
  transport Q (modular-function.pure-right-modular f g) (progressive-induction-lemma {n} Q f g step (last-fin n) start)
  \where {
    \func progressive-induction-lemma
      {n : Nat} {C : Fin n -> \Type}
      (Q : (\Pi (i : Fin n) -> C i) -> \Type)
      (f g : \Pi (i : Fin n) -> C i)
      (step : \Pi (delim : Fin n) (Q (modular-function f g delim)) -> Q (modular-function f g (suc delim)))
      (delim : Fin (suc n))
      (start : Q f)
      : Q (modular-function f g delim) \elim n, delim
      | 0, 0 => transportInv Q (modular-function.pure-left-modular f g) start
      | suc n, 0 => start
      | suc n, suc delim => step delim (progressive-induction-lemma {suc n} Q f g step delim start)
  }

\func pointed-function {n : Nat} {C : Fin n -> \Type}
                       (f : \Pi (i : Fin n) -> C i)
                       (index : Fin n)
                       (point : C index)
                       (j : Fin n)
  : C j \elim n, index, j
  | suc n, 0, 0 => point
  | suc n, 0, suc j => f (suc j)
  | suc n, suc index, 0 => f 0
  | suc n, suc index, suc j => pointed-function (\lam i => f (suc i)) index point j
  \where {
    \func at-index
      {n : Nat} {C : Fin n -> \Type}
      (f : \Pi (i : Fin n) -> C i)
      (index : Fin n)
      (point : C index)
      : pointed-function f index point index = point \elim n, index
      | suc n, 0 => idp
      | suc n, suc index => at-index (\lam i => f (suc i)) index point

    \func not-at-index
      {n : Nat} {C : Fin n -> \Type}
      (f : \Pi (i : Fin n) -> C i)
      (index : Fin n)
      (point : C index)
      (j : Fin n)
      (j-not-index : j = index -> Empty)
      : pointed-function f index point j = f j \elim n, index, j
      | suc n, 0, 0 => \have contra => j-not-index idp \in contradiction
      | suc n, 0, suc j => idp
      | suc n, suc index, 0 => idp
      | suc n, suc index, suc j
      => not-at-index (\lam i => f (suc i)) index point j (\lam p => j-not-index (pmap fsuc p))
  }

\func pointed-induction
  {n : Nat} {C : Fin n -> \Type}
  (Q : \Pi (i : Fin n) -> (C i) -> \Type)
  (f : \Pi (i : Fin n) -> C i)
  (index : Fin n)
  (point : C index)
  (eq1 : Q index point)
  (eq2 : \Pi (j : Fin n) (Not (j = index)) -> Q j (f j))
  (i : Fin n)
  : Q i (pointed-function f index point i)
\elim n, index, i
  | suc n, 0, 0 => eq1
  | suc n, 0, suc i => eq2 (suc i) (\lam e => contradiction)
  | suc n, suc index, 0 => eq2 0 (\lam e => contradiction)
  | suc n, suc index, suc i =>
    pointed-induction
        (\lam i ci => Q (suc i) ci)
        (\lam i => f (suc i)) index point eq1
        (\lam j ne => eq2 (suc j)
            (\lam p => ne (FinSet.suc-isInj _ _ p))) i

\func modular-to-pointed
  {n : Nat} {C : Fin n -> \Type}
  (f g : \Pi (i : Fin n) -> C i)
  (delim : Fin n)
  : modular-function f g delim = pointed-function (modular-function f g delim) delim (f delim) =>
  ext (unext f g delim)
  \where {
    \func unext
      {n : Nat} {C : Fin n -> \Type}
      (f g : \Pi (i : Fin n) -> C i)
      (delim : Fin n)
      (i : Fin n)
      : modular-function f g delim i = pointed-function (modular-function f g delim) delim (f delim) i
    \elim n, delim, i
      | suc n, 0, 0 => idp
      | suc n, 0, suc i => idp
      | suc n, suc delim, 0 => idp
      | suc n, suc delim, suc i => unext (\lam i => f (suc i)) (\lam j => g (suc j)) delim i
  }

\func modular-to-pointed-forward
  {n : Nat} {C : Fin n -> \Type}
  (f g : \Pi (i : Fin n) -> C i)
  (delim : Fin n)
  : modular-function f g (suc delim) = pointed-function (modular-function f g delim) delim (g delim) =>
  ext (unext f g delim)
  \where {
    \func unext
      {n : Nat} {C : Fin n -> \Type}
      (f g : \Pi (i : Fin n) -> C i)
      (delim : Fin n)
      (i : Fin n)
      : modular-function f g (suc delim) i = pointed-function (modular-function f g delim) delim (g delim) i
    \elim n, delim, i
      | suc n, 0, 0 => idp
      | suc n, 0, suc i => rewrite modular-function.pure-left-modular.unext idp
      | suc n, suc delim, 0 => idp
      | suc n, suc delim, suc i => unext (\lam i => f (suc i)) (\lam j => g (suc j)) delim i
  }