module ZipFolds

-- Based on http://okmij.org/ftp/Haskell/zip-folds.lhs

data FR : (a : Type) -> Type where
  Cont : ({r : Type} -> (a -> r -> r) -> r -> r) -> FR a

ofList : List a -> FR a
ofList l = Cont (\f, r => foldr f r l)

toList : FR a -> List a
toList (Cont f) = f (::) []

map : (a -> b) -> FR a -> FR b
map f (Cont g) = Cont (\h => g (\x, r => h (f x) r))

filter : (a -> Bool) -> FR a -> FR a
filter f (Cont g) = Cont (\h => g (\x, r => if f x then h x r else r))

drop : Int -> FR a -> FR a
drop n (Cont f) = Cont (\g, z => let f' = f (acc g) (seed z) in f' n)
  where
    acc : (a -> r -> r) -> a -> (Int -> r) -> Int -> r
    acc f x r n = if n <= 0 then f x (r n) else r (n - 1)
    seed : r -> Int -> r
    seed x _ = x

zipWith : (a -> b -> c) -> FR a -> FR b -> FR c
zipWith t (Cont f) fr = Cont (\g, z => let f' = f (acc z g) (seed z) in f' fr)
  where
    acc : r -> (c -> r -> r) -> a -> (FR b -> r) -> FR b -> r
    acc z f x r (Cont r2) = r2 (\y, _ => f (t x y) (r (drop 1 (Cont r2)))) z
    seed : r -> FR b -> r
    seed x _ = x

stream : FR Int
stream = ofList [1..10]

test : List (Int, Int)
test = toList $ zipWith (\x, y => (x, y)) stream stream


module SmallStep

import Logic

%access public export

%default total

data Tm : Type where
  C : Nat -> Tm
  P : Tm -> Tm -> Tm

Uninhabited (C _ = P _ _) where
  uninhabited Refl impossible

Uninhabited (P _ _ = C _) where
  uninhabited Refl impossible

evalF : (t : Tm) -> Nat
evalF (C n) = n
evalF (P n m) = evalF n + evalF m

data Eval : Tm -> Nat -> Type where
  E_Const : Eval (C n) n
  E_Plus : Eval t1 n1 -> Eval t2 n2 -> Eval (P t1 t2) (n1 + n2)

infixl 5 =+>

(=+>) : (t : Tm) -> (n : Nat) -> Type
(=+>) = Eval

infix 4 -+>

data Value : Tm -> Type where
  V_Const : (n : Nat) -> Value (C n)

Uninhabited (Value (P _ _)) where
  uninhabited (V_Const _) impossible

data Step : Tm -> Tm -> Type where
  ST_PlusConstConst : Step (P (C n1) (C n2)) (C (n1 + n2))
  ST_Plus1 : Step t1 t1' -> Step (P t1 t2) (P t1' t2)
  ST_Plus2 : Value v1 -> Step t2 t2' -> Step (P v1 t2) (P v1 t2')

(-+>) : Tm -> Tm -> Type
(-+>) = Step

test_step_1 : P (P (C 0) (C 3)) (P (C 2) (C 4)) -+>
              P (C (0 + 3)) (P (C 2) (C 4))
test_step_1 = ST_Plus1 ST_PlusConstConst

test_step_2 : P (C 0) (P (C 2) (P (C 0) (C 3))) -+>
              P (C 0) (P (C 2) (C (0 + 3)))
test_step_2 = ST_Plus2 (V_Const 0) (ST_Plus2 (V_Const 2) ST_PlusConstConst)

step_deterministic : Deterministic Step
step_deterministic ST_PlusConstConst ST_PlusConstConst =
  Refl
step_deterministic (ST_Plus1 s1) (ST_Plus1 s2) =
  rewrite step_deterministic s1 s2 in Refl
step_deterministic (ST_Plus2 _ s1) (ST_Plus2 _ s2) =
  rewrite step_deterministic s1 s2 in Refl

strong_progress : (t : Tm) -> Either (Value t) (t' : Tm ** t -+> t')
strong_progress (C n) = Left (V_Const n)
strong_progress (P (C n) (C m)) = Right (C (n + m) ** ST_PlusConstConst)
strong_progress (P (C n) (P t1 t2)) = case strong_progress (P t1 t2) of
  Left _ impossible
  Right (t' ** s) => Right (P (C n) t' ** ST_Plus2 (V_Const n) s)
strong_progress (P (P t1 t2) t3) = case strong_progress (P t1 t2) of
  Left _ impossible
  Right (t' ** s) => Right (P t' t3 ** ST_Plus1 s)

value_is_nf : Value v -> NormalForm Step v
value_is_nf (V_Const _) (_ ** _) impossible

nf_is_value : NormalForm Step v -> Value v
nf_is_value {v = (C k)} _ = V_Const k
nf_is_value {v = (P (C k) (C j))} contra =
  absurd (contra (C (k + j) ** ST_PlusConstConst))
nf_is_value {v = (P (C k) (P t1 t2))} contra = case strong_progress (P t1 t2) of
  Left _ impossible
  Right (x' ** s) => absurd (contra (P (C k) x' ** ST_Plus2 (V_Const k) s))
nf_is_value {v = (P (P t1 t2) t3)} contra = case strong_progress (P t1 t2) of
  Left _ impossible
  Right (x' ** s) => absurd (contra (P x' t3 ** ST_Plus1 s))

nf_same_as_value : (NormalForm Step v) ↔ (Value v)
nf_same_as_value = (nf_is_value, value_is_nf)

namespace Temp1
  data Value1 : Tm -> Type where
    V_Const1 : (n : Nat) -> Value1 (C n)
    V_Funny1 : Value1 (P t1 (C n2))

  data Step1 : Tm -> Tm -> Type where
    ST_PlusConstConst1 : Step1 (P (C n1) (C n2)) (C (n1 + n2))
    ST_Plus11 : Step1 t1 t1' -> Step1 (P t1 t2) (P t1' t2)
    ST_Plus21 : Value1 v1 -> Step1 t2 t2' -> Step1 (P v1 t2) (P v1 t2')

  value1_not_same_as_normal_form : (v : Tm ** ( Value1 v
                                              , Not (NormalForm Step1 v) ))
  value1_not_same_as_normal_form =
    (P (C 0) (C 0) ** (V_Funny1, \contra => contra (C 0 ** ST_PlusConstConst1)))

namespace Temp2
  data Value2 : Tm -> Type where
    V_Const2 : (n : Nat) -> Value2 (C n)

  data Step2 : Tm -> Tm -> Type where
    ST_Funny2 : Step2 (C n) (P (C n) (C 0))
    ST_PlusConstConst2 : Step2 (P (C n1) (C n2)) (C (n1 + n2))
    ST_Plus12 : Step2 t1 t1' -> Step2 (P t1 t2) (P t1' t2)
    ST_Plus22 : Value2 v1 -> Step2 t1 t2' -> Step2 (P v1 t2) (P v1 t2')

  value2_not_same_as_normal_form : (v : Tm ** ( Value2 v
                                              , Not (NormalForm Step2 v) ))
  value2_not_same_as_normal_form =
    (C 0 ** (V_Const2 0, \contra => contra (P (C 0) (C 0) ** ST_Funny2)))

namespace Temp3
  data Value3 : Tm -> Type where
    V_Const3 : (n : Nat) -> Value3 (C n)

  data Step3 : Tm -> Tm -> Type where
    ST_PlusConstConst3 : Step3 (P (C n1) (C n2)) (C (n1 + n2))
    ST_Plus13 : Step3 t1 t1' -> Step3 (P t1 t2) (P t1' t2)

  value3_not_same_as_normal_form : (v : Tm ** ( Not (Value3 v)
                                              , NormalForm Step3 v ))
  value3_not_same_as_normal_form =
    (P (C 0) (P (C 0) (C 0)) ** ( \v => case v of
                                    V_Const3 _ impossible
                                , \(x' ** s) => case s of
                                    ST_Plus13 ST_PlusConstConst3 impossible
                                    ST_Plus13 (ST_Plus13 _) impossible ))

namespace Temp4
  data Tm4 : Type where
    Tru4 : Tm4
    Fls4 : Tm4
    Test4 : Tm4 -> Tm4 -> Tm4 -> Tm4

  data Value4 : Tm4 -> Type where
    V_Tru4 : Value4 Tru4
    V_Fls4 : Value4 Fls4

  data Step4 : Tm4 -> Tm4 -> Type where
    ST_IfTrue4 : Step4 (Test4 Tru4 t1 t2) t1
    ST_IfFalse4 : Step4 (Test4 Fls4 t1 t2) t2
    ST_If4 : Step4 t1 t1' -> Step4 (Test4 t1 t2 t3) (Test4 t1' t2 t3)

  bool_step_prop1 : Not (Step4 Fls4 Fls4)
  bool_step_prop1 ST_IfTrue4 impossible
  bool_step_prop1 ST_IfFalse4 impossible
  bool_step_prop1 (ST_If4 _) impossible

  bool_step_prop2 : Not (Step4 (Test4 Tru4
                                      (Test4 Tru4 Tru4 Tru4)
                                      (Test4 Fls4 Fls4 Fls4))
                               Tru4)
  bool_step_prop2 ST_IfTrue4 impossible
  bool_step_prop2 ST_IfFalse4 impossible
  bool_step_prop2 (ST_If4 _) impossible

  bool_step_prop3 : Step4 (Test4 (Test4 Tru4 Tru4 Tru4)
                                 (Test4 Tru4 Tru4 Tru4)
                                 Fls4)
                          (Test4 Tru4
                                 (Test4 Tru4 Tru4 Tru4)
                                 Fls4)
  bool_step_prop3 = ST_If4 ST_IfTrue4

  strong_progress_bool : (t : Tm4) -> Either (Value4 t) (t' : Tm4 ** Step4 t t')
  strong_progress_bool Tru4 = Left V_Tru4
  strong_progress_bool Fls4 = Left V_Fls4
  strong_progress_bool (Test4 Tru4 t2 _) = Right (t2 ** ST_IfTrue4)
  strong_progress_bool (Test4 Fls4 _ t3) = Right (t3 ** ST_IfFalse4)
  strong_progress_bool (Test4 (Test4 t1 t2 t3) t4 t5) =
    case strong_progress_bool (Test4 t1 t2 t3) of
      Left _ impossible
      Right (t' ** s) => Right (Test4 t' t4 t5 ** ST_If4 s)

  step_deterministic_bool : Step4 x y1 -> Step4 x y2 -> y1 = y2
  step_deterministic_bool ST_IfTrue4 ST_IfTrue4 = Refl
  step_deterministic_bool ST_IfFalse4 ST_IfFalse4 = Refl
  step_deterministic_bool (ST_If4 s1) (ST_If4 s2) =
    rewrite step_deterministic_bool s1 s2 in Refl

infix 4 -+>*

(-+>*) : Relation Tm
(-+>*) = Multi Step

test_multistep_1 : P (P (C 0) (C 3)) (P (C 2) (C 4)) -+>* C 9
test_multistep_1 = MultiStep (ST_Plus1 ST_PlusConstConst) $
                   MultiStep (ST_Plus2 (V_Const 3) ST_PlusConstConst) $
                   MultiStep ST_PlusConstConst $
                   MultiRefl

test_multistep_2 : C 3 -+>* C 3
test_multistep_2 = MultiRefl

test_multistep_3 : P (C 0) (C 3) -+>* P (C 0) (C 3)
test_multistep_3 = MultiRefl

test_multistep_4 : P (C 0) (P (C 2) (P (C 0) (C 3))) -+>* P (C 0) (C 5)
test_multistep_4 = MultiStep (ST_Plus2 (V_Const 0)
                                       (ST_Plus2 (V_Const 2)
                                                 ST_PlusConstConst)) $
                   MultiStep (ST_Plus2 (V_Const 0) ST_PlusConstConst) $
                   MultiRefl

NormalFormOf : (t, t' : Tm) -> Type
NormalFormOf t t' = (t -+>* t', NormalForm Step t')

normal_forms_unique : Deterministic NormalFormOf
normal_forms_unique (MultiRefl, _) (MultiRefl, _) = Refl
normal_forms_unique (MultiRefl, contra) (MultiStep {y} x_step_y _, _) =
  absurd (contra (y ** x_step_y))
normal_forms_unique (MultiStep {y} x_step_y _, _) (MultiRefl, contra) =
  absurd (contra (y ** x_step_y))
normal_forms_unique {y2}
                    p@(MultiStep x_step_y y_step_y1, contra1)
                    (MultiStep x_step_y3 y3_step_y2, contra2) =
  let y3_eq_y = sym (step_deterministic x_step_y x_step_y3)
      y_step_y2 = replace {P=\x => Multi Step x y2} y3_eq_y y3_step_y2
  in normal_forms_unique (assert_smaller p (y_step_y1, contra1))
                         (y_step_y2, contra2)

Normalizing : (r : Relation t) -> Type
Normalizing {t} r = (x : t) -> (y : t ** (Multi r x y, NormalForm r y))

multistep_congr_1 : t1 -+>* t1' -> P t1 t2 -+>* P t1' t2
multistep_congr_1 MultiRefl = MultiRefl
multistep_congr_1 (MultiStep once next) =
  MultiStep (ST_Plus1 once) (multistep_congr_1 next)

multistep_congr_2 : Value t1 -> t2 -+>* t2' -> P t1 t2 -+>* P t1 t2'
multistep_congr_2 _ MultiRefl = MultiRefl
multistep_congr_2 v (MultiStep once next) =
  MultiStep (ST_Plus2 v once) (multistep_congr_2 v next)

step_normalizing : Normalizing Step
step_normalizing (C n) =
  (C n ** (MultiRefl, \(_ ** s) => case s of
                        ST_PlusConstConst impossible
                        ST_Plus1 _ impossible
                        ST_Plus2 _ _ impossible))
step_normalizing (P t1 t2) =
  let (_ ** (s1, nf1)) = step_normalizing t1
      (_ ** (s2, nf2)) = step_normalizing t2
      v1@(V_Const n1) = nf_is_value nf1
      V_Const n2 = nf_is_value nf2
      steps = multi_trans (multistep_congr_1 {t2=t2} s1) $
              multi_trans (multistep_congr_2 v1 s2) $
              MultiStep ST_PlusConstConst MultiRefl
  in (C (n1 + n2) ** (steps, \(_ ** s) => case s of
                               ST_PlusConstConst impossible
                               ST_Plus1 _ impossible
                               ST_Plus2 _ _ impossible))

eval__multistep : t =+> n -> t -+>* C n
eval__multistep E_Const = MultiRefl
eval__multistep (E_Plus {n1} e1 e2) =
  multi_trans (multistep_congr_1 (eval__multistep e1)) $
  multi_trans (multistep_congr_2 (V_Const n1) (eval__multistep e2)) $
  MultiStep ST_PlusConstConst MultiRefl

step__eval : t -+> t' -> t' =+> n -> t =+> n
step__eval ST_PlusConstConst E_Const = E_Plus E_Const E_Const
step__eval (ST_Plus1 s1) (E_Plus e1 e2) = E_Plus (step__eval s1 e1) e2
step__eval (ST_Plus2 _ s2) (E_Plus e1 e2) = E_Plus e1 (step__eval s2 e2)

multistep__eval : NormalFormOf t t' -> (n : Nat ** (t' = C n, t =+> n))
multistep__eval (MultiRefl, nf) =
  let V_Const n = nf_is_value nf
  in (n ** (Refl, E_Const))
multistep__eval p@(MultiStep {y} once next, nf) =
  let (n ** (prf, e)) = multistep__eval (assert_smaller p (next, nf))
  in (n ** (prf, step__eval once e))

evalF_eval : (evalF t = n) ↔ (t =+> n)
evalF_eval = (forward, backward)
where forward : evalF t = n -> t =+> n
      forward {t = (C _)} prf = rewrite prf in E_Const
      forward {t = (P t1 t2)} prf with (evalF t1) proof prf1
        forward {t = (P t1 t2)} prf | _ with (evalF t2) proof prf2
          forward {t = (P t1 t2)} prf | _ | _ =
            rewrite sym prf
            in E_Plus (forward (sym prf1)) (forward (sym prf2))
      backward : t =+> n -> evalF t = n
      backward E_Const = Refl
      backward (E_Plus e1 e2) =
        rewrite backward e1
        in rewrite backward e2
        in Refl


||| A segment is a compositional fragment of a telescope.
||| A key difference is that segments are right-nested, whereas
||| telescopes are left nested.
||| So telescopes are convenient for well-bracketing dependencies,
||| but segments are convenient for processing telescopes from left
||| to right.
|||
||| As with telescopes, indexing segments by their length (hopefully)
||| helps the type-checker infer stuff.
module Data.Telescope.Segment

import Data.Telescope.Telescope

import Syntax.PreorderReasoning
import Data.Fin
import Data.Nat
import Data.DPair

%default total

||| A segment is a compositional fragment of a telescope, indexed by
||| the segment's length.
public export
data Segment : (n : Nat) -> Left.Telescope k -> Type where
  Nil  : Segment 0 gamma
  (::) : (ty : TypeIn gamma) -> (delta : Segment n (gamma -. ty)) -> Segment (S n) gamma

||| A segment of size `n` indexed by `gamma` can be seen as the tabulation of a
||| function that turns environments for `gamma` into telescopes of size `n`.
public export
tabulate : (n : Nat) -> (Left.Environment gamma -> Left.Telescope n) -> Segment n gamma
tabulate Z tel = []
tabulate (S n) tel = (sigma :: tabulate n (uncurry delta)) where

  sigma : TypeIn gamma
  sigma env = fst (uncons (tel env))

  delta : (env : Environment gamma) -> sigma env -> Left.Telescope n
  delta env v with (uncons (tel env))
    delta env v | (sig ** delt ** _) = delt v

||| Any telescope is a segment in the empty telescope. It amounts to looking
||| at it left-to-right instead of right-to-left.
public export
fromTelescope : {k : Nat} -> Left.Telescope k -> Segment k []
fromTelescope gamma = tabulate _ (const gamma)

||| Conversely, a segment of size `n` in telescope `gamma` can be seen as a function
||| from environments for `gamma` to telescopes of size `n`.
public export
untabulate : {n : Nat} -> Segment n gamma -> (Left.Environment gamma -> Left.Telescope n)
untabulate []            _   = []
untabulate (ty :: delta) env = cons (ty env) (untabulate delta . (\ v => (env ** v)))

||| Any segment in the empty telescope correspond to a telescope.
public export
toTelescope : {k : Nat} -> Segment k [] -> Left.Telescope k
toTelescope seg = untabulate seg ()

%name Segment delta,delta',delta1,delta2

infixl 3 |++, :++

||| This lemma comes up all the time when mixing induction on Nat with
||| indexing modulo addition.  An alternative is to use something like
||| frex.
succLemma : (lft, rgt : Nat) -> lft + (S rgt) = S (lft + rgt)
succLemma x y = Calc $
  |~ x + (1 + y)
  ~~ (x + 1)+ y  ...(plusAssociative x 1 y)
  ~~ (1 + x)+ y  ...(cong (+y) $ plusCommutative x 1)
  ~~ 1 + (x + y) ...(sym $ plusAssociative 1 x y)

-- Should go somehwere in stdlib
public export
keep : (0 prf : a ~=~ b) -> a ~=~ b
keep Refl = Refl

-- Keeping the `Nat` argument relevant, should (hopefully) only
-- case-split on it and not the environment unnecessarily, allowing us
-- to calculate, so long as we know the 'shape' of the Segment.
--
-- This should work in theory, I don't think it actually works for
-- Idris at the moment.  Might work in Agda?

||| Segments act on telescope from the right.
public export
(|++) : (gamma : Left.Telescope k) -> {n : Nat} -> (delta : Segment n gamma) -> Left.Telescope (n + k)
(|++)  gamma {n = 0} delta  = gamma
(|++)  gamma {n=S n} (ty :: delta) = rewrite sym $ succLemma n k in
                                       gamma -. ty |++ delta

||| Segments form a kind of an indexed monoid w.r.t. the action `(|++)`
public export
(++) : {gamma : Left.Telescope k}
    -> {n : Nat}
    -> (lft : Segment n  gamma         )
    -> (rgt : Segment m (gamma |++ lft))
    -> Segment (n + m) gamma
(++) {n = 0  } delta       rgt = rgt
(++) {n = S n} (ty :: lft) rgt = ty :: lft ++ rewrite succLemma n k in
                                              rgt
-- This monoid does act on telescopes:
export
actSegmentAssociative : (gamma : Left.Telescope k)
                     -> (lft : Segment n gamma)
                     -> (rgt : Segment m (gamma |++ lft))
                     -> (gamma |++ (lft ++ rgt)) ~=~ ((gamma |++ lft) |++ rgt)
actSegmentAssociative gamma {n = 0}   []          rgt = Refl
actSegmentAssociative gamma {n = S n} (ty :: lft) rgt =
  let rgt' : Segment {k = n + (S k)} m (gamma -. ty |++ lft)
      rgt' = rewrite succLemma n k in
             rgt
      rgt_eq_rgt' : rgt ~=~ rgt'
      rgt_eq_rgt' = rewrite succLemma n k in
                    Refl
  in
  rewrite sym $ succLemma (n + m) k in
  rewrite sym $ succLemma n       k in keep $ Calc $
  |~ ((gamma -. ty) |++ (lft ++ rgt'))
  ~~ ((gamma -. ty |++  lft) |++ rgt')
          ...(actSegmentAssociative (gamma -. ty) lft rgt')

public export
weaken : {0 gamma : Left.Telescope k}
      -> {delta : Segment n gamma} -> (sy : TypeIn gamma)
      -> TypeIn (gamma |++ delta)
weaken           {delta = []         } sy = sy
weaken {n = S n} {delta = ty :: delta} sy = rewrite sym $ succLemma n k in
                                            weaken (weakenTypeIn sy)

public export
projection : {0 gamma : Left.Telescope k}
          -> {n : Nat}
          -> {0 delta : Segment n gamma}
          -> Environment (gamma |++ delta)
          -> Environment gamma
projection {n = 0  } {delta = []         } env = env
projection {n = S n} {delta = ty :: delta} env
  = let (env' ** _) = projection {n} {delta}
                    $ rewrite succLemma n k in env
    in env'

infixl 4 .=

public export
data Environment : (env : Left.Environment gamma)
                -> (delta : Segment n gamma) -> Type where
  Empty : Environment env []
  (.=) : {0 gamma : Left.Telescope k} -> {0 ty : TypeIn gamma}
      -> {0 env   : Left.Environment gamma}
      -> {0 delta : Segment n (gamma -. ty)}

      -> (x : ty env) -> (xs : Segment.Environment (env ** x) delta)
      -> Environment env (ty :: delta)

public export
(:++) : {0 gamma : Left.Telescope k} -> {0 delta : Segment n gamma}
     -> (env : Left.Environment gamma)
     -> (ext : Segment.Environment env delta)
     ->        Left.Environment (gamma |++ delta)
(:++) env Empty = env
(:++) {n = S n} env (x .= xs) = rewrite sym $ succLemma n k in
                                (env ** x) :++ xs

-- This is too nasty for now, leave to later
{-
public export
break : {0 k : Nat} -> (gamma : Telescope k') -> (pos : Position gamma)
     -> {auto 0 ford : k' = cast pos + k } -> Telescope k
break gamma          FZ      {ford = Refl} = gamma
break []            (FS pos) {ford = _   } impossible
break {k' = S k'} (gamma -. ty) (FS pos) {ford} = break gamma pos
  {ford = Calc $
   |~ k'
   ~~ cast pos + k ...(succInjective _ _ ford)}

-- Should go into Data.Fin
export
lastIsLast : {n : Nat} -> cast (last {n}) = n
lastIsLast {n = 0  } = Refl
lastIsLast {n = S n} = rewrite lastIsLast {n} in
                       Refl

public export
finComplement : {n : Nat} -> (i : Fin n) -> Fin n
finComplement FZ = last
finComplement (FS i) = weaken (finComplement i)

export
castNaturality : (i : Fin n) -> finToNat (weaken i) ~=~ finToNat i
castNaturality  FZ    = Refl
castNaturality (FS i) = rewrite castNaturality i in
                        Refl

export
finComplementSpec : (i : Fin (S n)) -> cast i + cast (finComplement i) = n
finComplementSpec FZ = keep lastIsLast
finComplementSpec {n = .(S n)} (FS i@ FZ   ) = rewrite castNaturality (finComplement i) in
                                               rewrite finComplementSpec i in
                                               Refl
finComplementSpec {n = .(S n)} (FS i@(FS _)) = rewrite castNaturality (finComplement i) in
                                               rewrite finComplementSpec i in
                                               Refl

complementLastZero : (n : Nat) -> finComplement (last {n}) = FZ
complementLastZero n = finToNatInjective _ _ $ plusLeftCancel n _ _ $ Calc $
   let n' : Nat
       n' = cast $ finComplement $ last {n} in
   |~ n + n'
   ~~ (finToNat $ last {n})  + n'
                    ...(cong (+n') $ sym $ lastIsLast {n})
   ~~ n             ...(finComplementSpec $ last {n})
   ~~ n         + 0 ...(sym $ plusZeroRightNeutral n)

public export
breakOnto : {0 k,k' : Nat} -> (gamma : Telescope k) -> (pos : Position gamma)
         -> (delta : Segment n gamma)
         -> {auto 0 ford1 : k' === (finToNat $ finComplement pos) }
         -> {default
               -- disgusting, sorry
               (replace {p = \u => k = finToNat pos + u}
                        (sym ford1)
                        (sym $ finComplementSpec pos))
             0 ford2 : (k === ((finToNat pos) + k')) }
         -> Segment (cast pos + n)
                    (break {k = k'}
                           gamma pos
                           {ford = ford2})
breakOnto gamma          FZ      delta {ford1 = Refl} {ford2} =
                         rewrite sym ford2 in
                         delta
breakOnto (gamma -. ty) (FS pos) delta {ford1 = Refl} {ford2} =
                         rewrite sym $ succLemma (cast pos) n in
                         rewrite castNaturality (finComplement pos) in
                         breakOnto gamma pos (ty :: delta)

uip : (prf1, prf2 : x ~=~ y) -> prf1 ~=~ prf2
uip Refl Refl = Refl

export
breakStartEmpty : (gamma : Telescope k')
               -> {auto 0 ford1 : k = 0}
               -> {auto 0 ford2 : k' = finToNat (start gamma) + k}
               -> break {k} {k'} gamma (start gamma) {ford = ford2}
                ~=~ Telescope.Nil
breakStartEmpty [] {ford1 = Refl} {ford2 = Refl}            = Refl
breakStartEmpty {k} {k' = S k'} {ford1} {ford2} (gamma -. ty) =
                -- Yuck!
                let 0 u : (k' = finToNat (start gamma) + k)
                    u = succInjective _ _ ford2
                    v : break {k} {k'} gamma (start gamma) {ford = u}
                        ~=~ Telescope.Nil
                    v = breakStartEmpty {k} {k'} gamma {ford2 = u}
                in replace {p = \z =>
                             Equal {a = Telescope k} {b = Telescope 0}
                                   (break {k'} {k} gamma (start gamma)
                                          {ford = z})
                                          []
                        }
                        (uip u _)
                        (keep v)

public export
projection : {0 gamma : Telescope k} -> (pos : Position gamma) -> (env : Environment gamma)
          -> Environment (break {k = cast (finComplement pos)} gamma pos
                                {ford = sym $ finComplementSpec pos})
projection FZ env = rewrite finComplementSpec $ FZ {k} in
                    env
projection {gamma = []} (FS pos) Empty impossible
projection {k = S k} {gamma = gamma -. ty} (FS pos) (env ** x) =
  rewrite castNaturality (finComplement pos) in
  projection {k} pos env
-}


fstDup: (xs : List a) -> map fst (map dup xs) === xs
fstDup Nil = Refl
fstDup (x::xs) = ?a


module Accumulate

export
accumulate : (a -> b) -> List a -> List b
accumulate f [] = []
accumulate f (x::xs) = f x :: accumulate f xs


mid : {A:Type} -> A -> A
mid = \ x => x

CPair : Type -> Type -> Type
CPair = \ a, b => (p : Type) -> (a -> b -> p) -> p

cdup : {A:Type} -> A -> CPair A A
cdup = \ a, x, p => p a a

CNat : Type
CNat = (n : Type) -> (n -> n) -> n -> n

czero : CNat
czero = \ n, s, z => z

csuc : CNat -> CNat
csuc = \ a, n, s, z => s (a n s z)

CVec : Type -> CNat -> Type
CVec = \ a, n => (V : CNat -> Type) -> ({n : CNat} -> a -> V n -> V (csuc n)) -> V czero -> V n

cnil : {A:Type} -> CVec A Main.czero
cnil = \ v, c, n => n

ccons : {A:Type}->{n:CNat} -> A -> CVec A n -> CVec A (csuc n)
ccons = \ a, as, v, c, n => c a (as v c n)

--------------------------------------------------------------------------------

-- fails
-- idTest : {A:Type} -> A -> A
-- idTest =
--     mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid
--     mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid mid

-- fails
-- pairTest =
--   let x0  = cdup Type
--       x1  = cdup x0
--       x2  = cdup x1
--       x3  = cdup x2
--       x4  = cdup x3
--       x5  = cdup x4
--       x6  = cdup x5
--       x7  = cdup x6
--       x8  = cdup x7
--       x9  = cdup x8
--       x10 = cdup x9
--       x11 = cdup x10
--       x12 = cdup x11
--       x13 = cdup x12
--       x14 = cdup x13
--       x15 = cdup x14
--       x16 = cdup x15
--       x17 = cdup x16
--       x18 = cdup x17
--       x19 = cdup x18
--       x20 = cdup x19
--       x21 = cdup x20
--       x22 = cdup x21
--       x23 = cdup x22
--       x24 = cdup x23
--       x25 = cdup x24
--       x26 = cdup x25
--       x27 = cdup x26
--       x28 = cdup x27
--       x29 = cdup x28
--       x30 = cdup x29
--   in x30

vecTest : Type
vecTest =
  let foo =
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type
       (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type (ccons Type

       cnil

       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))
       ))))))))))))))))))))))))))))))

  in Type


module Data.Nat.Factor

import Control.WellFounded
import Data.Fin
import Data.Fin.Extra
import Data.Nat
import Data.Nat.Equational
import Syntax.PreorderReasoning

%default total

||| Factor n p is a witness that p is indeed a factor of n,
||| i.e. there exists a q such that p * q = n.
public export
data Factor : Nat -> Nat -> Type where
    CofactorExists : {p, n : Nat} -> (q : Nat) -> n = p * q -> Factor p n

||| NotFactor n p is a witness that p is NOT a factor of n,
||| i.e. there exist a q and an r, greater than 0 but smaller than p,
||| such that p * q + r = n.
public export
data NotFactor : Nat -> Nat -> Type where
    ZeroNotFactorS : (n : Nat) -> NotFactor Z (S n)
    ProperRemExists : {p, n : Nat} -> (q : Nat) ->
        (r : Fin (pred p)) ->
        n = p * q + S (finToNat r) ->
        NotFactor p n

||| DecFactor n p is a result of the process which decides
||| whether or not p is a factor on n.
public export
data DecFactor : Nat -> Nat -> Type where
    ItIsFactor : Factor p n -> DecFactor p n
    ItIsNotFactor : NotFactor p n -> DecFactor p n

||| CommonFactor n m p is a witness that p is a factor of both n and m.
public export
data CommonFactor : Nat -> Nat -> Nat -> Type where
    CommonFactorExists : {a, b : Nat} -> (p : Nat) -> Factor p a -> Factor p b -> CommonFactor p a b

||| GCD n m p is a witness that p is THE greatest common factor of both n and m.
||| The second argument to the constructor is a function which for all q being
||| a factor of both n and m, proves that q is a factor of p.
|||
||| This is equivalent to a more straightforward definition, stating that for
||| all q being a factor of both n and m, q is less than or equal to p, but more
||| powerful and therefore more useful for further proofs. See below for a proof
||| that if q is a factor of p then q must be less than or equal to p.
public export
data GCD : Nat -> Nat -> Nat -> Type where
    MkGCD : {a, b, p : Nat} ->
        {auto notBothZero : NotBothZero a b} ->
        (CommonFactor p a b) ->
        ((q : Nat) -> CommonFactor q a b -> Factor q p) ->
        GCD p a b

Uninhabited (Factor Z (S n)) where
    uninhabited (CofactorExists q prf) = uninhabited prf

||| Given a statement that p is factor of n, return its cofactor.
export
cofactor : Factor p n -> (q : Nat ** Factor q n)
cofactor (CofactorExists q prf) =
        (q ** CofactorExists p $ rewrite multCommutative q p in prf)

||| 1 is a factor of any natural number.
export
oneIsFactor : (n : Nat) -> Factor 1 n
oneIsFactor Z = CofactorExists Z Refl
oneIsFactor (S k) = CofactorExists (S k) (rewrite plusZeroRightNeutral k in Refl)

||| 1 is the only factor of itself
export
oneSoleFactorOfOne : (a : Nat) -> Factor a 1 -> a = 1
oneSoleFactorOfOne 0 (CofactorExists _ prf) = sym prf
oneSoleFactorOfOne 1 _ = Refl
oneSoleFactorOfOne (S (S k)) (CofactorExists Z prf) =
  absurd . uninhabited $ trans prf $ multCommutative k 0
oneSoleFactorOfOne (S (S k)) (CofactorExists (S j) prf) =
  absurd . uninhabited $
    trans
      (succInjective 0 (j + S (j + (k * S j))) prf)
      (plusCommutative j (S (j + (k * S j))))

||| Every natural number is factor of itself.
export
Reflexive Nat Factor where
  reflexive = CofactorExists 1 $ rewrite multOneRightNeutral x in Refl

||| Factor relation is transitive. If b is factor of a and c is b factor of c
||| is also a factor of a.
export
Transitive Nat Factor where
  transitive (CofactorExists qb prfAB) (CofactorExists qc prfBC) =
    CofactorExists (qb * qc) $
        rewrite prfBC in
        rewrite prfAB in
        rewrite multAssociative x qb qc in
        Refl

export
Preorder Nat Factor where

multOneSoleNeutral : (a, b : Nat) -> S a = S a * b -> b = 1
multOneSoleNeutral Z b prf =
        rewrite sym $ plusZeroRightNeutral b in
        sym prf
multOneSoleNeutral (S k) Z prf =
        absurd . uninhabited $ trans prf $ multCommutative k 0
multOneSoleNeutral (S k) (S Z) prf = Refl
multOneSoleNeutral (S k) (S (S j)) prf =
        absurd . uninhabited .
        subtractEqLeft k {c = S j + S (j + (k * S j))} $
        rewrite plusSuccRightSucc j (S (j + (k * S j))) in
        rewrite plusZeroRightNeutral k in
        rewrite plusAssociative k j (S (S (j + (k * S j)))) in
        rewrite sym $ plusCommutative j k in
        rewrite sym $ plusAssociative j k (S (S (j + (k * S j)))) in
        rewrite sym $ plusSuccRightSucc k (S (j + (k * S j))) in
        rewrite sym $ plusSuccRightSucc k (j + (k * S j)) in
        rewrite plusAssociative k j (k * S j) in
        rewrite plusCommutative k j in
        rewrite sym $ plusAssociative j k (k * S j) in
        rewrite sym $ multRightSuccPlus k (S j) in
        succInjective k (j + (S (S (j + (k * (S (S j))))))) $
        succInjective (S k) (S (j + (S (S (j + (k * (S (S j)))))))) prf

||| If a is a factor of b and b is a factor of a, then a = b.
public export
Antisymmetric Nat Factor where
  antisymmetric {x = Z} (CofactorExists _ prfAB) _ = sym prfAB
  antisymmetric {y = Z} _ (CofactorExists _ prfBA) = prfBA
  antisymmetric {x = S a} {y = S _} (CofactorExists qa prfAB) (CofactorExists qb prfBA) =
      let qIs1 = multOneSoleNeutral a (qa * qb) $
              rewrite multAssociative (S a) qa qb in
              rewrite sym prfAB in
              prfBA
      in
      rewrite prfAB in
      rewrite oneSoleFactorOfOne qa . CofactorExists qb $ sym qIs1 in
      rewrite multOneRightNeutral a in
      Refl

PartialOrder Nat Factor where

||| No number can simultaneously be and not be a factor of another number.
export
factorNotFactorAbsurd : Factor p n -> Not (NotFactor p n)
factorNotFactorAbsurd (CofactorExists _ prf) (ZeroNotFactorS _) =
        uninhabited prf
factorNotFactorAbsurd (CofactorExists q prf) (ProperRemExists q' r contra) with (cmp q q')
    factorNotFactorAbsurd (CofactorExists q prf) (ProperRemExists (q + S d) r contra) | CmpLT d =
        SIsNotZ .
        subtractEqLeft (p * q) {b = S ((p * S d) + finToNat r)} $
        rewrite plusZeroRightNeutral (p * q) in
        rewrite plusSuccRightSucc (p * S d) (finToNat r) in
        rewrite plusAssociative (p * q) (p * S d) (S (finToNat r)) in
        rewrite sym $ multDistributesOverPlusRight p q (S d) in
        rewrite sym contra in
        rewrite sym prf in
        Refl
    factorNotFactorAbsurd (CofactorExists q prf) (ProperRemExists q r contra) | CmpEQ =
      SIsNotZ $ sym $
        plusLeftCancel (p * q) 0 (S (finToNat r)) $
          trans (plusZeroRightNeutral (p * q)) $
            trans (sym prf) contra
    factorNotFactorAbsurd (CofactorExists (q + S d) prf) (ProperRemExists q r contra) | CmpGT d =
        let srEQpPlusPD = the (p + (p * d) = S (finToNat r)) $
                rewrite sym $ multRightSuccPlus p d in
                subtractEqLeft (p * q) $
                    rewrite sym $ multDistributesOverPlusRight p q (S d) in
                    rewrite sym contra in
                    sym prf
        in
        case p of
            Z => uninhabited srEQpPlusPD
            (S k) =>
                succNotLTEzero .
                subtractLteLeft k {b = S (d + (k * d))} $
                rewrite sym $ plusSuccRightSucc k (d + (k * d)) in
                rewrite plusZeroRightNeutral k in
                rewrite srEQpPlusPD in
                elemSmallerThanBound r

||| Anything is a factor of 0.
export
anythingFactorZero : (a : Nat) -> Factor a 0
anythingFactorZero a = CofactorExists 0 (sym $ multZeroRightZero a)

||| For all natural numbers p and q, p is a factor of (p * q).
export
multFactor : (p, q : Nat) -> Factor p (p * q)
multFactor p q = CofactorExists q Refl

||| If n > 0 then any factor of n must be less than or equal to n.
export
factorLteNumber : Factor p n -> {auto positN : LTE 1 n} -> LTE p n
factorLteNumber (CofactorExists Z prf) =
        let nIsZero = trans prf $ multCommutative p 0
            oneLteZero = replace {p = LTE 1} nIsZero positN
        in
        absurd $ succNotLTEzero oneLteZero
factorLteNumber (CofactorExists (S k) prf) =
        rewrite prf in
        leftFactorLteProduct p k

||| If p is a factor of n, then it is also a factor of (n + p).
export
plusDivisorAlsoFactor : Factor p n -> Factor p (n + p)
plusDivisorAlsoFactor (CofactorExists q prf) =
        CofactorExists (S q) $
            rewrite plusCommutative n p in
            rewrite multRightSuccPlus p q in
            cong (plus p) prf

||| If p is NOT a factor of n, then it also is NOT a factor of (n + p).
export
plusDivisorNeitherFactor : NotFactor p n -> NotFactor p (n + p)
plusDivisorNeitherFactor (ZeroNotFactorS k) =
        rewrite plusZeroRightNeutral k in
        ZeroNotFactorS k
plusDivisorNeitherFactor (ProperRemExists q r remPrf) =
        ProperRemExists (S q) r $
            rewrite multRightSuccPlus p q in
            rewrite sym $ plusAssociative p (p * q) (S $ finToNat r) in
            rewrite plusCommutative p ((p * q) + S (finToNat r)) in
            rewrite remPrf in
            Refl

||| If p is a factor of n, then it is also a factor of any multiply of n.
export
multNAlsoFactor : Factor p n -> (a : Nat) -> {auto aok : LTE 1 a} -> Factor p (n * a)
multNAlsoFactor _ Z = absurd $ succNotLTEzero aok
multNAlsoFactor (CofactorExists q prf) (S a) =
        CofactorExists (q * S a) $
            rewrite prf in
            sym $ multAssociative p q (S a)

||| If p is a factor of both n and m, then it is also a factor of their sum.
export
plusFactor : Factor p n -> Factor p m -> Factor p (n + m)
plusFactor (CofactorExists qn prfN) (CofactorExists qm prfM) =
        rewrite prfN in
        rewrite prfM in
        rewrite sym $ multDistributesOverPlusRight p qn qm in
        multFactor p (qn + qm)

||| If p is a factor of a sum (n + m) and a factor of n, then it is also
||| a factor of m. This could be expressed more naturally with minus, but
||| it would be more difficult to prove, since minus lacks certain properties
||| that one would expect from decent subtraction.
export
minusFactor : {b : Nat} -> Factor p (a + b) -> Factor p a -> Factor p b
minusFactor (CofactorExists qab prfAB) (CofactorExists qa prfA) =
        CofactorExists (minus qab qa) $
            rewrite multDistributesOverMinusRight p qab qa in
            rewrite sym prfA in
            rewrite sym prfAB in
            replace {p = \x => b = minus (a + b) x} (plusZeroRightNeutral a) $
            rewrite plusMinusLeftCancel a b 0 in
            rewrite minusZeroRight b in
            Refl

||| A decision procedure for whether of not p is a factor of n.
export
decFactor : (n, d : Nat) -> DecFactor d n
decFactor Z Z = ItIsFactor $ reflexive {x = Z}
decFactor (S k) Z = ItIsNotFactor $ ZeroNotFactorS k
decFactor n (S d) =
        let Fraction n (S d) q r prf = Data.Fin.Extra.divMod n (S d) in
        case r of
            FZ =>
              ItIsFactor $ CofactorExists q $
                rewrite sym prf in
                  rewrite plusCommutative (q * (S d)) 0 in
                    multCommutative q (S d)

            (FS pr) =>
                ItIsNotFactor $ ProperRemExists q pr (
                        rewrite multCommutative d q in
                        rewrite sym $ multRightSuccPlus q d in
                        sym prf
                    )

||| For all p greater than 1, if p is a factor of n, then it is NOT a factor
||| of (n + 1).
export
factNotSuccFact : {p : Nat} -> GT p 1 -> Factor p n -> NotFactor p (S n)
factNotSuccFact {p = Z} pGt1 (CofactorExists q prf) =
        absurd $ succNotLTEzero pGt1
factNotSuccFact {p = S Z} pGt1 (CofactorExists q prf) =
        absurd . succNotLTEzero $ fromLteSucc pGt1
factNotSuccFact {p = S (S k)} pGt1 (CofactorExists q prf) =
        ProperRemExists q FZ (
            rewrite sym prf in
            rewrite plusCommutative n 1 in
            Refl
        )

using (p : Nat)
  ||| The relation of common factor is symmetric, that is if p is a
  ||| common factor of n and m, then it is also a common factor of
  ||| m and n.
  public export
  Symmetric Nat (CommonFactor p) where
    symmetric (CommonFactorExists p pfx pfy) = CommonFactorExists p pfy pfx

  ||| The relation of greatest common divisor is symmetric.
  public export
  Symmetric Nat (GCD p) where
    symmetric {x = Z} {y = Z} (MkGCD _ _) impossible
    symmetric {x = S _} (MkGCD cf greatest) =
            MkGCD (symmetric {ty = Nat} cf) $
            \q, cf => greatest q (symmetric {ty = Nat} cf)
    symmetric {y = S _} (MkGCD cf greatest) =
            MkGCD (symmetric {ty = Nat} cf) $
            \q, cf => greatest q (symmetric {ty = Nat} cf)

||| If p is a common factor of a and b, then it is also a factor of their GCD.
||| This actually follows directly from the definition of GCD.
export
commonFactorAlsoFactorOfGCD : {p : Nat} -> Factor p a -> Factor p b -> GCD q a b -> Factor p q
commonFactorAlsoFactorOfGCD pfa pfb (MkGCD _ greatest) =
        greatest p (CommonFactorExists p pfa pfb)

||| 1 is a common factor of any pair of natural numbers.
export
oneCommonFactor : (a, b : Nat) -> CommonFactor 1 a b
oneCommonFactor a b = CommonFactorExists 1
        (CofactorExists a (rewrite plusZeroRightNeutral a in Refl))
        (CofactorExists b (rewrite plusZeroRightNeutral b in Refl))

||| Any natural number is a common factor of itself and itself.
export
selfIsCommonFactor : (a : Nat) -> {auto ok : LTE 1 a} -> CommonFactor a a a
selfIsCommonFactor a =
  let prf = reflexive {x = a} in
    CommonFactorExists a prf prf

-- Some helpers for the gcd function.
data Search : Type where
    SearchArgs : (a, b : Nat) -> LTE b a -> {auto bNonZero : LTE 1 b} -> Search

left : Search -> Nat
left (SearchArgs l _ _) = l

right : Search -> Nat
right (SearchArgs _ r _) = r

Sized Search where
    size (SearchArgs a b _) = a + b

notLteAndGt : (a, b : Nat) -> LTE a b -> Not (GT a b)
notLteAndGt Z _ _ aGtB = succNotLTEzero aGtB
notLteAndGt (S _) Z aLteB _ = succNotLTEzero aLteB
notLteAndGt (S k) (S j) aLteB aGtB = notLteAndGt k j (fromLteSucc aLteB) (fromLteSucc aGtB)

gcd_step : (x : Search) ->
           (rec : (y : Search) -> Smaller y x ->
                  (f : Nat ** GCD f (left y) (right y))) ->
           (f : Nat ** GCD f (left x) (right x))
gcd_step (SearchArgs Z _ bLteA {bNonZero}) _ = absurd . succNotLTEzero $ transitive {ty = Nat} bNonZero bLteA
gcd_step (SearchArgs _ Z _ {bNonZero}) _ = absurd $ succNotLTEzero bNonZero
gcd_step (SearchArgs (S a) (S b) bLteA) rec =
  case divMod (S a) (S b) of
    Fraction (S a) (S b) q FZ prf =>
        let sbIsFactor =
                rewrite multCommutative b q in
                rewrite sym $ multRightSuccPlus q b in
                replace {p = \x => S a = x} (plusZeroRightNeutral (q * S b)) $ sym prf
            skDividesA = CofactorExists q sbIsFactor
            skDividesB = reflexive {x = S b}
        in
        (S b ** MkGCD (CommonFactorExists (S b) skDividesA skDividesB)
                      (\q', (CommonFactorExists q' _ qfb) => qfb))

    Fraction (S a) (S b) q (FS r) prf =>
            let rLtSb = lteSuccRight $ elemSmallerThanBound r
                _ = the (LTE 1 q) $ case q of
                    Z => absurd . notLteAndGt (S $ finToNat r) b (elemSmallerThanBound r) $
                        replace {p = LTE (S b)} (sym prf) bLteA
                    (S k) => LTESucc LTEZero
                (f ** MkGCD (CommonFactorExists f prfSb prfRem) greatestSbSr) =
                    rec (SearchArgs (S b) (S $ finToNat r) rLtSb) $
                      rewrite plusCommutative a (S b) in
                        LTESucc . LTESucc . plusLteLeft b . fromLteSucc $
                          transitive {rel = LTE} (elemSmallerThanBound $ FS r) bLteA

                prfSa =
                    rewrite sym prf in
                    rewrite multCommutative q (S b) in
                    plusFactor (multNAlsoFactor prfSb q) prfRem
                greatest = the
                    ((q' : Nat) -> CommonFactor q' (S a) (S b) -> Factor q' f)
                    (\q', (CommonFactorExists q' qfa qfb) =>
                        let sbfqSb =
                                rewrite multCommutative q (S b) in
                                multFactor (S b) q
                            rightPrf = minusFactor {a = q * S b}
                                (rewrite prf in qfa)
                                (transitive {ty = Nat} qfb sbfqSb)
                        in
                        greatestSbSr q' (CommonFactorExists q' qfb rightPrf)
                    )
            in
            (f ** MkGCD (CommonFactorExists f prfSa prfSb) greatest)

||| An implementation of Euclidean Algorithm for computing greatest common
||| divisors. It is proven correct and total; returns a proof that computed
||| number actually IS the GCD. Unfortunately it's very slow, so improvements
||| in terms of efficiency would be welcome.
export
gcd : (a, b : Nat) -> {auto ok : NotBothZero a b} -> (f : Nat ** GCD f a b)
gcd Z Z impossible
gcd Z b =
    (b ** MkGCD (CommonFactorExists b (anythingFactorZero b) (reflexive {x = b})) $
        \q, (CommonFactorExists q _ prf) => prf
    )
gcd a Z =
    (a ** MkGCD (CommonFactorExists a (reflexive {x = a}) (anythingFactorZero a)) $
        \q, (CommonFactorExists q prf _) => prf
    )
gcd (S a) (S b) with (cmp (S a) (S b))
    gcd (S (b + S d)) (S b) | CmpGT d =
        sizeInd gcd_step $
          SearchArgs (S (b + S d)) (S b) $
            rewrite sym $ plusSuccRightSucc b d in
              LTESucc . lteSuccRight $ lteAddRight b

    gcd (S a) (S a)         | CmpEQ =
        (S a ** MkGCD (selfIsCommonFactor (S a))
                      (\q, (CommonFactorExists q qfa _) => qfa))

    gcd (S a) (S (a + S d)) | CmpLT d =
        let (f ** MkGCD prf greatest) =
              sizeInd gcd_step $
                SearchArgs (S (a + S d)) (S a) $
                  rewrite sym $ plusSuccRightSucc a d in
                    LTESucc . lteSuccRight $ lteAddRight a
        in
        (f ** MkGCD (symmetric {ty = Nat} prf)
                    (\q, cf => greatest q $ symmetric {ty = Nat} cf))

||| For every two natural numbers there is a unique greatest common divisor.
export
gcdUnique : GCD p a b -> GCD q a b -> p = q
gcdUnique (MkGCD pCFab greatestP) (MkGCD qCFab greatestQ) =
    antisymmetric (greatestQ p pCFab) (greatestP q qCFab)

divByGcdHelper : (a, b, c : Nat) -> GCD (S a) (S a * S b) (S a * c) -> GCD 1 (S b) c
divByGcdHelper a b c (MkGCD _ greatest) =
    MkGCD (CommonFactorExists 1 (oneIsFactor (S b)) (oneIsFactor c)) $
    \q, (CommonFactorExists q (CofactorExists qb prfQB) (CofactorExists qc prfQC)) =>
        let qFab = CofactorExists qb $
                rewrite multCommutative q (S a) in
                rewrite sym $ multAssociative (S a) q qb in
                rewrite sym $ prfQB in
                Refl
            qFac = CofactorExists qc $
                rewrite multCommutative q (S a) in
                rewrite sym $ multAssociative (S a) q qc in
                rewrite sym $ prfQC in
                Refl
            CofactorExists f prfQAfA =
                greatest (q * S a) (CommonFactorExists (q * S a) qFab qFac)
            qf1 = multOneSoleNeutral a (f * q) $
                rewrite multCommutative f q in
                rewrite multAssociative (S a) q f in
                rewrite sym $ multCommutative q (S a) in
                prfQAfA
        in
        CofactorExists f $
            rewrite multCommutative q f in
            sym qf1

||| For every two natural numbers, if we divide both of them by their GCD,
||| the GCD of resulting numbers will always be 1.
export
divByGcdGcdOne : {a, b, c : Nat} -> GCD a (a * b) (a * c) -> GCD 1 b c
divByGcdGcdOne {a = Z} (MkGCD _ _) impossible
divByGcdGcdOne {a = S a} {b = Z} {c = Z} (MkGCD {notBothZero} _ _) =
        case replace {p = \x => NotBothZero x x} (multZeroRightZero (S a)) notBothZero of
            LeftIsNotZero impossible
            RightIsNotZero impossible
divByGcdGcdOne {a = S a} {b = Z} {c = S c} gcdPrf@(MkGCD {notBothZero} _ _) =
        case replace {p = \x => NotBothZero x (S a * S c)} (multZeroRightZero (S a)) notBothZero of
            LeftIsNotZero impossible
            RightIsNotZero => symmetric {ty = Nat} $ divByGcdHelper a c Z $ symmetric {ty = Nat} gcdPrf
divByGcdGcdOne {a = S a} {b = S b} {c = Z} gcdPrf@(MkGCD {notBothZero} _ _) =
        case replace {p = \x => NotBothZero (S a * S b) x} (multZeroRightZero (S a)) notBothZero of
            RightIsNotZero impossible
            LeftIsNotZero => divByGcdHelper a b Z gcdPrf
divByGcdGcdOne {a = S a} {b = S b} {c = S c} gcdPrf =
        divByGcdHelper a b (S c) gcdPrf


module EffCont

data Exists : List a -> a -> Type where
    Here : Exists (x :: xs) x
    There : Exists xs x -> Exists (y :: xs) x

data Effect : List (Type -> Type) -> Type -> Type where
  Pure : a -> Effect xs a
  EffectC : {f : Type -> Type} -> Exists xs f -> f a -> Effect xs a

data Eff : List (Type -> Type) -> Type -> Type where
  Cont : ({r : Type} -> (a -> r) -> ({x : Type} -> Effect xs x -> (x -> r) -> r) -> r) -> Eff xs a

pure : a -> Eff xs a
pure x = Cont (\k, imp => imp (Pure x) k)

(>>=) : Eff xs a -> (a -> Eff xs b) -> Eff xs b
(>>=) (Cont g) f = Cont (\k, imp => g (\x => let (Cont g') = f x in g' k imp) imp)

-- State effect
data State : (s : Type) -> Type -> Type where
  Get : State s s
  Put : s -> State s ()

get : {auto e : Exists xs (State s)} -> Eff xs s
get {e} = Cont (\k, imp => imp (EffectC e Get) k)

put : {auto e : Exists xs (State s)} -> s -> Eff xs ()
put {e} s = Cont (\k, imp => imp (EffectC e (Put s)) k)

-- Reader effect
data Reader : (e : Type) -> Type -> Type where
  Ask : Reader e e

ask : {auto e : Exists xs (Reader en)} -> Eff xs en
ask {e} = Cont (\k, imp => imp (EffectC e Ask) k)

-- Interpreters
runPure : Effect [] x -> (x -> r) -> r
runPure (Pure x) k = k x
runPure (EffectC Here _) _ impossible
runPure (EffectC (There _) _) _ impossible

stateImp : State s x -> (x -> s -> r) -> s -> r
stateImp Get k st = k st st
stateImp (Put st') k st = k () st'

runState : (Effect xs x -> (x -> r) -> r) -> Effect (State s :: xs) x -> (x -> s -> r) -> s -> r
runState k (Pure x) k' = k' x
runState k (EffectC Here effect) k' = stateImp effect k'
runState k (EffectC (There xs) fa) k' = \st => k (EffectC xs fa) (\x => k' x st)

readerImp : Reader e x -> (x -> e -> r) -> e -> r
readerImp Ask k e = k e e

runReader : (Effect xs x -> (x -> r) -> r) -> Effect (Reader e :: xs) x -> (x -> e -> r) -> e -> r
runReader k (Pure x) k' = k' x
runReader k (EffectC Here effect) k' = readerImp effect k'
runReader k (EffectC (There xs) fa) k' = \e => k (EffectC xs fa) (\x => k' x e)

run : (a -> r) -> ({x : Type} -> Effect xs x -> (x -> r) -> r) -> Eff xs a -> r
run k imp (Cont f) = f k imp

example : Eff [State Int, Reader Int] Int
example = do s <- get
             put (s + 1)
             s <- get
             a <- ask
             pure (s + a)

test : (Int, Int, Int)
test = let f = run (\x, s, e => (x, s, e)) (\eff, k => runState (runReader runPure) eff k) example in f 1 2


module Verified.Functor.List

import Verified.Functor

instance VerifiedFunctor List where
  functorIdentity [] = Refl
  functorIdentity (x :: xs) =
    let IHxs = functorIdentity xs in ?lemma1
  functorComposition [] g1 g2 = Refl
  functorComposition (x :: xs) g1 g2 =
    let IHxs = functorComposition xs g1 g2 in ?lemma2

---------- Proofs ----------

Verified.Functor.List.lemma2 = proof
  intros
  rewrite IHxs
  exact Refl

Verified.Functor.List.lemma1 = proof
  intros
  rewrite IHxs
  exact Refl


module Flexidisc.Validation

%default total
%access public export

data Validation e a = Error e | Valid a

ValidationL : (e, a :Type) -> Type
ValidationL e a = Validation (List e) a

implementation Functor (Validation e) where
  map func (Error x) = Error x
  map func (Valid x) = Valid (func x)

implementation Semigroup e => Applicative (Validation e) where
  pure x = Valid x
  (<*>) (Error x) (Error y) = Error (x <+> y)
  (<*>) (Error x) (Valid _) = Error x
  (<*>) (Valid _) (Error y) = Error y
  (<*>) (Valid f) (Valid x) = Valid (f x)

implementation (Show e, Show a) => Show (Validation e a) where
  show (Error x) = "❌ " <+> show x
  show (Valid x) = "✅ " <+> show x

export
validate : (a -> e) -> (a -> Bool) -> a -> Validation e a
validate err f x = if f x then Valid x else Error (err x)

export
validateL : (Applicative f, Semigroup (f e)) => (a -> e) -> (a -> Bool) -> a -> Validation (f e) a
validateL err f x = if f x then Valid x else Error (pure (err x))


= Chapter 3

> module Exercises.Interactive

Import `Data.Vect`, because we'll need it later.

> import public Data.Vect

Export functions by default.

> %access export

== 3.2.4 Exercises

=== 1. `length : List a -> Nat`

> my_length : List a -> Nat

Move the point over `my_length` and press `M-RET d` to add a clause.

```idris
my_length xs = ?my_length_rhs
```

Move the point over `xs` and press `C-c C-c` to case split on it.

```idris
my_length [] = ?my_length_rhs_1
my_length (x :: xs) = ?my_length_rhs_2
```

With the point on `?my_length_rhs_1`, press `M-RET p` to have Idris fill in `0`.

> my_length [] = 0

Fill out `?my_length_rhs_2` manually.

> my_length (x :: xs) = S (my_length xs)

=== 2. `reverse : List a -> List a`

> my_reverse : List a -> List a

`M-RET d` on `my_reverse` to add a clause.

```idris
my_reverse xs = ?my_reverse_rhs
```

Case split on `xs` (`C-c C-c`).

```idris
my_reverse [] = ?my_reverse_rhs_1
my_reverse (x :: xs) = ?my_reverse_rhs_2
```

Have Idris fill in `?my_reverse_rhs_1`.

```idris
my_reverse [] = []
```

We're actually going to need to do some fold-like recursion here, so revert back
to `my_reverse xs = ?my_reverse_rhs` and add a `where` clause with a local
function, `go`.

```idris
my_reverse xs = ?my_reverse_rhs
  where go : List a -> List a -> List a
```

Add a clause for `go` (`M-RET d`).

```idris
my_reverse xs = ?my_reverse_rhs
  where
    go : List a -> List a -> List a
    go xs ys = ?go_rhs
```

Case split on `xs` (`C-c C-c`).

```idris
go [] ys = ?go_rhs_1
go (x :: xs) ys = ?go_rhs_2
```

Rename `ys` to `acc`(umulator).

```idris
go [] acc = ?go_rhs_1
go (x :: xs) acc = ?go_rhs_2
```

Have Idris fill in `?go_rhs_1` (`M-RET p`).

```idris
go [] acc = acc
```

Fill in `?go_rhs_2` and `?my_reverse_rhs` manually. Rename the top-level `xs`
to `lst` to avoid confusion/shadowing.

> my_reverse lst = go lst []
>   where
>     go : List a -> List a -> List a
>     go [] acc = acc
>     go (x :: xs) acc = go xs (x :: acc)

=== 3. `map : (a -> b) -> List a -> List b`

> my_map : (a -> b) -> List a -> List b

Press `M-RET d` on `map` to add a clause.

```idris
my_map f xs = ?my_map_rhs
```

Case split (`C-c C-c`) on `xs`.

```idris
my_map f [] = ?my_map_rhs_1
my_map f (x :: xs) = ?my_map_rhs_2
```

Have Idris fill in `?my_map_rhs_1` (`M-RET p`).

> my_map f [] = []

Fill in `?my_map_rhs_2` manually.

> my_map f (x :: xs) = f x :: my_map f xs

=== 4. `map : (a -> b) -> Vect n a -> Vect n b`

> my_vect_map : (a -> b) -> Vect n a -> Vect n b

Add a `my_vect_map` clause (`M-RET d`).

```idris
my_vect_map f xs = ?my_vect_map_rhs
```

~~Case split (`C-c C-c`) on `xs`.~~

N.B. That doesn't seem to work, for whatever reason,
so add the clauses manually.

```idris
my_vect_map f [] = ?my_vect_map_rhs_1
my_vect_map f (x :: xs) = ?my_vect_map_rhs_2
```

Emacs or Idris seems upset about the holes too, so fill them in manually.

> my_vect_map f [] = []
> my_vect_map f (x :: xs) = f x :: map f xs

== 3.3.3 Exercises

=== 1. Reimplement `transpose_mat` with `zipWith` instead of `transpose_helper`

*Type* `transpose_mat`.

```idris
transpose_mat : Vect m (Vect n elem) -> Vect n (Vect m elem)
```

*Define* `transpose_mat`.

N.B. Emacs or Idris is still upset, so this is not very interactive.

```idris
transpose_mat [] = ?transpose_mat_rhs_1
transpose_mat (x :: xs) = ?transpose_mat_rhs_2
```

*Type* `?transpose_mat_rhs_1`.

```idris
  elem : Type
     n : Nat
--------------------------------------
transpose_mat_rhs_1 : Vect n (Vect 0 elem)
```

Rename `?transpose_mat_rhs_1` to `?create_empties` and make a lemma (a.k.a. lift
it to a top-level function) with `M-RET i e`.

```idris
create_empties : Vect n (Vect 0 elem)

transpose_mat : Vect m (Vect n elem) -> Vect n (Vect m elem)
transpose_mat [] = create_empties
transpose_mat (x :: xs) = ?transpose_mat_rhs_2
```

*Type* `?transpose_mat_rhs_2`.

```idris
  elem : Type
     n : Nat
     x : Vect n elem
     k : Nat
    xs : Vect k (Vect n elem)
--------------------------------------
transpose_mat_rhs_2 : Vect n (Vect (S k) elem)
```

(Further) *define* `transpose_mat`.

```idris
create_empties : Vect n (Vect 0 elem)

transpose_mat : Vect m (Vect n elem) -> Vect n (Vect m elem)
transpose_mat [] = create_empties
transpose_mat (x :: xs) = let xs_trans = transpose_mat xs
                          in ?transpose_mat_rhs_2
```

Fill in `?transpose_mat_rhs_2` with `zipWith
and do away with the intermediate variable, `xs_trans`.

```idris
create_empties : Vect n (Vect 0 elem)

transpose_mat : Vect m (Vect n elem) -> Vect n (Vect m elem)
transpose_mat [] = create_empties
transpose_mat (x :: xs) = zipWith (::) x (transpose_mat xs)
```

*Define* `create_empties`.

> public export
> create_empties : Vect n (Vect 0 elem)
> create_empties {n} = replicate n []

> public export
> transpose_mat : Vect m (Vect n elem) -> Vect n (Vect m elem)
> transpose_mat [] = create_empties
> transpose_mat (x :: xs) = zipWith (::) x (transpose_mat xs)

=== 2. `addMatrix`

> public export
> addMatrix : Num numType =>
>             Vect rows (Vect cols numType) -> Vect rows (Vect cols numType) ->
>             Vect rows (Vect cols numType)
> addMatrix []          ys          = ys
> addMatrix (xs :: xxs) (ys :: yys) = zipWith (+) xs ys :: addMatrix xxs yys

=== 3. `multMatrix`

> public export
> multMatrix : Num numType =>
>              Vect n (Vect m numType) -> Vect m (Vect p numType) ->
>              Vect n (Vect p numType)
> multMatrix left right = map (go (transpose_mat right)) left
>   where
>     go : Vect p (Vect m numType) -> Vect m numType -> Vect p numType
>     go xs ys = map (sum . zipWith (*) ys) xs

\[
  \begin{pmatrix}
    1 &   2 \\
    3 &   4 \\
    5 &   6
  \end{pmatrix} \times
  \begin{pmatrix}
    7 &   8 &   9 &  10 \\
   11 &  12 &  13 &  14
  \end{pmatrix} =
  \begin{pmatrix}
   29 &  32 &  35 &  38 \\
   65 &  72 &  79 &  86 \\
  101 & 112 & 123 & 134
  \end{pmatrix}
\]


module ProofColDivSeqPostuProof

import ProofColDivSeqBase
import ProofColDivSeqPostulate

%default total
-- %language ElabReflection
%access export

%hide Language.Reflection.P

--            from ProofColDivSeqBase
-- ########################################
base0 : P Z
base0 = rewrite definiP Z in
        rewrite definiDivSeq0 in IsLimited00 (Z ** definiLimited0)
-- ########################################

--            from ProofColDivSeqMain
-- ########################################
-- ########################################

--            from sub0xxxxx
-- ########################################
-- 01 3(6x+1) --B[1,-2]--> 3x
b18x3To3x : (k : Nat)
  -> (Not . P) (S (plus (plus (plus k k) (plus k k)) (plus k k))) -> (Not . P) k
b18x3To3x k = rewrite definiP (S (plus (plus (plus k k) (plus k k)) (plus k k))) in
              rewrite definiP k in \prfF, prf => (prfF . IsLimited01 k) prf

-- 02 3(18x+4) --A[6,-4]--> 3(24x+3) -->E[2,-4]--> 3(2x)
ae54x12To6x : (l : Nat)
  -> (Not . P) (S (S (plus (plus (plus (plus (plus l l) l) (plus (plus l l) l))
                                 (S (plus (plus (plus l l) l) (plus (plus l l) l))))
                           (S (plus (plus (plus l l) l) (plus (plus l l) l))))))
    -> (Not . P) (plus l l)
ae54x12To6x l = rewrite definiP (S (S (plus (plus (plus (plus (plus l l) l) (plus (plus l l) l))
                                                  (S (plus (plus (plus l l) l) (plus (plus l l) l))))
                                            (S (plus (plus (plus l l) l) (plus (plus l l) l)))))) in
                rewrite definiP (plus l l) in \prfF, prf => (prfF . IsLimited02 l) prf

-- 03 3(18x+10) --A[6,-4]->C[4,-4]--> 3(8x+3)
ac54x30To24x9 : (l : Nat)
  -> (Not . P) (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l)))
                                    (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                              (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l)))))))))
    -> (Not . P) (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l))))))
ac54x30To24x9 l = rewrite definiP (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l)))
                                                       (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                                                 (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))) in
                  rewrite definiP (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))) in \prfF, prf => (prfF . IsLimited03 l) prf

-- 04 3(18x+16) --A[6,-4]->B[1,-2]--> 3(4x+3)
ab54x30To12x9 : (l : Nat)
  -> (Not . P) (S (S (S (S (plus (plus (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))
                                       (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))))))
                                 (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))))))))))
    -> (Not . P) (S (S (S (plus (plus l l) (plus l l)))))
ab54x30To12x9 l = rewrite definiP (S (S (S (S (plus (plus (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))
                                                          (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l))))))))
                                                    (S (S (S (plus (plus (plus l l) l) (S (S (plus (plus l l) l)))))))))))) in
                  rewrite definiP (S (S (S (plus (plus l l) (plus l l))))) in \prfF, prf => (prfF . IsLimited04 l) prf

-- 05 3(3x+2) --C[4,-4]--> 3x
c9x6To3x : (j : Nat)
  -> (Not . P) (S (S (plus (plus j j) j))) -> (Not . P) j
c9x6To3x j = rewrite definiP (S (S (plus (plus j j) j))) in
             rewrite definiP j in \prfF, prf => (prfF . IsLimited05 j) prf

-- 06 3(12x+3) --E[2,-4]--> 3x
e36x9To3x : (l : Nat)
  -> (Not . P) (S (S (S (plus (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l))) (plus (plus l l) (plus l l))))))
    -> (Not . P) l
e36x9To3x l = rewrite definiP (S (S (S (plus (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l))) (plus (plus l l) (plus l l)))))) in
              rewrite definiP l in \prfF, prf => (prfF . IsLimited06 l) prf

-- 07 3(36x+9) --F[5,-2]->C[4,-4]--> 3(32x+7)
fc108x27To96x21 :
  (o:Nat) -> (Not . P) (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                                     (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                               (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))))
                                         (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))
fc108x27To96x21 o =
  rewrite definiP (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                                      (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                                (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))))
                                          (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))))) in
  rewrite definiP (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))
  in \prfF, prf => (prfF . IsLimited07 o) prf

-- 08 3(36x+21) --F[5,-2]->B[1,-2]--> 3(16x+9)
fb108x63To48x27 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                                        (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                                  (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))
                                            (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))))
fb108x63To48x27 o =
  rewrite definiP (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                                   (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                             (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))
                                       (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))))))) in
  rewrite definiP (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))))
  in \prfF, prf => (prfF . IsLimited08 o) prf

-- 09 3(36x+33) --F[5,-2]->E[2,-4]--> 3(8x+7)
fe108x99To24x21 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                   (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                             (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))
                                       (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))
fe108x99To24x21 o = rewrite definiP (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                                        (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                                                  (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))
                                                            (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))))))) in
                    rewrite definiP (S (S (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))
                    in \prfF, prf => (prfF . IsLimited09 o) prf

-- 10 3(36x+6) --F[5,-2]->E[2,-4]--> 3(8x+1)
fe108x18To24x3 :
  (o:Nat) ->  (Not . P) (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                        (plus (plus (plus o o) o) (plus (plus o o) o)))
                                  (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o)))))
                            (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o)))))))))
    -> (Not . P) (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
fe108x18To24x3 o = rewrite definiP (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                                                 (plus (plus (plus o o) o) (plus (plus o o) o)))
                                                           (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o)))))
                                                     (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (plus (plus (plus o o) o) (plus (plus o o) o))))))))) in
                   rewrite definiP (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                   in \prfF, prf => (prfF . IsLimited10 o) prf

-- 11 3(36x+18) --F[5,-2]->C[4,-4]--> 3(32x+15)
fc108x54To96x45 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                                (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))
                                          (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))
                                    (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))))))))))
fc108x54To96x45 o = rewrite definiP (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                                                     (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))
                                                               (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))
                                                         (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))))) in
                    rewrite definiP (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                            (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))))))))))
                    in \prfF, prf => (prfF . IsLimited11 o) prf

-- 12 3(36x+30) --F[5,-2]->B[1,-2]--> 3(16x+13)
fb108x90To48x39 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                   (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))
                                             (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))
                                       (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))))))))
fb108x90To48x39 o = rewrite definiP (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                   (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))
                                             (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))
                                       (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))))))) in
                    rewrite definiP (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))))))))))
                    in \prfF, prf => (prfF . IsLimited12 o) prf

-- 13 3(36x+12) --F[5,-2]->B[1,-2]--> 3(16x+5)
fb108x36To48x15 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                                (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                          (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))
                                    (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o))))))))))))
    -> (Not . P) (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))
fb108x36To48x15 o = rewrite definiP (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (plus (plus o o) o))
                                                (S (plus (plus (plus o o) o) (plus (plus o o) o))))
                                          (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))
                                    (S (S (plus (plus (plus (plus o o) o) (plus (plus o o) o)) (S (plus (plus (plus o o) o) (plus (plus o o) o)))))))))))) in
                    rewrite definiP (S (S (S (S (S (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o)))))))))
                    in \prfF, prf => (prfF . IsLimited13 o) prf

-- 14 3(36x+24) --F[5,-2]->E[2,-4]--> 3(8x+5)
fe108x72To24x15 :
  (o:Nat) ->  (Not . P) (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                              (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                        (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))
                                  (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))))))))
    -> (Not . P) (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))
fe108x72To24x15 o = rewrite definiP (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (plus (plus o o) o)))
                                              (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))
                                        (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o))))))))))
                                  (S (S (S (plus (plus (plus (plus o o) o) (S (plus (plus o o) o))) (S (S (plus (plus (plus o o) o) (S (plus (plus o o) o)))))))))))))))) in
                    rewrite definiP (S (S (S (S (S (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))
                    in \prfF, prf => (prfF . IsLimited14 o) prf

-- 15 3(36x+36) --F[5,-2]->C[4,-4]--> 3(32x+31)
fc108x108To96x93 :
  (o:Nat) -> (Not . P) (S (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                      (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                                (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))
                                          (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))))))))))))))
    -> (Not . P) (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                                                (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))))))))))))))))))))))))))
fc108x108To96x93 o = rewrite definiP (S (S (S (S (S (S (S (plus (plus (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))
                                                      (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o))))))))
                                                (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))
                                          (S (S (S (S (plus (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))) (S (S (S (plus (plus (plus o o) o) (S (S (plus (plus o o) o)))))))))))))))))))) in
                     rewrite definiP (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (S (plus (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))
                                                                                (plus (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))) (plus (plus (plus o o) (plus o o)) (plus (plus o o) (plus o o))))))))))))))))))))))))))))))))))))
                     in \prfF, prf => (prfF . IsLimited15 o) prf
-- ########################################


module Tasks

-- Based on https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems-final.pdf

data Task : (c : (Type -> Type) -> Type) -> Type -> Type -> Type where
  MkTask : ({f : Type -> Type} -> c f -> (k -> f v) -> f v) -> Task c k v

Tasks : ((Type -> Type) -> Type) -> Type -> Type -> Type
Tasks c k v = k -> Maybe (Task c k v)

data Const m a = MkConst m

Functor (Const m) where
  map _ (MkConst m) = MkConst m

[const] Monoid m => Applicative (Const m) where
  pure _ = MkConst neutral
  (MkConst x) <*> (MkConst y) = MkConst (x <+> y)

sprsh1 : Tasks Applicative String Integer
sprsh1 "B1" = Just $ MkTask $ \constr, fetch => B1 @{constr} fetch
  where
    B1 : Applicative f => (String -> f Integer) -> f Integer
    B1 fetch = (+) <$> fetch "A1" <*> fetch "A2"
sprsh1 "B2" = Just $ MkTask $ \constr, fetch => B2 @{constr} fetch
  where
    B2 : Applicative f => (String -> f Integer) -> f Integer
    B2 fetch = (* 2) <$> fetch "B1"
sprsh1 _ = Nothing

dependencies : Task Applicative k v -> List k
dependencies (MkTask task) = let (MkConst xs) = task const (\k => MkConst [k]) in xs

test1 : Maybe (List String)
test1 = sprsh1 "B1" >>= \task => Just $ dependencies task

test2 : Maybe (List String)
test2 = sprsh1 "B2" >>= \task => Just $ dependencies task


module SmallStepCombined

%access public export

%default total

data Tm : Type where
  C : Nat -> Tm
  P : Tm -> Tm -> Tm
  Tru : Tm
  Fls : Tm
  If : Tm -> Tm -> Tm -> Tm

data Value : Tm -> Type where
  V_Const : (n : Nat) -> Value (C n)
  V_Tru : Value Tru
  V_Fls : Value Fls

Uninhabited (Value (P _ _)) where
  uninhabited (V_Const _) impossible
  uninhabited V_Tru impossible
  uninhabited V_Fls impossible

data Step : Tm -> Tm -> Type where
  ST_PlusConstConst : Step (P (C n1) (C n2)) (C (n1 + n2))
  ST_Plus1 : Step t1 t1' -> Step (P t1 t2) (P t1' t2)
  ST_Plus2 : Value v1 -> Step t2 t2' -> Step (P v1 t2) (P v1 t2')
  ST_IfTrue : Step (If Tru t1 t2) t1
  ST_IfFalse : Step (If Fls t1 t2) t2
  ST_If : Step t1 t1' -> Step (If t1 t2 t3) (If t1' t2 t3)

Uninhabited (Step (C n) _) where
  uninhabited ST_PlusConstConst impossible
  uninhabited (ST_Plus1 _) impossible
  uninhabited (ST_Plus2 _ _) impossible
  uninhabited ST_IfTrue impossible
  uninhabited ST_IfFalse impossible
  uninhabited (ST_If _) impossible

Uninhabited (Step Tru _) where
  uninhabited ST_PlusConstConst impossible
  uninhabited (ST_Plus1 _) impossible
  uninhabited (ST_Plus2 _ _) impossible
  uninhabited ST_IfTrue impossible
  uninhabited ST_IfFalse impossible
  uninhabited (ST_If _) impossible

Uninhabited (Step Fls _) where
  uninhabited ST_PlusConstConst impossible
  uninhabited (ST_Plus1 _) impossible
  uninhabited (ST_Plus2 _ _) impossible
  uninhabited ST_IfTrue impossible
  uninhabited ST_IfFalse impossible
  uninhabited (ST_If _) impossible

infix 4 -+>

(-+>) : Tm -> Tm -> Type
(-+>) = Step

step_deterministic : Step x y1 -> Step x y2 -> y1 = y2
step_deterministic ST_PlusConstConst ST_PlusConstConst = Refl
step_deterministic (ST_Plus1 s1) (ST_Plus2 (V_Const _) _) = absurd s1
step_deterministic (ST_Plus1 s1) (ST_Plus2 V_Tru _) = absurd s1
step_deterministic (ST_Plus1 s1) (ST_Plus2 V_Fls _) = absurd s1
step_deterministic (ST_Plus2 (V_Const _) _) (ST_Plus1 s1) = absurd s1
step_deterministic (ST_Plus2 V_Tru _) (ST_Plus1 s1) = absurd s1
step_deterministic (ST_Plus2 V_Fls _) (ST_Plus1 s1) = absurd s1
step_deterministic (ST_Plus1 s1) (ST_Plus1 s2) =
  rewrite step_deterministic s1 s2
  in Refl
step_deterministic (ST_Plus2 v1 s1) (ST_Plus2 v2 s2) =
  rewrite step_deterministic s1 s2
  in Refl
step_deterministic ST_IfTrue ST_IfTrue = Refl
step_deterministic ST_IfFalse ST_IfFalse = Refl
step_deterministic (ST_If s1) (ST_If s2) =
  rewrite step_deterministic s1 s2
  in Refl

strong_progress : (t : Tm ** (Not (Value t), Not (t' : Tm ** t -+> t')))
strong_progress = (P Tru (C 0) ** ( \v => absurd v
                                  , \(_ ** s) => case s of
                                      ST_Plus1 s1 => absurd s1
                                      ST_Plus2 _ s2 => absurd s2 ))


module Matrix

import Data.Vect

%default total

transposeHelper : Vect n elem -> Vect n (Vect len elem) -> Vect n (Vect (S len) elem)
transposeHelper [] [] = []
transposeHelper (x :: xs) (y :: ys) = (x :: y) :: transposeHelper xs ys

transposeMatrix : { n, m : _ } -> Vect m (Vect n elem) -> Vect n (Vect m elem)
transposeMatrix [] = replicate n []
transposeMatrix (x :: xs) = let xsTrans = transposeMatrix xs in
                                transposeHelper x xsTrans


StringOrInt : Bool -> Type
StringOrInt x = case x of
                    True => Int
                    False => String

getStringOrInt : (x : Bool) -> StringOrInt x
getStringOrInt x = case x of
                        True => 94
                        False => "Ninety four"

valToString : (x : Bool) -> StringOrInt x -> String
valToString x val = case x of
                        True => cast val
                        False => val

valToStringHoles : (x : Bool) -> StringOrInt x -> String
valToStringHoles x val = case x of
                        True => ?xtrueType
                        False => ?xfalseType


module Toolkit.Data.List.Occurs.Does.Not

import Decidable.Equality

import Toolkit.Decidable.Informative

import Toolkit.Data.List.Occurs.Error

%default total

public export
data Occurs : (type : Type)
                 -> (p    : type -> Type)
                 -> (xs   : List type)
                 -> (cy   : Nat)
                         -> Type
  where
    End : Occurs type p Nil Z

    Yes : (holds : p x)
       -> (rest  : Occurs type p     xs  cn)
                -> Occurs type p (x::xs) cn

    No : (nope : Not (p x))
      -> (rest : Occurs type p     xs     cn)
              -> Occurs type p (x::xs) (S cn)

export
Uninhabited (Occurs type p Nil (S x)) where
  uninhabited End impossible
  uninhabited (Yes holds rest) impossible
  uninhabited (No nope rest) impossible

namespace Exactly

  shouldBeZero : (p x -> Void)
              -> Does.Not.Occurs type p (x :: xs) 0 -> Void
  shouldBeZero f (Yes holds rest) = f holds

  shouldNotOccurMore : (Does.Not.Occurs type p xs k -> Void)
                    -> (p x -> Void)
                    -> Does.Not.Occurs type p (x :: xs) (S k) -> Void
  shouldNotOccurMore f g (Yes holds rest) = g holds
  shouldNotOccurMore f g (No nope rest) = f rest

  wrongOccursNot : (Does.Not.Occurs type p xs cn -> Void)
                 -> p x
                 -> Does.Not.Occurs type p (x :: xs) cn -> Void
  wrongOccursNot f y (Yes holds rest) = f rest
  wrongOccursNot f y (No nope rest) = nope y

  export
  doesNotExactlyOccur : {type : Type}
              -> {p    : type -> Type}
              -> (f    : (this : type) -> Dec (p this))
              -> (xs   : List type)
              -> (cn   : Nat)
                      -> DecInfo Occurs.Error
                                 (Does.Not.Occurs type p xs cn)
  doesNotExactlyOccur f [] Z
    = Yes End

  doesNotExactlyOccur f [] (S k)
    = No (MkError Z (S k))
         absurd

  doesNotExactlyOccur f (x :: xs) cn with (f x)
    doesNotExactlyOccur f (x :: xs) cn | (Yes prf) with (doesNotExactlyOccur f xs cn)
      doesNotExactlyOccur f (x :: xs) cn | (Yes prf) | (Yes y)
        = Yes (Yes prf y)

      doesNotExactlyOccur f (x :: xs) cn | (Yes prf) | (No msg contra)
        = No (MkError cn (found msg))
             (wrongOccursNot contra prf)

    doesNotExactlyOccur f (x :: xs) cn | (No contra) with (cn)

      doesNotExactlyOccur f (x :: xs) cn | (No contra) | 0
        = No (MkError cn Z)
             (shouldBeZero contra)

      doesNotExactlyOccur f (x :: xs) cn | (No contra) | (S k) with (doesNotExactlyOccur f xs k)
        doesNotExactlyOccur f (x :: xs) cn | (No contra) | (S k) | (Yes prf)
          = Yes (No contra prf)

        doesNotExactlyOccur f (x :: xs) cn | (No contra) | (S k) | (No msg g)
          = No (MkError cn (S k))
               (shouldNotOccurMore g contra)

-- [ EOF ]