module Util

import public Effects
import public Effect.Random

import public Data.Vect
import public Data.Matrix
import public Data.Matrix.Numeric

%access public export

infixl 5 <+ -- add a vector to each row of a matrix

||| Maximum size of the integer distribution
RAND_MAX : Integer
RAND_MAX = 32767

||| Generate a random Double in the range of  [0.0, 1.0]
rndUnitDouble : Eff Double [RND]
rndUnitDouble =
  pure $ (fromInteger !(rndInt 0 RAND_MAX)) / fromInteger RAND_MAX

||| Generate a randome Double in the range of [min, max]
rndDouble : (min : Double) -> (max : Double) -> Eff Double [RND]
rndDouble min max =
  pure $ (min + (max - min) * !rndUnitDouble)

||| Generate a random vector of length n of Doubles in the range of [min, max]
rndVect : (n : Nat) ->
          (min: Double) ->
          (max: Double) ->
          Eff (Vect n Double) [RND]
rndVect Z _ _ = pure []
rndVect (S k) min max = pure (!(rndDouble min max) :: !(rndVect k min max))

||| Generate an n x m matrix of random Doubles in the range of [min, max]
rndMat : (n : Nat) ->
         (m : Nat) ->
         (min : Double) ->
         (max: Double) ->
         Eff (Matrix n m Double) [RND]
rndMat Z _ _ _ = pure []
rndMat (S k) m min max = pure (!(rndVect m min max) :: !(rndMat k m min max))

||| Generate a vector of length n of Doubles initialized to 0.0
zeroVect : (n : Nat) -> Vect n Double
zeroVect Z = []
zeroVect (S k) = 0.0 :: zeroVect k

||| Generate an n x m matrix of random Doubles initialized to 0.0
zeroMat : (n : Nat) -> (m : Nat) -> Matrix n m Double
zeroMat Z _ = []
zeroMat (S k) m = zeroVect m :: zeroMat k m

||| Add a vector to each row of a matrix
(<+) : Matrix n m Double -> Vect m Double -> Matrix n m Double
(<+) [] _ = []
(<+) (x :: xs) v = (x + v) :: (xs <+ v)


module ZZUtils
import ZZ
import NatUtils
%access public export
%default total

data IsNonNegative : ZZ->Type where
 NonNegative :(IsNonNegative (Pos k))

data NotBothZeroZ :ZZ->ZZ->Type where
  LeftPositive :NotBothZeroZ (Pos (S k)) m
  LeftNegative :NotBothZeroZ (NegS k) m
  RightPositive :NotBothZeroZ m (Pos (S k))
  RightNegative :NotBothZeroZ m (NegS k)

data NotZero:ZZ->Type where
  PositiveZ:NotZero (Pos (S k))
  NegativeZ:NotZero (NegS k)

data IsNegative:ZZ->Type where
  Negative: IsNegative (NegS k)

|||Zero is not positive
zeroNotPos:IsPositive (Pos Z)->Void
zeroNotPos Positive impossible

|||Proof that NotBothZeroZ 0 0 is uninhabited
zeroZeroBothZero:NotBothZeroZ (Pos Z) (Pos Z) ->Void
zeroZeroBothZero LeftPositive impossible
zeroZeroBothZero LeftNegative impossible
zeroZeroBothZero RightPositive impossible
zeroZeroBothZero RightNegative impossible

|||Decides whther two numbers are not both zero or not.
decNotBothZero : (a:ZZ)->(b:ZZ)->Dec (NotBothZeroZ a b)
decNotBothZero (Pos Z) (Pos Z) = No ( zeroZeroBothZero)
decNotBothZero (Pos Z) (Pos (S k)) = Yes RightPositive
decNotBothZero (Pos Z) (NegS k) = Yes RightNegative
decNotBothZero (Pos (S k)) b = Yes LeftPositive
decNotBothZero (NegS k) b = Yes LeftNegative

|||Checks 2 numbers and produces a proof of either (a=0,b=0) or (NotBothZeroZ a b)
checkNotBothZero : (a:ZZ)->(b:ZZ)->Either ((a=0),(b=0)) (NotBothZeroZ a b)
checkNotBothZero (Pos Z) (Pos Z) = Left (Refl, Refl)
checkNotBothZero (Pos Z) (Pos (S k)) = Right RightPositive
checkNotBothZero (Pos Z) (NegS k) = Right RightNegative
checkNotBothZero (Pos (S k)) b = Right LeftPositive
checkNotBothZero (NegS k) b = Right LeftNegative

|||Proof that positive numbers are not zero
posThenNotZero:{a:ZZ}->(IsPositive a)->(NotZero a)
posThenNotZero {a = (Pos (S k))} Positive = PositiveZ

NegSThenNotZero:{a:ZZ}->(IsNegative a)->(NotZero a)
NegSThenNotZero {a = (NegS k)} ZZUtils.Negative = NegativeZ

|||Rewrites a=b as a=1*b
rewriteRightAsOneTimesRight: {a:ZZ}->{b:ZZ}->(a=b)->(a=(1*b))
rewriteRightAsOneTimesRight {a}{b}prf = rewrite ( (multOneLeftNeutralZ b)) in prf
|||Rewrites a=b as 1*a=b
rewriteLeftAsOneTimesLeft: {a:ZZ}->{b:ZZ}->(a=b)->(1*a=b)
rewriteLeftAsOneTimesLeft {a}{b}prf = rewrite ( (multOneLeftNeutralZ a)) in prf
|||Rewrites 1*a = b as a=b
rewriteOneTimesLeftAsLeft:{a:ZZ}->{b:ZZ}->((1*a) = b)->(a=b)
rewriteOneTimesLeftAsLeft {a}{b}prf  =
  rewrite ( sym $(multOneLeftNeutralZ a)) in prf
|||Rewrites   (a*1)=b as a=b
rewriteLeftTimesOneAsLeft : {a:ZZ}->{b:ZZ}->((a*1)=b)->(a=b)
rewriteLeftTimesOneAsLeft {a}{b}prf =
  rewrite ( sym $(multOneRightNeutralZ a)) in prf

|||Proof that negative integers are not non negative
negsAreNotNonNegs:(IsNonNegative (NegS k))->Void
negsAreNotNonNegs NonNegative impossible
|||A function that checks whether an integer is non negative and returns a
|||proof or a contradiction
decideIsNonNeg:(a: ZZ) -> Dec (IsNonNegative   a)
decideIsNonNeg (Pos k) = Yes NonNegative
decideIsNonNeg (NegS k) = No (negsAreNotNonNegs)
|||Proof that natural numbers are non negative
naturalsNonNegative: (x:Nat)->IsNonNegative (Pos x)
naturalsNonNegative x = NonNegative

|||The less than type for ZZ
LTZ :ZZ->ZZ->Type
LTZ a b = LTEZ (1+a) b

|||Theorem that if a and b are natural numbers and a<b
|||then, (Pos a) <(Pos b)
ltNatToZZ:{a:Nat}->{b:Nat}->(LT a b)->(LTZ (Pos a) (Pos b))
ltNatToZZ (LTESucc x) = PositiveLTE (LTESucc x)
|||Proves that a<= a for any integer a
lteReflZ:{a:ZZ}->LTEZ a a
lteReflZ {a =(Pos k)} = PositiveLTE (lteRefl {n=k})
lteReflZ {a= (NegS k)} = NegativeLte (lteRefl {n=k})
|||Proof that a number is less than its successor
numLtSucc:(a:ZZ)->LTZ a (1+a)
numLtSucc a = lteReflZ
|||Proof that one is not less than zero
oneNotLTEZero:(LTEZ (Pos (S Z)) (Pos Z))->Void
oneNotLTEZero (PositiveLTE LTEZero) impossible
oneNotLTEZero (PositiveLTE (LTESucc _)) impossible
|||Proof that a number greater than or equal to 1 is positive
gteOnePositive:(a:ZZ)->(LTEZ 1 a)->(IsPositive a)
gteOnePositive (Pos Z) (PositiveLTE LTEZero) impossible
gteOnePositive (Pos Z) (PositiveLTE (LTESucc _)) impossible
gteOnePositive (Pos (S k)) x = Positive
gteOnePositive (NegS _) (PositiveLTE _) impossible
gteOnePositive (NegS _) NegLessPositive impossible
gteOnePositive (NegS _) (NegativeLte _) impossible

|||Proof that it is impossible that (1+a)<=a
succNotLteNumZ:(a:ZZ)->(LTEZ (1+a) a) ->Void
succNotLteNumZ (Pos k)  y =
  impliesContrapositive (LTEZ (Pos (S k)) (Pos k)) (LTE (S k) k)
    LTEZZtoNat  (succNotLTEnum k) y
succNotLteNumZ (NegS Z)  (PositiveLTE _)  impossible
succNotLteNumZ (NegS Z)  NegLessPositive  impossible
succNotLteNumZ (NegS Z)  (NegativeLte _)  impossible
succNotLteNumZ (NegS (S k))  y  =
  impliesContrapositive (LTEZ (NegS k) (NegS (S k))) (LTE (S k) k)
     LTEZZtoNatNeg (succNotLTEnum k) y

|||Multiplying positive numbers gives a positive number.
posMultPosIsPos: (IsPositive m)->(IsPositive d)->(IsPositive (m*d))
posMultPosIsPos{m = (Pos (S k))}{d = (Pos (S j))} Positive Positive = Positive
|||The theorem that (-a)*(-b)=a*b
negateMultNegateNeutralZ: {a:ZZ}->{b:ZZ}->((-a)*(-b)=a*b)
negateMultNegateNeutralZ{a}{b} =
  rewrite (multNegateRightZ (-a) b) in
  rewrite (multNegateLeftZ a b ) in
  rewrite (doubleNegElim (a*b)) in Refl
|||Proof that zero times an integer is not positive
posIsNotZeroTimesInteger:((Pos (S k))=(Pos Z)*a)->Void
posIsNotZeroTimesInteger{a = (Pos _)} Refl impossible
posIsNotZeroTimesInteger{a = (NegS _)} Refl impossible
|||Proof that Zero times an integer is not negative
negSIsNotZeroTimesInteger :((NegS k)=(Pos Z)*a)->Void
negSIsNotZeroTimesInteger {a = (Pos _)} Refl impossible
negSIsNotZeroTimesInteger {a = (NegS _)} Refl impossible
|||Converts a nonNegative integer into the corresponding Natural number
nonNegIntegerToNat:(a:ZZ)->(IsNonNegative a)->Nat
nonNegIntegerToNat (Pos k) NonNegative = k
|||Proof that a positive number is not zero times a negative number
zeroTimesNegIsNotPos:(Pos (S k)) = (Pos Z)*(NegS j)->Void
zeroTimesNegIsNotPos Refl impossible
|||Proof that a positive number is not a negative number times zero
negTimesZeroIsNotPos :(Pos (S k)) = (NegS j)*(Pos Z)->Void
negTimesZeroIsNotPos {k}{j}prf =
  zeroTimesNegIsNotPos{k=k}{j=j}
     (rewrite (sym(multCommutativeZ (NegS j) (Pos Z))) in prf)

|||Lemma that (c*d)*n = (c*n)*d
multCommuAndAssocZ1: {c:ZZ}->{d:ZZ}->{n:ZZ}->(c*d)*n = (c*n)*d
multCommuAndAssocZ1 {c}{d}{n}= rewrite (sym(multAssociativeZ c n d)) in
                           rewrite (multCommutativeZ n d ) in
                           rewrite( (multAssociativeZ c d n))in
                           Refl

|||Proof that if (a*b=1) then either (a=1,b=1)  or (a=-1,b=-1)
productOneThenNumbersOne: (a:ZZ)->(b:ZZ)->(a*b=1) ->Either (a=1,b=1) (a=(-1),b=(-1))
productOneThenNumbersOne (Pos Z) (Pos _) Refl impossible
productOneThenNumbersOne (Pos Z) (NegS _) Refl impossible
productOneThenNumbersOne (Pos (S Z)) (Pos Z) prf = void (posIsNotPosMultZero {k=Z} (sym prf))
productOneThenNumbersOne (Pos (S Z)) (Pos (S Z)) prf = Left (Refl, Refl)
productOneThenNumbersOne (Pos (S Z)) (Pos (S (S _))) Refl impossible
productOneThenNumbersOne (Pos (S Z)) (NegS _) Refl impossible
productOneThenNumbersOne (Pos (S (S k))) (Pos Z) prf = void (posIsNotPosMultZero  (sym prf))
productOneThenNumbersOne (Pos (S (S _))) (Pos (S Z)) Refl impossible
productOneThenNumbersOne (Pos (S (S _))) (Pos (S (S _))) Refl impossible
productOneThenNumbersOne (Pos (S (S _))) (NegS _) Refl impossible
productOneThenNumbersOne (NegS Z) (Pos Z) Refl impossible
productOneThenNumbersOne (NegS Z) (Pos (S _)) Refl impossible
productOneThenNumbersOne (NegS Z) (NegS Z) prf = Right (Refl,Refl)
productOneThenNumbersOne (NegS Z) (NegS (S _)) Refl impossible
productOneThenNumbersOne (NegS (S k)) (Pos Z) prf = void (negTimesZeroIsNotPos{k=Z} (sym prf))
productOneThenNumbersOne (NegS (S _)) (Pos (S _)) Refl impossible
productOneThenNumbersOne (NegS (S _)) (NegS Z) Refl impossible
productOneThenNumbersOne (NegS (S _)) (NegS (S _)) Refl impossible

|||Right cancellation law for multiplication when right is positive
multRightCancelPosZ:(left1:ZZ)->(left2:ZZ)->(right:ZZ)->
   IsPositive right->(left1*right=left2*right)->(left1 = left2)
multRightCancelPosZ (Pos j) (Pos i) (Pos (S k)) Positive prf =
 cong (multRightCancel j i (S k) SIsNotZ expression) where
   expression = posInjective prf
multRightCancelPosZ (Pos _) (NegS _) (Pos (S _)) Positive Refl impossible
multRightCancelPosZ (NegS _) (Pos _) (Pos (S _)) Positive Refl impossible
multRightCancelPosZ (NegS j) (NegS i) (Pos (S k)) Positive prf =
  cong (succInjective j i (multRightCancel (S j) (S i) (S k) SIsNotZ expression)) where
    expression = posInjective (negativesSameNumbersSame prf)

|||Right cancellation law for multiplication given a proof that right is not zero
multRightCancelZ:(left1:ZZ)->(left2:ZZ)->(right:ZZ)->
   NotZero right->(left1*right=left2*right)->(left1 = left2)
multRightCancelZ left1 left2 (Pos (S k)) PositiveZ prf =
  multRightCancelPosZ left1 left2 (Pos (S k)) Positive prf
multRightCancelZ left1 left2 (NegS k) NegativeZ prf =
  multRightCancelPosZ left1 left2 (-(NegS k)) Positive expression where
    expression = rewrite (multNegateRightZ left1 (NegS k)) in
                 rewrite (multNegateRightZ left2 (NegS k)) in
                  (numbersSameNegativesSame prf)
|||Left Cancellation law for multiplication given a proof that left is not zero
multLeftCancelZ :(left : ZZ) -> (right1 : ZZ) -> (right2 : ZZ) ->
  NotZero left -> (left*right1 = left*right2) -> (right1 = right2)
multLeftCancelZ left right1 right2 x prf =
  multRightCancelZ right1 right2 left x expression where
    expression = rewrite (multCommutativeZ right1 left) in
                 rewrite (multCommutativeZ right2 left) in
                  prf

|||Proof that (-a)*(-b) = a*b
multNegNegNeutralZ: (a:ZZ)->(b:ZZ)->(-a)*(-b)=a*b
multNegNegNeutralZ a b =
  rewrite multNegateRightZ (-a) b in
  rewrite multNegateLeftZ a b in
  rewrite doubleNegElim (a*b) in
  Refl

|||The theorem that if  a =r+q*b and g = (m1*b)+(n1*rem)
|||Then g = (n1*a) + ((m1+n1*(-quot))*b)
bezoutReplace:{a:ZZ}->{b:ZZ}->{m1:ZZ}->{rem:ZZ}->
  {quot:ZZ}->{n1:ZZ}->{g:ZZ}-> (a = rem + (quot *b))->
     (g=(m1*b)+(n1*rem))->(g = (n1*a) + ((m1+n1*(-quot))*b))
bezoutReplace {a}{b}{m1}{rem}{quot}{n1}{g}prf prf1 =
  rewrite multDistributesOverPlusLeftZ m1 (n1*(-quot)) b in
  rewrite prf in
  rewrite multDistributesOverPlusRightZ n1 rem (quot*b) in
  rewrite (sym (plusAssociativeZ (n1*rem) (n1*((quot)*b)) ((m1*b)+((n1*(-quot))*b)))) in
  rewrite (plusCommutativeZ (m1*b) ((n1*(-quot))*b)) in
  rewrite (plusAssociativeZ (n1*((quot)*b)) ((n1*(-quot))*b) (m1*b)) in
  rewrite (multAssociativeZ n1 quot b) in
  rewrite (sym (multDistributesOverPlusLeftZ (n1*quot) (n1*(-quot)) b)) in
  rewrite (sym(multDistributesOverPlusRightZ n1 quot (-quot))) in
  rewrite (plusNegateInverseLZ quot) in
  rewrite (multZeroRightZeroZ n1) in
  rewrite (multZeroLeftZeroZ b) in
  rewrite (plusZeroLeftNeutralZ (m1*b)) in
  rewrite (plusCommutativeZ (n1*rem) (m1*b)) in
   prf1

zeroNotNonZero: (NotZero (Pos Z)) -> Void
zeroNotNonZero PositiveZ impossible
zeroNotNonZero NegativeZ impossible

decZero: (a: ZZ) -> Dec (NotZero a)
decZero (Pos Z) = No zeroNotNonZero
decZero (Pos (S k)) = Yes (posThenNotZero Positive)
decZero (NegS k) = Yes (NegSThenNotZero Negative)

nonZeroNotZero: (a: ZZ) -> ( (a = (Pos Z) ) -> Void) -> (NotZero a)
nonZeroNotZero (Pos Z) f = ?nonZeroNotZero_rhs_1
nonZeroNotZero (Pos (S k)) f = ?nonZeroNotZero_rhs_4
nonZeroNotZero (NegS k) f = ?nonZeroNotZero_rhs_2


||| In a discrete ordered group, distinct elements are at least a
||| fixed `unit` distance apart.  The group is assumed to be abelian.
module Specifications.DiscreteOrderedGroup

import public Specifications.OrderedGroup

%default total
%access public export

||| specification
isDiscreteOrder : Binop s -> Binrel s -> s -> s -> Type
isDiscreteOrder (+) (<=) zero unit = (x : _) ->
  Not (x = zero) -> x <= zero -> unit + x <= zero

||| composed specification
data DiscreteOrderedGroupSpec :
       Binop s -> s -> (s -> s) -> Binrel s -> s -> Type where
  MkDiscreteOrderedGroup :
    OrderedGroupSpec add zero neg leq ->
    isAbelian add ->
    isDiscreteOrder add leq zero unit ->
    DiscreteOrderedGroupSpec add zero neg leq unit

||| forget
orderedGroup : DiscreteOrderedGroupSpec add zero neg leq _ ->
  OrderedGroupSpec add zero neg leq
orderedGroup (MkDiscreteOrderedGroup g _ _) = g

||| forget
partiallyOrderedGroup : DiscreteOrderedGroupSpec add zero neg leq _ ->
  PartiallyOrderedGroupSpec add zero neg leq
partiallyOrderedGroup spec = partiallyOrderedGroup (orderedGroup spec)

||| forget more
group : DiscreteOrderedGroupSpec add zero neg _ _ -> GroupSpec add zero neg
group spec = group (partiallyOrderedGroup spec)

||| forget
discreteOrder : DiscreteOrderedGroupSpec add zero _ leq unit ->
  isDiscreteOrder add leq zero unit
discreteOrder (MkDiscreteOrderedGroup _ _ d) = d

||| forget
totalOrder : DiscreteOrderedGroupSpec _ _ _ leq _ -> TotalOrderSpec leq
totalOrder (MkDiscreteOrderedGroup p _ _) = totalOrder p

||| forget
abelian : DiscreteOrderedGroupSpec add _ _ _ _ -> isAbelian add
abelian (MkDiscreteOrderedGroup _ a _) = a


module Group

%access public export

||| Given a type and a binary operation the type of proofs that the operation is associative
total
Associative : (typ : Type) -> ((*): typ -> typ -> typ) -> Type
Associative typ (*) = (a : typ) -> (b : typ) -> (c : typ) -> ((a * b) * c) = (a * (b * c))

||| Given a type and a binary operation the type of proofs that the operation is commutative
total
Commutative : (typ : Type) -> ((*) : typ -> typ -> typ) -> Type
Commutative typ (*) = (a : typ) -> (b : typ) -> (a * b) = (b * a)

||| Given a type and a binary operation the type of proofs that identity exists
total
IdentityExists : (typ : Type) -> ((*) : typ -> typ -> typ) -> Type
IdentityExists typ (*) = (e : typ ** (a : typ ** ((a*e) = a, (e*a) = a)))

||| Given a type and a binary operation the type of proofs that each element has its inverse
total
InverseExists : (typ : Type) -> ((*) : typ -> typ -> typ) -> Type
InverseExists typ (*) = (pfid : (IdentityExists typ (*)) ** ((a : typ) -> (a_inv : typ ** ((a*a_inv = fst(pfid)),(a_inv*a = fst(pfid))))))

--||| Given a type and a binary operation the type of proofs that the type along with the
||| operation is a group
total
IsGroup : (grp : Type) -> ((*) : grp -> grp -> grp) -> Type
IsGroup grp (*) = (Associative grp (*), (IdentityExists grp (*), InverseExists grp (*)))

-- Generates inverses
Inv: (grp : Type) -> ((*) : grp -> grp -> grp) -> IsGroup grp (*) -> (x: grp) -> grp
Inv grp (*) y x = fst(snd(snd(snd(y))) x)

-- Given a group, the type of proofs that it is abelian
total
IsAbelian:  (grp : Type) -> ((*) : grp -> grp -> grp) -> IsGroup grp (*) -> Type
IsAbelian grp (*) x = (a:grp) -> (b:grp) -> (a*b = b*a)

-- The type of proofs that a given function f between x and y is injective
total
Inj: (x: Type) -> (y: Type) -> (f: x-> y) -> Type
Inj x y f = ((a: x) -> (b: x) -> (f a = f b) -> a = b)

-- The type of proofs that a function between groups is a homomorphism
total
Hom: (grp : Type) -> ((*) : grp -> grp -> grp) -> (g : Type) -> ((***) : g -> g -> g) -> (f: grp -> g) -> Type
Hom grp (*) g (+) f = (a: grp) -> (b: grp) -> ((f (a*b)) = ((f a)+(f b)))

-- The type of proofs that a given group is a subgroup of another, via injective homorphisms
total
Subgroup: (h: Type) -> ((+) : h -> h -> h) -> (IsGroup h (+)) -> (g: Type) -> ((*) : g -> g -> g) -> (IsGroup g (*)) -> Type
Subgroup h (+) x g (*) y = DPair (h->g) (\f => ((Hom h (+) g (*) f), (Inj h g f)))

-- The type of proofs that a given subgroup is self-conjugate
total
NSub: (h: Type) -> ((+) : h -> h -> h) -> (x: IsGroup h (+)) -> (g: Type) -> ((*) : g -> g -> g) -> (y: IsGroup g (*)) -> (Subgroup h (+) x g (*) y) -> Type
NSub h (+) x g (*) y z = (a: h) -> (b: g) -> (x: h ** (b*(f a)*(inv b) = (f x))) where
  f = fst(z)
  inv = Inv g (*) y

{-
-- The type generated by the image of a function
data Imgfn: (x: Type) -> (y: Type) -> (f: x -> y) -> Type where
  Gen: (q: y) -> ((p: x ** (f(p) = q)) -> Unit) -> Imgfn x y f

-- Generates the coset over h of a given element in g
Coset: (h: Type) -> ((+) : h -> h -> h) -> (pf1: IsGroup h (+)) -> (g: Type) -> ((*) : g -> g -> g) -> (pf2: IsGroup g (*)) -> (Subgroup h (+) pf1 g (*) pf2) -> (a: g) -> Type
Coset h (+) pf1 g (*) pf2 sbgrp a =  Imgfn h g (\r => a*(fst(sbgrp) r))

-- Embedding the generated Imgfn in the image type
Imgembed: (x: Type) -> (y: Type) -> (f: x -> y) -> (Imgfn x y f) -> y
Imgembed x y f (Gen q pf) = q

-- Embedding the coset in g
Cosetembed: (h: Type) -> ((+) : h -> h -> h) -> (pf1: IsGroup h (+)) -> (g: Type) -> ((*) : g -> g -> g) -> (pf2: IsGroup g (*)) -> (sbgrp: Subgroup h (+) pf1 g (*) pf2) -> (a: g) -> (Coset h (+) pf1 g (*) pf2 sbgrp a) -> g
Cosetembed h (+) pf1 g (*) pf2 sbgrp a (Gen q pf) = q
-}


module Pow

namespace Preloaded
  %access public export
  %default total

  ||| Divides a natural number by 2 and returns
  ||| the quotient and the remainder as a boolean value:
  ||| True = remainder is 1, False = remainder is 0.
  divMod2 : Nat -> (Nat, Bool)
  divMod2 Z     = (Z, False)
  divMod2 (S Z) = (Z, True)
  divMod2 (S (S n)) = case divMod2 n of (q, r) => (S q, r)

  -- The first argument (k) helps Idris to prove
  -- that the function terminates.
  powSqrAux : Nat -> Nat -> Nat -> Nat
  powSqrAux Z _ _ = 1
  powSqrAux _ _ Z = 1
  powSqrAux (S k) b e =
      case divMod2 e of
          (e', False) => powSqrAux k (b * b) e'
          (e', True) => b * powSqrAux k (b * b) e'

  powSqr : Nat -> Nat -> Nat
  powSqr b e = powSqrAux e b e

%access export
%default total

-- The following lemma is useful
divMod2Lemma : (n : Nat) -> n = 2 * fst (divMod2 n) + if snd (divMod2 n) then 1 else 0
divMod2Lemma Z     = Refl
divMod2Lemma (S Z) = Refl
divMod2Lemma (S (S k)) with (divMod2 k) proof eq
    | (q, _) = rewrite divMod2Lemma k in
               rewrite sym eq in
               rewrite sym $ plusSuccRightSucc q (q + 0) in
               Refl

powEq : (b, e : Nat) -> powSqr b e = power b e
powEq b e with (divMod2 e) proof eq
  | (e', True)  = ?proof_rhs_1
  | (e', False) = rewrite divMod2Lemma e in
                  rewrite sym eq in
                  rewrite plusZeroRightNeutral e' in
                  rewrite plusZeroRightNeutral $ e'+e' in
                  ?proof_rhs_2


module Extra.Nat

import Data.Fin
import Data.Nat
import Data.Nat.Equational
import Data.Nat.Division

||| Subtract a `Nat` by a `Fin`
public export
subtract : (n : Nat) -> Fin (S n) -> Nat
subtract n FZ = n
subtract (S n) (FS k) = subtract n k

||| `Data.Nat.lteRefl` but different symbols
public export
lteAsymRefl : {a, b : Nat} -> a = b -> LTE a b
lteAsymRefl Refl = lteRefl

||| Proof of forall a, b where b /= 0, a/b <= a
public export
divNatNZLteNumer : (numer : Nat) -> (denom : Nat) -> (prf : Not (denom = 0)) -> LTE (divNatNZ numer denom prf) numer
divNatNZLteNumer a Z prf = void $ prf Refl
divNatNZLteNumer a (S b) prf =
  let lte1 = lteAddRight {m = (snd (divmodNatNZ a (S b) prf))} (mult (fst (divmodNatNZ a (S b) prf)) (S b)) in
  let a1 = lteAsymRefl $ sym (DivisionTheoremDivMod a (S b) prf) in
  let a2 = lteTransitive lte1 (rewrite plusCommutative (mult (fst (divmodNatNZ a (S b) prf)) (S b)) (snd (divmodNatNZ a (S b) prf)) in a1) in
  let lte2 = leftFactorLteProduct (fst (divmodNatNZ a (S b) prf)) b in
  let a3 = lteTransitive lte2 a2 in
  rewrite sym $ fstDivmodNatNZeqDiv a (S b) prf prf in a3


||| THIS WORKS
data MyNat = A | Next MyNat

cycABC: MyNat -> MyNat
cycABC A                    = Next A
cycABC (Next A)             = Next (Next A)
cycABC (Next(Next A))       = A
cycABC (Next(Next(Next x))) = Next(Next(Next x))

cycACB: MyNat -> MyNat
cycACB A                    = Next(Next A)
cycACB (Next A)             = A
cycACB (Next(Next A))       = Next A
cycACB (Next(Next(Next x))) = Next(Next(Next x))

invPrfMyNat : (x:MyNat) -> cycABC ( cycACB x ) = x
invPrfMyNat A = Refl

||| THIS WORKS
cyc012: Nat -> Nat
cyc012 Z                    = S Z
cyc012 (S Z)                = S (S Z)
cyc012 (S(S Z))             = Z
cyc012 (S(S(S x)))          = S(S(S x))

cyc021: Nat -> Nat
cyc021 Z                    = S(S Z)
cyc021 (S Z)                = Z
cyc021 (S(S Z))             = S Z
cyc021 (S(S(S x)))          = S(S(S x))

invPrfNat : (x:Nat) -> cyc012 ( cyc021 x ) = x
invPrfNat Z = Refl

||| Map between

abc120: MyNat -> Nat
abc120 A                    = S Z
abc120 (Next A)             = S (S Z)
abc120 (Next(Next A))       = Z
abc120 (Next(Next(Next x))) = S(S(S (abc120 x)))

iabc120: Nat -> MyNat
iabc120 Z                    = Next(Next A)
iabc120 (S Z)                = A
iabc120 (S(S Z))             = Next A
iabc120 (S(S(S x)))          = Next(Next(Next (iabc120 x)))

invPrfNat : (x:MyNat) -> iabc120 ( abc120 x ) = x
invPrfNat A = Refl

||| THIS DOES WORK, how unexpected.
cyc01: Nat -> Nat
cyc01 Z                    = S Z
cyc01 (S Z)                = Z
cyc01 (S(S x))             = S(S(x))

invPrfNat2 : (x:Nat) -> cyc01 ( cyc01 x ) = x
invPrfNat2 Z = Refl


data Vect : Nat -> Type -> Type where
  Nil  : Vect 0 a
  (::) : a -> Vect n a -> Vect (S n) a

-- Recursion works fine:
--exactLength Z [] = Just []
--exactLength Z (x :: y) = Nothing
--exactLength (S k) [] = Nothing
--exactLength (S k) (x :: y) = map (x ::) $ exactLength k y

data EqNat : Nat -> Nat -> Type where
  Same : (a : Nat) -> EqNat a a

natEqSucc : EqNat k j -> EqNat (S k) (S j)
natEqSucc (Same k) = Same (S k)

checkEqNat : (a : Nat) -> (b : Nat) -> Maybe (EqNat a b)
checkEqNat Z Z = Just $ Same Z
checkEqNat Z (S k) = Nothing
checkEqNat (S k) Z = Nothing
checkEqNat (S k) (S j) = [| natEqSucc (checkEqNat k j) |]

replaceLength : EqNat m n -> Vect m a -> Vect n a
replaceLength (Same m) y = y

exactLength : (len : Nat) -> Vect n Nat -> Maybe (Vect len Nat)
exactLength {n} len v = [| replaceLength (checkEqNat n len) (pure v) |]

checkEqNat' : (a : Nat) -> (b : Nat) -> Maybe (a = b)
checkEqNat' Z Z = Just Refl
checkEqNat' Z (S k) = Nothing
checkEqNat' (S k) Z = Nothing
checkEqNat' (S k) (S j) = [| cong $ checkEqNat' k j |]

sameCons : {xs : List a} -> {ys : List a} -> xs = ys -> x :: xs = x :: ys
sameCons Refl = Refl

sameLists : {xs : List a} -> {ys : List a} -> x = y -> xs = ys -> x :: xs = y :: ys
sameLists Refl Refl = Refl

data ThreeEq : a -> b -> c -> Type where
  ThreeSame : ThreeEq a a a

allSameS : (x, y, z : Nat) -> ThreeEq x y z -> ThreeEq (S x) (S y) (S z)
allSameS z z z ThreeSame = ThreeSame

--append : (x : a) -> Vect n a -> Vect (S n) a
--append x [] = [x]
--append x (y :: z) = y :: append x z


module Finite.idr

import Data.Fin

--- This is the module for the Finite sets

||| The Data type finite. It is the same as Fin except the size has been made explicit
data Finite : Nat -> Type where
    FinZ : (k : Nat) -> (Finite (S k))
    FinS : (k : Nat) -> (Finite k) -> (Finite (S k))

total
Finite_to_Fin : (k : Nat) -> (Finite k) -> (Fin k)
Finite_to_Fin Z a impossible
Finite_to_Fin (S k) (FinZ k) = FZ
Finite_to_Fin (S k) (FinS k nm) = FS (Finite_to_Fin k nm)

total
Fin_to_Finite : (k : Nat) -> (Fin k) -> (Finite k)
Fin_to_Finite Z a impossible
Fin_to_Finite (S k) FZ = FinZ k
Fin_to_Finite (S k) (FS l) = FinS k (Fin_to_Finite k l)

||| Predecessor function for finite
total
predFinite : (k : Nat) -> (Finite k) -> (Finite k)
predFinite Z a impossible
predFinite (S k) (FinZ k) = FinZ k
predFinite (S (S k)) (FinS (S k) (FinZ k)) = FinZ (S k)
predFinite (S (S k)) (FinS (S k) l) = FinS (S k) (predFinite (S k) l)

||| Predecessor function for fin
total
predFin : (n : Nat) -> (Fin n) -> (Fin n)
predFin n a = Finite_to_Fin n ( predFinite n (Fin_to_Finite n a))


module Main

import Control.Monad.Identity
import Control.Monad.Trans

import Data.Nat
import Data.String.Parser
import Data.String.Parser.Expression

%default partial

table : OperatorTable Nat
table =
  [ [Infix (do token "^"; pure (power) ) AssocRight]
  , [ Infix (do token "*"; pure (*) ) AssocLeft ]
  , [ Infix (do token "+"; pure (+) ) AssocLeft ]
  ]

table' : OperatorTable Integer
table' =
  [ [ Infix (do token "*"; pure (*) ) AssocLeft
    , Infix (do token "/"; pure (div) ) AssocLeft
    ]
  , [ Infix (do token "+"; pure (+) ) AssocLeft
    , Infix (do token "-"; pure (-) ) AssocLeft
    ]
  ]

mutual
  term : Parser Nat
  term = (natural <|> expr) <* spaces
         <?> "simple expression"

  expr : Parser Nat
  expr = buildExpressionParser Nat table term

mutual
  term' : Parser Integer
  term' = (integer <|> expr') <* spaces
         <?> "simple expression"

  expr' : Parser Integer
  expr' = buildExpressionParser Integer table' term'

showRes : Show a => Either String (a, Int) -> IO ()
showRes res = case res of
                Left err => putStrLn err
                Right (xs, rem) => printLn xs

main : IO ()
main = do showRes (parse natural "5678")
          showRes (parse integer "-3")
          showRes (parse expr "1+4^3^2^1")
          showRes (parse expr' "4 + 2 * 3")
          showRes (parse expr' "13-3+1*2-10/2")


> module Fraction.Normalize

> import Fraction.Fraction
> import Fraction.BasicOperations
> import PNat.PNat
> import PNat.Operations
> import PNat.Properties
> import Nat.Positive
> import Nat.GCD
> import Nat.GCDOperations
> import Nat.Divisor
> import Nat.DivisorOperations
> import Nat.DivisorProperties
> import Nat.GCDAlgorithm

> %default total
> %access public export

> ||| Reduction to normal form via gcdAlg (NatGCDAlgorithm)
> normalize : Fraction -> Fraction
> normalize (m, d') = (qmg, fromNat qdg zLTqdg) where
>   d       :  Nat
>   d       =  toNat d'
>   g       :  Nat
>   g       =  gcdAlg m d
>   prf     :  (GCD g m d)
>   prf     =  gcdAlgLemma m d
>   gDm     :  (g `Divisor` m)
>   gDm     =  gcdDivisorFst prf
>   gDd     :  (g `Divisor` d)
>   gDd     =  gcdDivisorSnd prf
>   qmg     :  Nat
>   qmg     =  quotient m g gDm
>   qdg     :  Nat
>   qdg     =  quotient d g gDd
>   zLTd    :  (Z `LT` d)
>   zLTd    =  toNatLTLemma d'
>   zLTqdg  :  (Z `LT` qdg)
>   zLTqdg  =  quotientPreservesPositivity d g gDd zLTd
> -- %freeze normalize

> {-
> ||| Reduction to normal form via gcdAlg (NatGCDAlgorithm)
> normalize : Fraction -> Fraction
> normalize (m, d') =
>   let d       :  Nat
>               =  toNat d' in
>   let g       :  Nat
>               =  gcdAlg m d in
>   let prf     :  (GCD g m d)
>               =  gcdAlgLemma m d in
>   let gDm     :  (g `Divisor` m)
>               =  gcdDivisorFst prf in
>   let gDd     :  (g `Divisor` d)
>               =  gcdDivisorSnd prf in
>   let qmg     :  Nat
>               =  quotient m g gDm in
>   let qdg     :  Nat
>               =  quotient d g gDd in
>   let zLTd    :  (Z `LT` d)
>               =  toNatLTLemma d' in
>   let zLTqdg  :  (Z `LT` qdg)
>               =  quotientPreservesPositivity d g gDd zLTd in
>   (qmg, fromNat qdg zLTqdg)
> -- %freeze normalize
> ---}


import Decidable.Equality

data Vect : Nat -> Type -> Type where
     Nil  : Vect Z a
     (::) : a -> Vect k a -> Vect (S k) a

%name Vect xs, ys, zs

data Elem : a -> Vect k a -> Type where
     Here : Elem x (x :: xs)
     There : (later : Elem x xs) -> Elem x (y :: xs)

not_in_nil : Elem value [] -> Void
not_in_nil Here impossible
not_in_nil (There _) impossible

not_in_tail : (notThere : Elem value xs -> Void) -> (notHere : (value = x) -> Void) -> Elem value (x :: xs) -> Void
not_in_tail notThere notHere Here = notHere Refl
not_in_tail notThere notHere (There later) = notThere later

isElem : DecEq a => (value : a) -> (xs : Vect n a) -> Dec (Elem value xs)
isElem value [] = No not_in_nil
isElem value (x :: xs) = case decEq value x of
                              Yes Refl => Yes Here
                              No notHere => case isElem value xs of
                                                 Yes prf => Yes (There prf)
                                                 No notThere => No (not_in_tail notThere notHere)


module Part2.Sez10_2e
import Data.List.Views
import Data.Nat.Views

%default total

{- some excrcises from sec 10.2 -}

{- equalSuffix excercise, should convert to Haskell (using slower snocList from Sez10_2a_snoc) -}
equalSuffixHelper : Eq a => List a -> List a -> List a
equalSuffixHelper input1 input2 with (snocList input1)
    equalSuffixHelper [] input2 | Empty = []
    equalSuffixHelper (xs ++ [x]) input2 | (Snoc rec1) with (snocList input2)
        equalSuffixHelper (xs ++ [x]) [] | (Snoc rec1) | Empty = []
        equalSuffixHelper (xs ++ [x]) (ys ++ [y]) | (Snoc rec1) | (Snoc rec2)
              = if x == y then x :: equalSuffixHelper xs ys | rec1 | rec2 else []

equalSuffix : Eq a => List a -> List a -> List a
equalSuffix xs ys = reverse (equalSuffixHelper xs ys)

{-
idris repl
*Part2/Sez10_2e> equalSuffix [1,2,4,5] [1..5]
[4, 5] : List Integer
-}

{- other excercises use even more advanced views, I expect harder time to create
performant Haskell version -}

{- toBinary
uses

data HalfRec : Nat -> Type where
     HalfRecZ : HalfRec Z
     HalfRecEven : {n : Nat} -> (rec : Lazy (HalfRec n)) -> HalfRec (n + n)
     HalfRecOdd : {n : Nat} -> (rec : Lazy (HalfRec n)) -> HalfRec (S (n + n))
-}
-- TODO the following compiles and prints out gibberish  when executed
-- toBinary' : Nat -> String
-- toBinary' n with (halfRec n)
--   toBinary' Z | HalfRecZ = "0"
--   toBinary' (x + x) |  HalfRecEven rec = (toBinary' x | rec) ++ "0"
--   toBinary' (S (x + x)) | HalfRecOdd rec = if x == Z then "1" else (toBinary' x | rec) ++ "1"

toBinary : (n : Nat) -> String
toBinary Z = "0"
toBinary k = go k []
  where
    go : (n : Nat) -> (acc : List Char) -> String
    go n acc with (halfRec n)
      go Z acc | HalfRecZ = pack acc
      go (x + x) acc | (HalfRecEven rec) = go x ('0' :: acc) | rec
      go (S (x + x)) acc | (HalfRecOdd rec) = go x ('1' :: acc) | rec

{-
*Part2/Sez10_2e> toBinary 42
"101010" : String
-}

{- palindrome, I expect linear cost vList will be hard to implement -}
palindrome : Eq a => List a -> Bool
palindrome xs with (vList xs)
   palindrome [] | VNil = True
   palindrome [x] | VOne = True
   palindrome (x :: xs ++ [y]) | VCons rec
       = if x == y then palindrome xs | rec else False

{-
idris repl
*Part2/Sez10_2e> :l Part2/Sez10_2e
Type checking ./Part2/Sez10_2e.idr
*Part2/Sez10_2e> palindrome (unpack "abccba")
True : Bool
*Part2/Sez10_2e> palindrome (unpack "abcba")
True : Bool
*Part2/Sez10_2e> palindrome (unpack "abccb")
False : Bool
-}


module Equis.Matrix.Arithmetic

import Matrix
import Data.Matrix.Numeric

%access private

||| Hadamard multiplication (element-wise product) of two matrices.
|||
||| @x the first matrix
||| @y the second matrix
export
hadamard : Num a => (x, y : Matrix n m a) -> Matrix n m a
hadamard x y = mapMatrix (\(i, j) => i * j) (zipMatrix x y)

||| Calculates a Gramian matrix.
|||
||| @x the matrix
export
gram : Num a => (x : Matrix n m a) -> Matrix m m a
gram x = (transpose x) <> x

||| Calculates the determinant of a square matrix (including order 1 and 0 matrices).
|||
||| @x the matrix
export
determinant : Neg a => (x : Matrix n n a) -> a
determinant {n = Z} _ = 1
determinant {n = (S Z)} x = scalar x
determinant {n = (S (S _))} x = det x

||| Calculates the matrix of minors for the given square matrix.
|||
||| @x the matrix
export
minors : Neg a => (x : Matrix n n a) -> Matrix n n a
minors {n = Z} _ = []
minors {n = (S _)} x = mapMatrix determinant (minors' x) where
  ||| Generates a matrix of m - 1 × n - 1 submatrices within the given matrix.
  |||
  ||| @x the matrix
  minors' : (x : Matrix (S n) (S m) a) -> Matrix (S n) (S m) (Matrix n m a)
  minors' {n} {m} x = mapMatrix (\(i, j) => subMatrix i j x) (coords (S n) (S m))

||| Calculates the matrix of cofactors for the given square matrix.
|||
||| @x the matrix
export
cofactors : Neg a => (x : Matrix n n a) -> Matrix n n a
cofactors {n} x = hadamard (signs n n False) (minors x) where
  ||| Generates an alternating vector of +1 and -1 of length `n`
  |||
  ||| @n the length of the vector to ganerate
  ||| @b if set to false, the first element will be 1, otherwise it will be -1
  signs' : (n : Nat) -> (b : Bool) -> Vect n a
  signs' Z _ = []
  signs' (S k) False = 1 :: (signs' k True)
  signs' (S k) True = -1 :: (signs' k False)
  ||| Generates an alternating matrix of +1 and -1 of order m × n.
  |||
  ||| @n the first matrix dimension
  ||| @m the second matrix dimension
  ||| @b if set to false, the first element will be 1, otherwise it will be -1
  signs : (n : Nat) -> (m : Nat) -> (b : Bool) -> Matrix n m a
  signs Z m _ = []
  signs (S k) m False = (signs' m False) :: (signs k m True)
  signs (S k) m True = (signs' m True) :: (signs k m False)

||| Calculates the adjugate of the given square matrix.
|||
||| @x the matrix
export
adjugate : Neg a => (x : Matrix n n a) -> Matrix n n a
adjugate x = transpose (cofactors x)

||| Calculates the inverse of the given square matrix (must be of at least order 1).
|||
||| @x the matrix
export
inverse : Neg a => Fractional a => (x : Matrix n n a) -> Matrix n n a
inverse x = (1 / (determinant x)) <#> (adjugate x)

||| Calculates an inverse Gramian matrix.
|||
||| @x the matrix
export
inverseGram : Neg a => Fractional a => (x : Matrix (S n) (S m) a) -> Matrix (S m) (S m) a
inverseGram x = inverse (gram x)


||| Spec: https://webassembly.github.io/spec/core/valid/types.html#limits
module WebAssembly.Validation.Types.Limits

import WebAssembly.Structure

-------------------------------------------------------------------------------
-- Validation Rules
-------------------------------------------------------------------------------

public export
data ValidLimits : Limits -> Nat -> Type where
  ||| Proof that a limit wihtout an upper bound is valid for some given k
  |||
  |||    n <= k
  |||----------------
  ||| ⊢ {min n} : k
  |||
  MkValidLowerBound : (mm = Nothing) -> LTE n k -> ValidLimits (MkLimits n mm) k
  ||| Proof that a limit with lower and upper bound is valid for some given k
  |||
  |||   (n <= k)   (m <= k)   (n <= m)
  |||------------------------------------
  |||        ⊢ {min n, max m} : k
  |||
  MkValidLimits     : (mm = Just m)  -> LTE n k -> LTE m k -> LTE n m -> ValidLimits (MkLimits n mm) k

-------------------------------------------------------------------------------
-- Lemmas about limit-type validation
-------------------------------------------------------------------------------

||| If the lower bound of a limit without an upper bound is larger than k, then we the limit is invalid
total lower_bound_invalid_upper_bound_absent : (LTE n k -> Void) -> (ValidLimits (MkLimits n Nothing) k -> Void)
lower_bound_invalid_upper_bound_absent not_n_lte_k (MkValidLowerBound _ n_lte_k) = not_n_lte_k n_lte_k
lower_bound_invalid_upper_bound_absent not_n_lte_k (MkValidLimits _ n_lte_k _ _) = not_n_lte_k n_lte_k

||| If the lower bound of a limit with lower and upper bounds is larger than k, then we the limit is invalid
total lower_bound_invalid_upper_bound_present : (LTE n k -> Void) -> (ValidLimits (MkLimits n (Just m)) k -> Void)
lower_bound_invalid_upper_bound_present _ (MkValidLowerBound Refl _) impossible
lower_bound_invalid_upper_bound_present not_n_lte_k (MkValidLimits Refl n_lte_k _ _) = not_n_lte_k n_lte_k

||| If the upper bound of a limit is larger than k, then the limit is invalid
total upper_bound_invalid : (LTE m k -> Void) -> (ValidLimits (MkLimits n (Just m)) k -> Void)
upper_bound_invalid _ (MkValidLowerBound Refl _) impossible
upper_bound_invalid not_m_lte_k (MkValidLimits Refl _ m_lte_k _) = not_m_lte_k m_lte_k

||| If the lower bound is greater than the upper bound, then the limit is invalid
total lower_bound_greater_than_upper_bound : (LTE n m -> Void) -> (ValidLimits (MkLimits n (Just m)) k -> Void)
lower_bound_greater_than_upper_bound _ (MkValidLowerBound Refl _) impossible
lower_bound_greater_than_upper_bound not_n_lte_m (MkValidLimits Refl _ _ n_lte_m) = not_n_lte_m n_lte_m

-------------------------------------------------------------------------------
-- Decidablity of Validation
-------------------------------------------------------------------------------

||| Decide whether a given limit is valid for some given k
total public export
isLimitsValid : (limits : Limits) -> (k : Nat) -> Dec (ValidLimits limits k)
isLimitsValid (MkLimits n Nothing) k =
  case isLTE n k of
    No  contra => No (lower_bound_invalid_upper_bound_absent contra)
    Yes prof   => Yes (MkValidLowerBound Refl prof)
isLimitsValid (MkLimits n (Just m)) k =
  case isLTE n k of
    No  contra => No (lower_bound_invalid_upper_bound_present contra)
    Yes n_lte_k   => case isLTE m k of
      No contra => No (upper_bound_invalid contra)
      Yes m_lte_k  => case isLTE n m of
        No contra => No (lower_bound_greater_than_upper_bound contra)
        Yes n_lte_m  => Yes (MkValidLimits Refl n_lte_k m_lte_k n_lte_m)


||| Basic matrix operations with dimensionalities enforced
||| at the type level
module Data.Matrix

import public Data.Vect

%default total

||| Matrix with n rows and m columns
Matrix : Nat -> Nat -> Type -> Type
Matrix n m a = Vect n (Vect m a)

||| Get the specified column of a matrix
getCol : Fin m -> Matrix n m a -> Vect n a
getCol f = map (index f)

||| Get the specified row of a matrix
getRow : Fin n -> Matrix n m a -> Vect m a
getRow = index

||| Delete the specified column of a matrix
deleteCol : Fin (S m) -> Matrix n (S m) a -> Matrix n m a
deleteCol f = map (deleteAt f)

||| Delete the specified row of a matrix
deleteRow : Fin (S n) -> Matrix (S n) m a -> Matrix n m a
deleteRow = deleteAt

insertRow : Fin (S n) -> Vect m a -> Matrix n m a -> Matrix (S n) m a
insertRow = insertAt

insertCol : Fin (S m) -> Vect n a -> Matrix n m a -> Matrix n (S m) a
insertCol f = zipWith (insertAt f)

||| Matrix element at specified row and column indices
indices : Fin n -> Fin m -> Matrix n m a -> a
indices f1 f2 = index f2 . index f1

||| Cast a vector from a standard Vect to a proper n x 1 matrix
col : Vect n a -> Matrix n 1 a
col = map (\x => [x])

||| Cast a row from a standard Vect to a proper 1 x n matrix
row : Vect n a -> Matrix 1 n a
row r = [r]

||| Matrix formed by deleting specified row and col
subMatrix : Fin (S n) -> Fin (S m) -> Matrix (S n) (S m) a -> Matrix n m a
subMatrix r c = deleteRow r . deleteCol c

||| Flatten a matrix of matrices
concatMatrix : Matrix n m (Matrix x y a) -> Matrix (n * x) (m * y) a
concatMatrix = Vect.concat . map (map Vect.concat) . map transpose

||| All finite numbers of the specified level
fins : (n : Nat) -> Vect n (Fin n)
fins Z     = Nil
fins (S n) = FZ :: (map FS $ fins n)


data Shape = Triangle Double Double
           | Rectangle Double Double
           | Circle Double

area : Shape -> Double
area (Triangle base height) = 0.5 * base * height
area (Rectangle length height) = length * height
area (Circle radius) = pi * radius * radius

Eq Shape where
  (==) (Triangle w h) (Triangle w' h') =
    w == w' && h == h'
  (==) (Rectangle w h) (Rectangle w' h') =
    w == w' && h == h'
  (==) (Circle r) (Circle r') =
    r == r'
  (==) _ _ = False

Ord Shape where
  compare first second = compare (area first) (area second)

testShapes : List Shape
testShapes = [Circle 3, Triangle 3 9, Rectangle 2 6, Circle 4, Rectangle 2 7]

data Picture = Primitive Shape
             | Combine Picture Picture
             | Rotate Double Picture
             | Translate Double Double Picture
%name Shape shape, shape1, shape2
%name Picture pic, pic1, pic2

rectangle : Picture
rectangle = Primitive (Rectangle 20 10)
circle : Picture
circle = Primitive (Circle 5)
triangle : Picture
triangle = Primitive (Triangle 10 10)
triangle2 : Picture
triangle2 = Primitive (Triangle 100 10)

test_picture : Picture
test_picture = Combine (Combine (Translate 5 5 rectangle)
                  (Combine (Translate 35 5 circle) (Translate 15 25 triangle))) (Translate 20 20 triangle2)

picture_area : Picture -> Double
picture_area (Primitive shape) = area shape
picture_area (Combine pic1 pic2) = picture_area pic1 + picture_area pic2
picture_area (Rotate x pic) = picture_area pic
picture_area (Translate x y pic) = picture_area pic

safe_divide : Double -> Double -> Maybe Double
safe_divide x 0.0 = Nothing
safe_divide x y = Just (x / y)

maxMaybe : Ord a => Maybe a -> Maybe a -> Maybe a
maxMaybe Nothing y = y
maxMaybe x Nothing = x
maxMaybe (Just x) (Just y) = case compare x y of
                                  LT => Just y
                                  EQ => Just x
                                  GT => Just x

biggestTriangle_helper : (p : Picture) -> (size : Maybe Double) -> Maybe Double
biggestTriangle_helper (Primitive tri @ (Triangle x y)) Nothing = Just (area tri)
biggestTriangle_helper (Primitive tri @ (Triangle x y)) right_size @ (Just z) = let left_size = Just (area tri) in
                                                                                maxMaybe left_size right_size
biggestTriangle_helper (Primitive (Rectangle x y)) size = size
biggestTriangle_helper (Primitive (Circle x)) size = size
biggestTriangle_helper (Combine left right) size = let left_size = biggestTriangle_helper left size
                                                       right_size = biggestTriangle_helper right size in
                                                       maxMaybe (maxMaybe left_size right_size) size
biggestTriangle_helper (Rotate x pic) size = biggestTriangle_helper pic size
biggestTriangle_helper (Translate x y pic) size = biggestTriangle_helper pic size

biggestTriangle : Picture -> Maybe Double
biggestTriangle p = biggestTriangle_helper p Nothing


= IndPrinciples : Induction Principles

> module IndPrinciples
>
> import IndProp
>
> %access public export
> %default total
>

With the Curry-Howard correspondence and its realization in Idris in mind, we
can now take a deeper look at induction principles.

\ \todo[inline]{We've written all induction principles by hand throughout the
chapter, Idris doesn't generate them. Edit the text to reflect this}

== Basics

Every time we declare a new \idr{data} type, Idris automatically generates an
_induction principle_ for this type. This induction principle is a theorem like
any other: If \idr{t} is defined inductively, the corresponding induction
principle is called \idr{t_ind}. Here is the one for natural numbers:

> nat_ind : {P : Nat -> Type} -> P Z -> ((n : Nat) -> P n -> P (S n)) ->
>           ((n : Nat) -> P n)
> nat_ind pz _ Z = pz
> nat_ind pz f (S k) = f k (nat_ind pz f k)

\todo[inline]{Mention it's no coincidence it's similar to fold}

The `induction` tactic is a straightforward wrapper that, at its core, simply
performs `apply t_ind`. To see this more clearly, let's experiment with directly
using `apply nat_ind`, instead of the `induction` tactic, to carry out some
proofs. Here, for example, is an alternate proof of a theorem that we saw in the
`Induction` chapter.

> mult_0_r' : (n : Nat) -> n * Z = Z
> mult_0_r' = nat_ind {P=\x => x*Z = Z}
>   Refl             -- n = Z
>   (\k => id)       -- n = S k

This proof is basically the same as the earlier one, but a few minor differences
are worth noting.

First, in the induction step of the proof (the "\idr{S}" case), we have to do a
little bookkeeping manually (the `intros`) that `induction` does automatically.

Second, we do not introduce \idr{n} into the context before applying
\idr{nat_ind} — the conclusion of \idr{nat_ind} is a quantified formula, and
`apply` needs this conclusion to exactly match the shape of the goal state,
including the quantifier. By contrast, the `induction` tactic works either with
a variable in the context or a quantified variable in the goal.

These conveniences make `induction` nicer to use in practice than applying
induction principles like \idr{nat_ind} directly. But it is important to realize
that, modulo these bits of bookkeeping, applying \idr{nat_ind} is what we are
really doing.

==== Exercise: 2 stars, optional (plus_one_r')

Complete this proof without using the `induction` tactic.

> plus_one_r' : (n : Nat) -> n + 1 = S n
> plus_one_r' n = ?plus_one_r__rhs

$\square$

Idris generates induction principles for every datatype defined with \idr{data},
including those that aren't recursive. Although of course we don't need
induction to prove properties of non-recursive datatypes, the idea of an
induction principle still makes sense for them: it gives a way to prove that a
property holds for all values of the type.

These generated principles follow a similar pattern. If we define a type \idr{t}
with constructors \idr{c1} ... \idr{cn}, Idris generates a theorem with this
shape:

```idris
t_ind : {P : t -> Type},
          ... case for c1 ... ->
          ... case for c2 ... -> ...
          ... case for cn ... ->
        (n : t) -> P n
```

The specific shape of each case depends on the arguments to the corresponding
constructor. Before trying to write down a general rule, let's look at some more
examples. First, an example where the constructors take no arguments:

> data YesNo = Yes' | No'

> yesno_ind : {P : YesNo -> Type} -> P Yes' -> P No' ->
>             ((y : YesNo) -> P y)
> yesno_ind px _ Yes' = px
> yesno_ind _ py No'  = py

==== Exercise: 1 star, optional (rgb)

Write out the induction principle that Idris will generate for the following
datatype. Write down your answer on paper or type it into a comment, and then
compare it with what Idris prints.

> data RGB = Red | Green | Blue

> rgb_ind : ?rgb_ind

$\square$

Here's another example, this time with one of the constructors taking some
arguments.

> data NatList : Type where
>   NNil : NatList
>   NCons : Nat -> NatList -> NatList

> natlist_ind : {P : NatList -> Type} -> P NNil ->
>               ((n : Nat) -> (l : NatList) -> P l -> P (NCons n l)) ->
>               ((l : NatList) -> P l)
> natlist_ind pn _ NNil = pn
> natlist_ind pn f (NCons k x) = f k x (natlist_ind pn f x)

==== Exercise: 1 star, optional (natlist1)

Suppose we had written the above definition a little differently:

> data NatList1 : Type where
>   NNil1 : NatList1
>   NCons1 : NatList1 -> Nat -> NatList1

Now what will the induction principle look like? $\square$

From these examples, we can extract this general rule:

  - The type declaration gives several constructors; each corresponds to one
    clause of the induction principle.

  - Each constructor \idr{c} takes argument types \idr{a1} ... \idr{an}.

  - Each \idr{ai} can be either \idr{t} (the datatype we are defining) or some
    other type \idr{s}.

  - The corresponding case of the induction principle says:

    - "For all values \idr{x1}...\idr{xn} of types \idr{a1}...\idr{an}, if
      \idr{P} holds for each of the inductive arguments (each \idr{xi} of type
      \idr{t}), then \idr{P} holds for \idr{c x1 ... xn}".

==== Exercise: 1 star, optional (byntree_ind)

Write out the induction principle that Idris will generate for the following
datatype. (Again, write down your answer on paper or type it into a comment, and
then compare it with what Idris prints.)

> data Byntree : Type where
>  Bempty : Byntree
>  Bleaf : YesNo -> Byntree
>  Nbranch : YesNo -> Byntree -> Byntree -> Byntree

$\square$

==== Exercise: 1 star, optional (ex_set)

Here is an induction principle for an inductively defined set.

```idris
ExSet_ind : {P : ExSet -> Type} -> ((b : Bool) -> P (Con1 b)) ->
           ((n : Nat) -> (e : ExSet) -> P e -> P (Con2 n e)) ->
           ((e : ExSet) -> P e)
```

Give an \idr{data} definition of \idr{ExSet}:

> -- data ExSet : Type where
> -- FILL IN HERE

$\square$

== Polymorphism

Next, what about polymorphic datatypes?

The inductive definition of polymorphic lists

```idris
data List : (x : Type) -> Type where
  Nil : List x
  Cons : x -> List x -> List x
```

is very similar to that of \idr{NatList}. The main difference is that, here, the
whole definition is _parameterized_ on a set \idr{x}: that is, we are defining a
family of inductive types \idr{List x}, one for each \idr{x}. (Note that,
wherever \idr{List} appears in the body of the declaration, it is always applied
to the parameter \idr{x}.) The induction principle is likewise parameterized on
\idr{x}:

> list_ind : {x : Type} -> {P : List x -> Type} -> P [] ->
>            ((a : x) -> (l : List x) -> P l -> P (a :: l)) ->
>            ((l : List x) -> P l)
> list_ind pn _ [] = pn
> list_ind pn f (h::t) = f h t (list_ind pn f t)

Note that the whole induction principle is parameterized on \idr{x}. That is,
\idr{list_ind} can be thought of as a polymorphic function that, when applied to
a type \idr{x}, gives us back an induction principle specialized to the type
\idr{List x}.

==== Exercise: 1 star, optional (tree)

Write out the induction principle that Idris will generate for the following
datatype. Compare your answer with what Idris prints.

> data Tree : (x : Type) -> Type where
>   Leaf : x -> Tree x
>   Node : Tree x -> Tree x -> Tree x

> tree_ind : ?tree_ind

$\square$

==== Exercise: 1 star, optional (mytype)

Find an inductive definition that gives rise to the following induction
principle:

```idris
mytype_ind : {x : Type} -> {P : MyType x -> Type} ->
             ((a : x) -> P (Constr1 a)) ->
             ((n : Nat) -> P (Constr2 n)) ->
             ((m : MyType x) -> P m -> (n : Nat) -> P (Constr3 m n)) ->
             ((m : MyType x) -> P m)
```

$\square$

==== Exercise: 1 star, optional (foo)

Find an inductive definition that gives rise to the following induction
principle:

```idris
foo_ind : {x, y : Type} -> {P : Foo x y -> Type} ->
       ((a : x) -> P (Bar a)) ->
       ((b : y) -> P (Baz b)) ->
       ((f1 : Nat -> Foo x y) -> ((n : Nat) -> P (f1 n)) -> P (Quux f1)) ->
       ((f2 : Foo x y) -> P f2)
```

$\square$

==== Exercise: 1 star, optional (foo')

Consider the following inductive definition:

> data Foo' : (x : Type) -> Type where
>   C1 : List x -> Foo' x -> Foo' x
>   C2 : Foo' x

What induction principle will Idris generate for \idr{Foo'}? Fill in the blanks,
then check your answer with Idris.)

```idris
foo'_ind : {x : Type} -> {P : Foo' x -> Type} ->
           ((l : List x) -> (f : Foo' x) -> ?hole1 -> ?hole2) ->
           ?hole3 ->
           (f : Foo' x -> ?hole4)
```

$\square$

== Induction Hypotheses

Where does the phrase "induction hypothesis" fit into this story?

The induction principle for numbers

```idris
nat_ind : {P : Nat -> Type} -> P Z -> ((n : Nat) -> P n -> P (S n)) ->
          ((n : Nat) -> P n)
``

is a generic statement that holds for all propositions \idr{P} (or rather,
strictly speaking, for all families of propositions \idr{P} indexed by a number
\idr{n}). Each time we use this principle, we are choosing \idr{P} to be a
particular expression of type \idr{Nat -> Type}.

We can make proofs by induction more explicit by giving this expression a name.
For example, instead of stating the theorem \idr{mult_0_r} as
"\idr{(n : Nat) -> n * 0 = 0}," we can write it as "\idr{(n : Nat) -> P_m0r n}",
where \idr{P_m0r} is defined as...

> P_m0r : (n : Nat) -> Type
> P_m0r n = n * Z = Z

... or equivalently:

> P_m0r' : Nat -> Type
> P_m0r' = \n => n * Z = Z

Now it is easier to see where \idr{P_m0r} appears in the proof.

> mult_0_r'' : (n: Nat) -> P_m0r n
> mult_0_r'' = nat_ind {P=P_m0r}
>   Refl             -- n = Z
>   (\n => id)       -- n = S k

This extra naming step isn't something that we do in normal proofs, but it is
useful to do it explicitly for an example or two, because it allows us to see
exactly what the induction hypothesis is. If we prove \idr{(n : Nat) -> P_m0r n}
by induction on \idr{n} (using either `induction` or `apply nat_ind`), we see
that the first subgoal requires us to prove \idr{P_m0r 0} ("\idr{P} holds for
zero"), while the second subgoal requires us to prove
\idr{(n' : Nat) -> P_m0r n' -> P_m0r (S n')} (that is "\idr{P} holds of
\idr{S n'} if it holds of \idr{n'}" or, more elegantly, "\idr{P} is preserved by
\idr{S}"). The _induction hypothesis_ is the premise of this latter implication
— the assumption that \idr{P} holds of \idr{n'}, which we are allowed to use in
proving that \idr{P} holds for \idr{S n'}.

== More on the `induction` Tactic

The `induction` tactic actually does even more low-level bookkeeping for us than
we discussed above.

Recall the informal statement of the induction principle for natural numbers:

  - If \idr{P n} is some proposition involving a natural number \idr{n}, and we
    want to show that \idr{P} holds for all numbers \idr{n}, we can reason like
    this:

    - show that \idr{P Z} holds

    - show that, if \idr{P n'} holds, then so does \idr{P (S n')}

    - conclude that \idr{P n} holds for all \idr{n}.

So, when we begin a proof with `intros n` and then `induction n`, we are first
telling Idris to consider a _particular_ `n` (by introducing it into the
context) and then telling it to prove something about _all_ numbers (by using
induction).

What Idris actually does in this situation, internally, is to "re-generalize"
the variable we perform induction on. For example, in our original proof that
\idr{plus} is associative...

```coq
Theorem plus_assoc' : forall n m p : Nat,
  n + (m + p) = (n + m) + p.
Proof.
  (* ...we first introduce all 3 variables into the context,
     which amounts to saying "Consider an arbitrary n, m, and
     p..." *)
  intros n m p.
  (* ...We now use the induction tactic to prove P n (that
     is, n + (m + p) = (n + m) + p) for _all_ n,
     and hence also for the particular n that is in the context
     at the moment. *)
  induction n as [| n'].
  - (* n = O *) Refl.
  - (* n = S n' *)
    (* In the second subgoal generated by induction -- the
       "inductive step" -- we must prove that P n' implies
       P (S n') for all n'.  The induction tactic
       automatically introduces n' and P n' into the context
       for us, leaving just P (S n') as the goal. *)
    simpl. rewrite -> IHn'. Refl. Qed.
```

It also works to apply `induction` to a variable that is quantified in the goal.

```coq
Theorem plus_comm' : forall n m : Nat,
  n + m = m + n.
Proof.
  induction n as [| n'].
  - (* n = O *) intros m. rewrite <- plus_n_O. Refl.
  - (* n = S n' *) intros m. simpl. rewrite -> IHn'.
    rewrite <- plus_n_Sm. Refl. Qed.
```

Note that `induction n` leaves `m` still bound in the goal — i.e., what we are
proving inductively is a statement beginning with `forall m`.

If we do `induction` on a variable that is quantified in the goal _after_ some
other quantifiers, the `induction` tactic will automatically introduce the
variables bound by these quantifiers into the context.

```coq
Theorem plus_comm'' : forall n m : Nat,
  n + m = m + n.
Proof.
  (* Let's do induction on m this time, instead of n... *)
  induction m as [| m'].
  - (* m = O *) simpl. rewrite <- plus_n_O. Refl.
  - (* m = S m' *) simpl. rewrite <- IHm'.
    rewrite <- plus_n_Sm. Refl. Qed.
```

==== Exercise: 1 star, optional (plus_explicit_prop)

Rewrite both \idr{plus_assoc'} and \idr{plus_comm'} and their proofs in the same
style as \idr{mult_0_r''} above — that is, for each theorem, give an explicit
definition of the proposition being proved by induction, and state the theorem
and proof in terms of this defined proposition.

> -- FILL IN HERE

$\square$

== Induction Principles in \idr{Type}

Earlier, we looked in detail at the induction principles that Idris generates
for inductively defined _sets_. The induction principles for inductively defined
_propositions_ like \idr{Ev} are a tiny bit more complicated. As with all
induction principles, we want to use the induction principle on \idr{Ev} to
prove things by inductively considering the possible shapes that something in
\idr{Ev} can have. Intuitively speaking, however, what we want to prove are not
statements about evidence but statements about _numbers_: accordingly, we want
an induction principle that lets us prove properties of numbers by induction on
evidence.

For example, from what we've said so far, you might expect the inductive
definition of \idr{Ev}...

```idris
data Ev : Nat -> Type where
  Ev_0 : Ev Z
  Ev_SS : Ev n -> Ev (S (S n))
```

...to give rise to an induction principle that looks like this...

> ev_ind_max : {P : {n : Nat} -> Ev n -> Type} ->
>        P {n=Z} Ev_0 ->
>        ((m : Nat) -> (e : Ev m) -> P {n=m} e -> P {n=S (S m)} (Ev_SS e)) ->
>        ((n : Nat) -> (e : Ev n) -> P {n} e)

... because:

  - Since \idr{Ev} is indexed by a number \idr{n} (every \idr{Ev} object \idr{e}
    is a piece of evidence that some particular number \idr{n} is even), the
    proposition \idr{P} is parameterized by both \idr{n} and \idr{e} — that is,
    the induction principle can be used to prove assertions involving both an
    even number and the evidence that it is even.

  - Since there are two ways of giving evidence of evenness (\idr{Ev} has two
    constructors), applying the induction principle generates two subgoals:

    - We must prove that \idr{P} holds for \idr{Z} and \idr{Ev_0}.

    - We must prove that, whenever \idr{n} is an even number and \idr{e} is an
      evidence of its evenness, if \idr{P} holds of \idr{n} and \idr{e}, then it
      also holds of \idr{S (S n)} and \idr{Ev_SS {n=S (S n)} e}.

  - If these subgoals can be proved, then the induction principle tells us that
    \idr{P} is true for all even numbers \idr{n} and evidence \idr{e} of their
    evenness.

This is more flexibility than we normally need or want: it is giving us a way to
prove logical assertions where the assertion involves properties of some piece
of _evidence_ of evenness, while all we really care about is proving properties
of _numbers_ that are even — we are interested in assertions about numbers, not
about evidence. It would therefore be more convenient to have an induction
principle for proving propositions \idr{P} that are parameterized just by
\idr{n} and whose conclusion establishes \idr{P} for all even numbers \idr{n}:

```idris
       {P : Nat -> Type} ->
       ... ->
       (n : Nat) ->
       Ev n -> P n
```

For this reason, Idris actually generates the following simplified induction
principle for \idr{Ev}:

> ev_ind : {P : Nat -> Type} -> P Z ->
>         ((n : Nat) -> Ev n -> P n -> P (S (S n))) ->
>         ((n : Nat) -> Ev n -> P n)
> ev_ind pz _ Z Ev_0 = pz
> ev_ind pz f (S (S k)) (Ev_SS ev) = f k ev (ev_ind pz f k ev)

In particular, Idris has dropped the evidence term \idr{e} as a parameter of the
the proposition \idr{P}.

In English, \idr{ev_ind} says:

  - Suppose, \idr{P} is a property of natural numbers (that is, \idr{P n} is a
    \idr{Type} for every \idr{n}). To show that \idr{P n} holds whenever \idr{n}
    is even, it suffices to show:

    - \idr{P} holds for \idr{Z},

    - for any \idr{n}, if \idr{n} is even and \idr{P} holds for \idr{n}, then
      \idr{P} holds for \idr{S (S n)}.

As expected, we can apply \idr{ev_ind} directly instead of using `induction`. For
example, we can use it to show that \idr{Ev'} (the slightly awkward alternate
definition of evenness that we saw in an exercise in the `IndProp` chapter)
is equivalent to the cleaner inductive definition \idr{Ev}:

> ev_ev' : Ev n -> Ev' n
> ev_ev' {n} = ev_ind {P=Ev'}
>  Ev'_0
>  (\_, _, ev' => Ev'_sum Ev'_2 ev')
>  n

The precise form of a \idr{data} definition can affect the induction principle
Idris generates.

For example, in chapter `IndProp`, we defined \idr{Le} as:

```idris
data Le : Nat -> Nat -> Type where
  Le_n : Le n n
  Le_S : Le n m -> Le n (S m)
```

This definition can be streamlined a little by observing that the left-hand
argument \idr{n} is the same everywhere in the definition, so we can actually
make it a "general parameter" to the whole definition, rather than an argument
to each constructor.

\todo[inline]{This doesn't actually seem to change anything in our case}

```idris
data Le : (n:Nat) -> Nat -> Type where
  Le_n : Le n n
  Le_S : Le n m -> Le n (S m)
```

The second one is better, even though it looks less symmetric. Why? Because it
gives us a simpler induction principle.

> le_ind : {n : Nat} -> {P : Nat -> Type} -> P n ->
>          ((m : Nat) -> (n <=' m) -> P m -> P (S m)) ->
>          ((n0 : Nat) -> (n <=' n0) -> P n0)
> le_ind {n=n0} pn _ n0 Le_n = pn
> le_ind pn f (S m) (Le_S le) = f m le (le_ind pn f m le)

== Formal vs. Informal Proofs by Induction

Question: What is the relation between a formal proof of a proposition \idr{P}
and an informal proof of the same proposition \idr{P}?

Answer: The latter should _teach_ the reader how to produce the former.

Question: How much detail is needed??

Unfortunately, there is no single right answer; rather, there is a range of
choices.

At one end of the spectrum, we can essentially give the reader the whole formal
proof (i.e., the "informal" proof will amount to just transcribing the formal
one into words). This may give the reader the ability to reproduce the formal
one for themselves, but it probably doesn't _teach_ them anything much.

At the other end of the spectrum, we can say "The theorem is true and you can
figure out why for yourself if you think about it hard enough." This is also not
a good teaching strategy, because often writing the proof requires one or more
significant insights into the thing we're proving, and most readers will give up
before they rediscover all the same insights as we did.

In the middle is the golden mean — a proof that includes all of the essential
insights (saving the reader the hard work that we went through to find the proof
in the first place) plus high-level suggestions for the more routine parts to
save the reader from spending too much time reconstructing these (e.g., what the
\idr{ih} says and what must be shown in each case of an inductive proof), but
not so much detail that the main ideas are obscured.

Since we've spent much of this chapter looking "under the hood" at formal proofs
by induction, now is a good moment to talk a little about _informal_ proofs by
induction.

\todo[inline]{Do we need 2 templates here?}

In the real world of mathematical communication, written proofs range from
extremely longwinded and pedantic to extremely brief and telegraphic. Although
the ideal is somewhere in between, while one is getting used to the style it is
better to start out at the pedantic end. Also, during the learning phase, it is
probably helpful to have a clear standard to compare against. With this in mind,
we offer two templates — one for proofs by induction over _data_ (i.e., where
the thing we're doing induction on lives in \idr{Type}) and one for proofs by
induction over _evidence_ (i.e., where the inductively defined thing lives in
\idr{Type}).

=== Induction Over an Inductively Defined Set

_Template_:

  - _Theorem_: <Universally quantified proposition of the form "For all
    \idr{n:s, P(n)}," where \idr{s} is some inductively defined set.>

    _Proof_: By induction on \idr{n}.

    <one case for each constructor \idr{c} of \idr{s}...>

    - Suppose \idr{n = c a1 ... ak}, where <...and here we state the \idr{IH}
      for each of the \idr{a}'s that has type \idr{s}, if any>. We must show
      <...and here we restate \idr{P(c a1 ... ak)}>.

      <go on and prove \idr{P(n)} to finish the case...>

    - <other cases similarly...> $\square$

_Example_:

  - _Theorem_: For all sets \idr{x}, lists \idr{l : List x}, and numbers
    \idr{n}, if \idr{length l = n} then \idr{index (S n) l = None}.

    _Proof_: By induction on \idr{l}.

    - Suppose \idr{l = []}. We must show, for all numbers \idr{n}, that, if
      \idr{length [] = n}, then \idr{index (S n) [] = None}.

      This follows immediately from the definition of \idr{index}.

    - Suppose \idr{l = x :: l'} for some \idr{x} and \idr{l'}, where
      \idr{length l' = n'} implies \idr{index (S n') l' = None}, for any number
      \idr{n'}. We must show, for all \idr{n}, that, if \idr{length (x::l') = n}
      then \idr{index (S n) (x::l') = None}.

      Let \idr{n} be a number with \idr{length l = n}. Since
      \idr{length l = length (x::l') = S (length l')},
      it suffices to show that
      \idr{index (S (length l')) l' = None}.
      But this follows directly from the induction hypothesis, picking \idr{n'}
      to be \idr{length l'}. $\square$

=== Induction Over an Inductively Defined Proposition

Since inductively defined proof objects are often called "derivation trees,"
this form of proof is also known as _induction on derivations_.

_Template_:

  - _Theorem_: <Proposition of the form "\idr{Q -> P}," where \idr{Q} is some
    inductively defined proposition (more generally, "For all \idr{x} \idr{y}
    \idr{z}, \idr{Q x y z -> P x y z}")>

    _Proof_: By induction on a derivation of \idr{Q}. <Or, more generally,
    "Suppose we are given \idr{x}, \idr{y}, and \idr{z}. We show that
    \idr{Q x y z} implies \idr{P x y z}, by induction on a derivation of
    \idr{Q x y z}"...>

    <one case for each constructor \idr{c} of \idr{Q}...>

      - Suppose the final rule used to show \idr{Q} is \idr{c}. Then <...and
        here we state the types of all of the \idr{a}'s together with any
        equalities that follow from the definition of the constructor and the IH
        for each of the \idr{a}'s that has type \idr{Q}, if there are any>. We
        must show <...and here we restate \idr{P}>.

        <go on and prove \idr{P} to finish the case...>

        <other cases similarly...> $\square$

_Example_

  - _Theorem_: The \idr{<=} relation is transitive — i.e., for all numbers
    \idr{n}, \idr{m}, and \idr{o}, if \idr{n <= m} and \idr{m <= o}, then
    \idr{n <= o}.

    _Proof_: By induction on a derivation of \idr{m <= o}.

      - Suppose the final rule used to show \idr{m <= o} is \idr{Le_n}. Then
        \idr{m = o} and we must show that \idr{n <= m}, which is immediate by
        hypothesis.

      - Suppose the final rule used to show \idr{m <= o} is \idr{Le_S}. Then
        \idr{o = S o'} for some \idr{o'} with \idr{m <= o'}. We must show that
        \idr{n <= S o'}. By induction hypothesis, \idr{n <= o'}.

        But then, by \idr{Le_S}, \idr{n <= S o'}. $\square$


module Tk

import public Data.So

%default total
%access public export

infix 6 ==., /=.

interface Equ a where
  (==.) : a -> a -> Bool

  eqRefl  : (x : a) -> So (x ==. x)
  eqSymm  : (x, y : a) -> x ==. y = y ==. x
  eqTrans : (x, y, z : a) -> So (x ==. y) -> So (y ==. z) -> So (x ==. z)

  (/=.) : a -> a -> Bool
  x /=. y = not $ x ==. y

  neqIsNotEq : (x, y : a) -> x ==. y = not (x /=. y)

infix 6 <., <=., >., >=.

interface Equ a => Ordu a where
  (<.) : a -> a -> Bool

  ltArefl : (x : a) -> So (not $ x <. x)
  ltAsymm : (x, y : a) -> So (x <. y) -> So (not $ y <. x)
  ltTrans : (x, y, z : a) -> So (x <. y) -> So (y <. z) -> So (x <. z)

  (<=.) : a -> a -> Bool
  x <=. y = x <. y || x ==. y

  lteIsLtOrE : (x, y : a) -> x <=. y = (x <. y || x ==. y)

  (>.) : a -> a -> Bool
  x >. y = y <. x

  gtInverseOfLt : (x, y : a) -> x <. y = y >. x

  (>=.) : a -> a -> Bool
  x >=. y = y <=. x

  gteIsGtOrE : (x, y : a) -> x >=. y = (x >. y || x ==. y)


module NatTree

import Data.Fin

% access public export

data NatTree : Type where
  Leaf : Nat -> NatTree
  Node : NatTree -> NatTree -> NatTree

tsum : NatTree -> Nat
tsum (Leaf k) = k
tsum (Node x y) = (tsum x) + (tsum y)

recNT : (x: Type) -> (Nat -> x) -> (NatTree -> x -> NatTree -> x -> x)
  -> (NatTree -> x)
recNT x f g (Leaf k) = f k
recNT x f g (Node y z) = g y yValue z zValue where
  yValue =  recNT x f g y
  zValue = recNT x f g z

tsumTheHardWay : NatTree -> Nat
tsumTheHardWay = recNT Nat leafCase nodeCase where
  leafCase = \k : Nat => k
  nodeCase = \t1 => \n1 => \t2 => \n2 => n1 + n2

flattenTree : NatTree -> List Nat
flattenTree = recNT (List Nat) leafCase nodeCase where
  leafCase = \k => k :: []
  nodeCase = \t1 => \l1 => \t2 => \l2 => (l1 ++ l2)

data FinNatTree : Type where
  FLeaf : Nat -> FinNatTree
  FNode : (n: Nat) -> ((Fin (S n)) -> FinNatTree) -> FinNatTree

sumFinNat : (n: Nat) -> (Fin n -> Nat) -> Nat
sumFinNat Z f = Z
sumFinNat (S k) f = (f FZ) + tailsum where
  tailsum = sumFinNat k (\ x : (Fin k) => f (FS x))

finSum : FinNatTree -> Nat
finSum (FLeaf k) = k
finSum (FNode n f) = sumFinNat (S n) treeSums where
  treeSums = \index : Fin (S n) => finSum (f index)

  data Evil : Type where
    Diag : (Evil -> Bool) -> Evil

  evil : Evil -> Bool
  evil (Diag f) = not (f (Diag f))

  contra : Bool
  contra = evil (Diag evil)


module Group.FiniteGenerate

import Group

%access public export

--proof that one element divides another in a group

-- element is generator if it is in the group and there is no element in the group that divides it

-- Create the collection of generators with their order

-- generates g^n given a group element g and n : Nat
Pow : (grp : Type) -> ((*) : grp -> grp -> grp) -> IsGroup grp (*) -> (g : grp) -> (n : Nat) -> grp
Pow grp (*) pfgrp g Z = fst (Group_id grp (*) pfgrp)
Pow grp (*) pfgrp g (S k) = g*(Pow grp (*) pfgrp g k)

-- check that group is finitely generated
HasFiniteOrder : (grp : Type) -> ((*) : grp -> grp -> grp) -> IsGroup grp (*) -> (g : grp) -> Type
HasFiniteOrder grp (*) pfgrp g = (n : Nat ** (LT Z n, Pow grp (*) pfgrp g n = fst (Group_id grp (*) pfgrp)))


module Gen
import Data.Fin
import Data.ZZ

%hide Prelude.Classes.(+)
%hide Prelude.Classes.(*)
%hide Prelude.Classes.(-)
%hide Prelude.Classes.(/)

%default total

-- Fin -> Nat

implicit finNat : Fin n -> Nat
finNat = finToNat

-- Fin, Nat, Integer -> ZZ

implicit finZZ : Fin n -> ZZ
finZZ = Pos . finToNat

implicit natZZ : Nat -> ZZ
natZZ = Pos

implicit integerZZ : Integer -> ZZ
integerZZ = cast

-- Fin, Nat, ZZ -> Integer

implicit zzInteger : ZZ -> Integer
zzInteger = cast

implicit natInteger : Nat -> Integer
natInteger = zzInteger . natZZ

implicit finInteger : Fin n -> Integer
finInteger = zzInteger . finZZ

-- operations

namespace NatOp
  (+) : Nat -> Nat -> Nat
  (+) = plus

  (*) : Nat -> Nat -> Nat
  (*) = mult

namespace ZZOp
  (+) : ZZ -> ZZ -> ZZ
  (+) = plusZ

  (*) : ZZ -> ZZ -> ZZ
  (*) = multZ

  (-) : ZZ -> ZZ -> ZZ
  (-) = subZ

namespace IntegerOp
  (+) : Integer -> Integer -> Integer
  (+) = prim__addBigInt

  (*) : Integer -> Integer -> Integer
  (*) = prim__mulBigInt

  (-) : Integer -> Integer -> Integer
  (-) = prim__subBigInt

-- Rationals

data Rat : Type where
  (#) : ZZ -> (y:Nat) -> {default Refl nz: isZero y = False} -> Rat

infixl 9 #

nonZeroMult : (x,y: Nat) -> isZero x = False -> isZero y = False -> isZero (mult x y) = False
nonZeroMult Z y p1 p2 = void $ trueNotFalse p1
nonZeroMult (S k) Z p1 p2 = void $ trueNotFalse p2
nonZeroMult (S k) (S j) p1 p2 = Refl

isRZero : Rat -> Bool
isRZero ((#) (Pos Z) y) = True
isRZero _ = False

rPlus : Rat -> Rat -> Rat
rPlus ((#) x y {nz=p1}) ((#) x' y' {nz=p2}) =
  (#) (x * y' + x' * y) (y * y') {nz=nonZeroMult y y' p1 p2}

rSub : Rat -> Rat -> Rat
rSub ((#) x y {nz=p1}) ((#) x' y' {nz=p2}) =
  (#) (x * y' - x' * y) (y * y') {nz=nonZeroMult y y' p1 p2}

rMult : Rat -> Rat -> Rat
rMult ((#) k j {nz=p1}) ((#) i y {nz=p2}) =
  (#) (k * i) (j * y) {nz=nonZeroMult j y p1 p2}

rDiv : Rat -> (y: Rat) -> isRZero y = False -> Rat
rDiv ((#) k j {nz=p1}) ((#) (Pos Z) y {nz=p2}) rnz = void $ trueNotFalse rnz
rDiv ((#) k j {nz=p1}) ((#) (Pos (S i)) y {nz=p2}) rnz =
  (#) (k * y) (j * (S i)) {nz=nonZeroMult j (S i) p1 Refl}
rDiv ((#) (Pos Z) j {nz=p1}) ((#) (NegS i) y {nz=p2}) rnz = 0#1
rDiv ((#) (Pos (S k)) j {nz=p1}) ((#) (NegS i) y {nz=p2}) rnz =
  (#) ((NegS k) * y) (j * (S i)) {nz=nonZeroMult j (S i) p1 Refl}
rDiv ((#) (NegS k) j {nz=p1}) ((#) (NegS i) y {nz=p2}) rnz =
  (#) ((S k) * y) (j * (S i)) {nz=nonZeroMult j (S i) p1 Refl}

-- Fin, Nat, ZZ, Integer -> Rat

implicit zzRat : ZZ -> Rat
zzRat = (# 1)

implicit integerRat : Integer -> Rat
integerRat = zzRat . integerZZ

implicit natRat : Nat -> Rat
natRat = zzRat . natZZ

implicit finRat : Fin n -> Rat
finRat = zzRat . finZZ

namespace RatOp
  (+) : Rat -> Rat -> Rat
  (+) = rPlus

  (*) : Rat -> Rat -> Rat
  (*) = rMult

  (-) : Rat -> Rat -> Rat
  (-) = rSub

  (/) : Rat -> (y: Rat) -> {default Refl nz: isRZero y = False} -> Rat
  (/) x y {nz} = rDiv x y nz


import Data.Vect

-- Exercise 1
myPlusCommutes : (n : Nat) -> (m : Nat) -> n + m = m + n
myPlusCommutes Z m = rewrite plusZeroRightNeutral m in Refl
myPlusCommutes (S k) m = rewrite myPlusCommutes k m in
                         rewrite plusSuccRightSucc m k in Refl

-- Exercise 2
reverseProof_nil : (acc : Vect n1 a) -> Vect (plus n1 0) a
reverseProof_nil {n1} acc = rewrite plusZeroRightNeutral n1 in acc

reverseProof_xs : Vect (S n + k) a -> Vect (plus n (S k)) a
reverseProof_xs {n} {k} xs = rewrite sym (plusSuccRightSucc n k) in xs

myReverse : Vect n a -> Vect n a
myReverse xs = reverse' [] xs
  where
    reverse' : Vect n a -> Vect m a -> Vect (n + m) a
    reverse' acc [] = reverseProof_nil acc
    reverse' acc (x :: xs) = reverseProof_xs (reverse' (x::acc) xs)


module VectHelp

import Data.Vect

%default total

||| If an element is in a concat of two vectors, then it's in one or the other.
export
elemConcat: {x: a} -> (xs: Vect m a) -> (ys: Vect n a) -> Elem x (xs ++ ys) -> Either (Elem x xs) (Elem x ys)
elemConcat {x} []        (x :: xs) Here          = Right Here
elemConcat {x} []        (y :: xs) (There later) = Right (There later)
elemConcat {x} (x :: xs) ys        Here          = Left Here
elemConcat {x} (y :: xs) ys        (There later) = case elemConcat xs ys later of
  Left l  => Left $ There l
  Right r => Right r

shift : {x : a} -> {xs : Vect len a} ->
        (y : a ** Elem y xs) -> (y : a ** Elem y (x :: xs))
shift (x ** pf) = (x ** There pf)

||| Returns all the proofs that elements of a vector are in that vector.
export
allElems: (xs: Vect n a) -> Vect n (x: a ** Elem x xs)
allElems []        = []
allElems (x :: xs) =
    let es = allElems xs
    in (x ** Here) :: (map shift es)


module CatR

%default total

{- A category is very much like a graph. It has vertices
   named objects and vertices named arrows. Each arrow goes
   from an object to an object (possibly the same!). -}
record Cat (obj: Type) (arr: obj -> obj -> Type) where
  constructor MkCat

  {- For each object, there is an arrow called `id` which
  goes from the object to itself. -}
  id: {o: obj} -> arr o o

  {- Given an arrow `f` from object `a` to `b` and an arrow
      `g` from `b` to `c`. We can compose these arrow. The
      result is an arrow from `a` to `c`. -}
  compose: {a, b, c: obj} -> arr a b -> arr b c -> arr a c

  -- Here comes some properties of `id` and `compose`

  {- For any arrow `f`, compose id f = f -}
  neutralLeft:  {a, b: obj} -> (f: arr a b) -> compose id f = f

  {- For any arrow `f`, compose f id = f  -}
  neutralRight: {a, b: obj} -> (f: arr a b) -> compose f id = f

  {- For any arrows `f`, `g` and `h`,
    composing f with g, and then the result with h
    gives exatctly the same result as
    composing f with the result of the composition of g and h

    Which means, like string concatenation than we can commpose
    the way we preserve the order of each element in the sequence.-}
  associativity: {a, b, c, d: obj} ->
                  (f: arr a b) -> (g: arr b c) -> (h: arr c d) ->
                  compose f (compose g h) = compose (compose f g) h

{- `LE n m` encode the property that `n ≤ m`
    i.e. `n` is less or equal to `m` -}
data LE : Nat -> Nat -> Type where
  LERefl : {o: Nat} -> LE o o
  LENext : {a, b: Nat} -> LE a b -> LE a (S b)

{- Taking naturals as objects and `LE` as arrows,
   this actually forms a category! -}
natPoset : Cat Nat LE
natPoset = ???


module Divisors

import ZZ
%access public export
%default total
|||isDivisible a b can be constucted if b divides a
isDivisible : ZZ -> ZZ -> Type
isDivisible a b = (n : ZZ ** a = b * n)

|||1 divides everything
oneDiv : (a : ZZ) -> isDivisible a 1
oneDiv a = (a ** rewrite sym (multOneLeftNeutralZ a) in Refl)

|||Genetes a proof of (a+b) = d*(n+m) from (a=d*n)and (b=d*m)
DistributeProof: (a:ZZ)->(b:ZZ)->(d:ZZ)->(n:ZZ)->(m:ZZ)->(a=d*n)->(b=d*m)->((a+b) = d*(n+m))
DistributeProof a b d n m pf1 pf2 = rewrite  (multDistributesOverPlusRightZ d n m) in(trans (the (a+b=(d*n)+b) (v1)) v2) where
  v1 =plusConstantRightZ a (d*n) b pf1
  v2 =plusConstantLeftZ b (d*m) (d*n) pf2

|||The theorem d|a =>d|ac
MultDiv:(isDivisible a d) ->(c:ZZ)->(isDivisible (a*c) d)
MultDiv {d} (n**Refl) c =((n*c)** (rewrite sym (multAssociativeZ d n c) in (Refl)))

|||The theorem d|a and d|b =>d|(a+b)
PlusDiv : (isDivisible a d)->(isDivisible b d)->(isDivisible (a+b) d)
PlusDiv {d}{a}{b} (n**prf1) (m**prf2) = ((n+m)**(DistributeProof a b d n m prf1 prf2))
|||The theorem b|a and c|b =>c|a
TransDivide : (isDivisible a b)->(isDivisible b c)->(isDivisible a c)
TransDivide {c} (x ** pf1) (y ** pf2) = (y*x ** (rewrite  multAssociativeZ c y x in  (rewrite pf1 in (rewrite pf2 in Refl))))

|||If d divides a and b it divides a linear combination of a and b
LinCombDiv:(m:ZZ)->(n:ZZ)->(isDivisible a d)->(isDivisible b d)->(isDivisible ((a*m)+(b*n)) d)
LinCombDiv m n dDiva dDivb = PlusDiv (MultDiv  dDiva m) (MultDiv  dDivb n)

EuclidConservesDivisor:(m:ZZ)->(isDivisible a d)->(isDivisible b d)->(isDivisible (a+(b*(-m))) d)
EuclidConservesDivisor m dDiva dDivb = PlusDiv dDiva (MultDiv dDivb (-m) )

|||Any integer divides zero
ZZDividesZero:(a:ZZ)->(isDivisible 0 a )
ZZDividesZero a = (0**(sym (multZeroRightZeroZ a)))
|||A type that is occupied iff c is a common factor of a and b
isCommonFactorZ : (a:ZZ) -> (b:ZZ) -> (c:ZZ) -> Type
isCommonFactorZ a b c = ((isDivisible a c),(isDivisible b c))
|||The GCD type that is occupied iff d = gcd (a,b). Here GCD is defined as that positive integer such that any common factor of a and b divides it
GCDZ : (a:ZZ) -> (b:ZZ) -> (d:ZZ) -> Type
GCDZ a b d = ((IsPositive d),(isCommonFactorZ a b d),({c:ZZ}->(isCommonFactorZ a b c)->(isDivisible d c)))
|||Anything divides itself
SelfDivide:(a:ZZ)->(isDivisible a a)
SelfDivide a = (1**sym (multOneRightNeutralZ a))

|||Generates the proof that if c is a common factor of a and 0 then c divides a
GCDCondition : (a:ZZ) -> ({c:ZZ}->(isCommonFactorZ a 0 c)->(isDivisible a c))
GCDCondition  a {c} (cDiva,cDiv0) = cDiva
|||Proves that the GCD of a and 0 is a
gcdOfZeroAndInteger:(a:ZZ)->IsPositive a ->GCDZ a 0 a
gcdOfZeroAndInteger a pf = (pf,((SelfDivide a),(ZZDividesZero a)),((GCDCondition a)))
|||The theorem, d|a =>d|(-a)
dDividesNegative:(isDivisible a d)->(isDivisible  (-a) d)
dDividesNegative{a}{d} (x ** pf) = ((-x)**(multNegateRightIsNegateZ a d x pf))
|||The theorem c|b and c|(a+bp) then c|a
cDiva :{p:ZZ} ->(cDIvb :(isDivisible b c))->(cDIvExp:isDivisible (a+(b*p)) c)->(isDivisible a c)
cDiva {p}{b}{a}{c} cDivb cDivExp = rewrite (sym (addAndSubNeutralZ a (b*p))) in (PlusDiv cDivExp (dDividesNegative(MultDiv cDivb (p))))
|||A helper function for euclidConservesGcd function
genFunctionForGcd :(f:({c:ZZ}->(isCommonFactorZ a b c)->(isDivisible d c)))->(({c:ZZ}->(isCommonFactorZ b (a+(b*(-m)))  c)->(isDivisible d c)))
genFunctionForGcd f (cDivb,cDivExp) = f((cDiva cDivb cDivExp,cDivb))

|||The theorem, gcd(a,b)=d => gcd (b, a+ b(-m))=d
euclidConservesGcd :(m:ZZ)->(GCDZ a b d)->(GCDZ b  (a+(b*(-m))) d)
euclidConservesGcd m  (posProof, (dDiva,dDivb), f) = (posProof,(dDivb,(EuclidConservesDivisor m  dDiva dDivb)),genFunctionForGcd f)
|||The theorem that if c and d are positive d|c => (d is less than or equal to c)
posDivPosImpliesLte:(isDivisible c d)->(IsPositive c)->(IsPositive d)->LTEZ d c
posDivPosImpliesLte {d}{c}(x ** pf) cPos dPos = posLteMultPosPosEqZ {q=x} d c dPos cPos pf

|||The Theorem that if c and d are positive, d|c and c|d =>(c=d)
PosDivAndDivByImpliesEqual: (isDivisible c d)->(isDivisible d c)->(IsPositive c)->(IsPositive d) -> (c=d)
PosDivAndDivByImpliesEqual x y z x1 =lteAndGteImpliesEqualZ dLtec cLted where
  dLtec =posDivPosImpliesLte x z x1
  cLted =posDivPosImpliesLte y x1 z


module gcd

import NatUtils

%default total
%access public export

|||Auxilliary proof for euclidDivide
--Proof to finish euclidDivide, couldn't add it as a where clause within euclidDivide. If someone knows how to do that, please do so.
extendedEqualityProof : (a : Nat) -> (b : Nat) -> (q : Nat) -> (r : Nat)->
					((S r) = b) -> (a = r + (q * b)) -> (S a = (S q) * b)
extendedEqualityProof a b q r proofSmlEq proofBigEq =
	trans (cong proofBigEq) (plusConstantRight (S r) b (q * b) proofSmlEq)

||| Given a, b, and a proof that b != 0, returns (q, r) and proofs that a = bq + r, r < b
--removed possible problems with Rohit's
euclidDivide : (a : Nat) -> (b : Nat) ->
  (b = Z -> Void) -> (q : Nat ** (r : Nat ** ((a = r + (q * b)), LT r b)))
euclidDivide a Z pf = void(pf Refl)
euclidDivide Z (S k) SIsNotZ = (Z ** (Z ** (Refl, LTESucc LTEZero)))
euclidDivide (S n) (S k) SIsNotZ =
  case (euclidDivide n (S k) SIsNotZ) of
		(q ** (r ** (equalityProof, ltproof))) =>
        case (ltImpliesEqOrLT r (S k) ltproof) of
						(Right proofSrLTSk) => (q ** ((S r) ** ((cong equalityProof), proofSrLTSk)))
						(Left proofSreqSk) => ((S q) ** (Z ** ((extendedEqualityProof n (S k) q r proofSreqSk equalityProof), LTESucc LTEZero)))

|||Type of proof that d divides a
isDivisible : (a : Nat) -> (d : Nat) -> (Not (d = Z)) -> Type
isDivisible a d proofNotZ = (n : Nat ** a = n * d)

|||Proof that all natural numbers divide zero
zeroDivisible : {n : Nat} ->  isDivisible Z (S n) SIsNotZ
zeroDivisible {n} = (Z ** sym (multZeroLeftZero (S n)))

|||Proof that 1 divides all natural numbers
oneDivides : {n : Nat} -> isDivisible n (S Z) SIsNotZ
oneDivides {n} = (n ** rewrite multOneRightNeutral n in Refl)

|||Proof that all natural numbers divide themselves
divisionReflexive : {n : Nat} -> (isDivisible (S n) (S n) SIsNotZ)
divisionReflexive {n} = (S Z ** rewrite multOneLeftNeutral n in Refl)

|||Type of proof that d is a common divisor of a and b
isCommonDivisor : (a : Nat) -> (b : Nat) -> (d : Nat) -> (Not (d = Z)) -> Type
isCommonDivisor a b d proofNotZ = (isDivisible a d proofNotZ, isDivisible b d proofNotZ)

|||Proof that 1 is a common divisor of all pairs of natural numbers
oneCommonDivisor : {a : Nat} -> (b : Nat) -> (isCommonDivisor a b (S Z) SIsNotZ)
oneCommonDivisor {a} {b} = (oneDivides, oneDivides)

|||Proof that n is a common divisor of n and 0
zeroCommonDivisorRight : {n : Nat} -> (isCommonDivisor (S n) Z (S n) SIsNotZ)
zeroCommonDivisorRight {n} = (divisionReflexive, zeroDivisible)

|||Proof that n is a common divisor of 0 and n
zeroCommonDivisorLeft : {n : Nat} -> (isCommonDivisor Z (S n) (S n) SIsNotZ)
zeroCommonDivisorLeft {n} = (zeroDivisible, divisionReflexive)

|||Proof that a common divisor of (a, b) is a common divisor of (b, a)
commonDivisorSymmetric : {a : Nat} -> {b : Nat} -> {d : Nat} -> (isCommonDivisor a b d proofNotZ) -> (isCommonDivisor b a d proofNotZ)
commonDivisorSymmetric {a} {b} {d} (proofDividesa, proofDividesb) = (proofDividesb, proofDividesa)

|||Proof that d divides a implies d divides c * a
dividesMultiple : {a : Nat} -> {d : Nat} -> (isDivisible a d proofNotZ) -> (c : Nat)-> (isDivisible (c * a) d proofNotZ)
dividesMultiple {d} (n ** Refl) c = ((c * n) ** (rewrite (multAssociative c n d) in Refl))

|||Proof of distributivity
distributeProof: (a : Nat) -> (b : Nat) -> (d : Nat) -> (m : Nat) -> (n : Nat) ->
(a = m * d) -> (b = n * d) -> ((a + b) = (m + n) * d)
distributeProof a b d m n proofDividesa proofDividesb =
	rewrite (multDistributesOverPlusLeft m n d) in
		(trans (the (a + b = (m * d) + b) (v1)) v2) where
			v1 = plusConstantRight a (m * d) b proofDividesa
			v2 = plusConstantLeft b (n * d) (m * d) proofDividesb

|||Proof that d is a common divisor of a and b implies d divides a + b
dividesSum :  {a : Nat} -> {b : Nat} -> {d : Nat} -> (isCommonDivisor a b d proofNotZ)-> (isDivisible (a + b) d proofNotZ)
dividesSum {a} {b} {d} ((m ** proofDividesa), (n ** proofDividesb)) =
	((m + n) ** (distributeProof a b d m n proofDividesa proofDividesb))

|||Proof d divides a and a = b implies d divides b
eqConservesDivisible : {a : Nat} -> {b : Nat} -> {d : Nat} -> {proofNotZ : Not (d = Z)} ->
					(isDivisible a d proofNotZ) -> (a = b) -> (isDivisible b d proofNotZ)
eqConservesDivisible {a} {d} (n ** proofDivides) Refl = (n ** proofDivides)

|||Proof that d is a common divisor of a and b implies d divides a * x + b * y
dividesLinearCombination :  {a : Nat} -> {b : Nat} -> {d : Nat} -> {proofNotZ : Not (d = Z)} ->
						(isCommonDivisor a b d proofNotZ) -> (x : Nat) -> (y : Nat) ->
						(isDivisible ((x * a) + (y * b)) d proofNotZ)
dividesLinearCombination {proofNotZ = prf} commonDivisorProof x y =
	dividesSum {proofNotZ = prf} ((dividesMultiple {proofNotZ = prf} (fst commonDivisorProof) x), (dividesMultiple {proofNotZ = prf} (snd commonDivisorProof) y))


module NatOrder

import NatUtils

%default total
%access public export

|||New type for <= on Nat
LEQ : (a : Nat) -> (b : Nat) -> Type
LEQ a b = (k : Nat ** ((a + k) = b))

|||New type for >= on Nat
GEQ : (a : Nat) -> (b : Nat) -> Type
GEQ a b = LEQ b a

|||New type for < on Nat
LNEQ : (a : Nat) -> (b : Nat) -> Type
LNEQ a b = (LEQ a b, Not (a = b))

|||New type for > on Nat
GNEQ : (a : Nat) -> (b : Nat) -> Type
GNEQ a b = LNEQ b a

|||Proof that 0 is the smallest natural number
LEQZero : {n : Nat} -> LEQ Z n
LEQZero {n} = (n ** Refl)

|||Proof that 0 is lesser than every successor
LNEQZeroSucc : {n : Nat} -> LNEQ Z (S n)
LNEQZeroSucc {n} = (((S n) ** Refl), ZIsNotS)

|||Proof that a <= b implies (S a) <= (S b)
LEQSucc : {a : Nat} -> {b : Nat} -> (LEQ a b) -> (LEQ (S a) (S b))
LEQSucc {a} {b} (k ** proofEq) = (k ** cong proofEq)

|||Proof that a < b implies (S a) < (S b)
LNEQSucc : {a : Nat} -> {b : Nat} -> (LNEQ a b) -> (LNEQ (S a) (S b))
LNEQSucc {a} {b} ((k ** proofEq), proofNotEq) = ((k ** cong proofEq), proofNotEqSucc proofNotEq) where
	proofNotEqSucc : (Not (a = b)) -> (Not (S a = S b))
	proofNotEqSucc proofNotEq proofEq = proofNotEq (predEqual proofEq)

|||Proof that (S a) <= (S b) implies a <= b
LEQPred : {a : Nat} -> {b : Nat} -> (LEQ (S a) (S b)) -> (LEQ a b)
LEQPred {a} {b} (k ** proofEq) = (k ** predEqual proofEq)

|||Proof that (S a) < (S b) implies a < b
LNEQPred : {a : Nat} -> {b : Nat} -> (LNEQ (S a) (S b)) -> (LNEQ a b)
LNEQPred {a} {b} ((k ** proofEq), proofNotEq) = ((k ** predEqual proofEq), proofNotEqPred proofNotEq) where
	proofNotEqPred : (Not (S a = S b)) -> (Not (a = b))
	proofNotEqPred proofNotEq proofEq = proofNotEq (cong proofEq)

|||Proof that !(a <= b) implies !((S a) <= (S b))
notLEQSucc : {a : Nat} -> {b : Nat} -> (Not (LEQ a b)) -> (Not (LEQ (S a) (S b)))
notLEQSucc {a} {b} proofNotLEQ proofLEQ = proofNotLEQ (LEQPred proofLEQ)

|||Proof that !((S a) <= (S b)) implies !(a <= b)
notLEQPred : {a : Nat} -> {b : Nat} ->  (Not (LEQ (S a) (S b))) -> (Not (LEQ a b))
notLEQPred {a} {b} proofNotLEQ proofLEQ = proofNotLEQ (LEQSucc proofLEQ)

|||Proof that !(a < b) implies !((S a) < (S b))
notLNEQSucc : {a : Nat} -> {b : Nat} -> (Not (LNEQ a b)) -> (Not (LNEQ (S a) (S b)))
notLNEQSucc {a} {b} proofNotLNEQ proofLNEQ = proofNotLNEQ (LNEQPred proofLNEQ)

|||Proof that !((S a) < (S b)) implies !(a < b)
notLNEQPred : {a : Nat} -> {b : Nat} ->  (Not (LNEQ (S a) (S b))) -> (Not (LNEQ a b))
notLNEQPred {a} {b} proofNotLNEQ proofLNEQ = proofNotLNEQ (LNEQSucc proofLNEQ)

|||Proof that !(a < = b) implies b < a
notLEQImpliesGNEQ : {a : Nat} -> {b : Nat} -> (Not (LEQ a b)) -> (GNEQ a b)
notLEQImpliesGNEQ {a = Z} {b} proofNotLEQ = void (proofNotLEQ (b ** Refl))
notLEQImpliesGNEQ {a = (S n)} {b = Z} _ = ((S n ** Refl), ZIsNotS)
notLEQImpliesGNEQ {a = (S n)} {b = (S m)} proofNotLEQ = (LEQSucc proofGEQ, notEqualSucc proofNotEq) where
	proofGEQ = fst (notLEQImpliesGNEQ {a = n} {b = m} (notLEQPred proofNotLEQ))
	proofNotEq = snd (notLEQImpliesGNEQ {a = n} {b = m} (notLEQPred proofNotLEQ))

|||Proof that !(a < b) implies b <= a
notLNEQImpliesGEQ : {a : Nat} -> {b : Nat} -> (Not (LNEQ a b)) -> (GEQ a b)
notLNEQImpliesGEQ {a} {b = Z} _ = (a ** Refl)
notLNEQImpliesGEQ {a = Z} {b = (S m)} proofNotLNEQ = void (proofNotLNEQ (((S m) ** Refl), ZIsNotS))
notLNEQImpliesGEQ {a = (S n)} {b = (S m)} proofNotLNEQ = (LEQSucc proofGEQ) where
	proofGEQ = notLNEQImpliesGEQ {a = n} {b = m} (notLNEQPred proofNotLNEQ)

|||decides if a <= b
isLEQ : (a : Nat) -> (b : Nat) -> Dec (LEQ a b)
isLEQ Z b = Yes (b ** Refl)
isLEQ (S a) Z = No (\(k ** proofEq) => (SIsNotZ proofEq))
isLEQ (S a) (S b) with (isLEQ a b)
	isLEQ (S a) (S b) | (Yes proofLEQ) = Yes (LEQSucc proofLEQ)
	isLEQ (S a) (S b) | (No contra) = No (\(k ** proofEq) => (contra (k ** (predEqual proofEq))))

|||Proof that a <= b implies a = b or a < b
leqImpliesEqOrLNEQ : {a : Nat} -> {b : Nat} -> (LEQ a b) -> Either (a = b) (LNEQ a b)
leqImpliesEqOrLNEQ {a} {b} (k ** proofEq) = case k of
	Z => Left (rewrite (plusCommutative Z a) in proofEq)
	(S n) => Right (((S n) ** proofEq), nonZeroSumNotEqual proofEq SIsNotZ)

|||Proof that all successors are larger than 0
succNotLEQzero : {n : Nat} -> (Not (LEQ (S n) Z))
succNotLEQzero {n} = \(k ** proofEq) => (SIsNotZ proofEq)

|||Proof that LEQ is reflexive
leqRefl : {n : Nat} -> LEQ n n
leqRefl {n} = (Z ** plusZeroRightNeutral n)

|||Proof that (a + k = b) and (b + l = a) implies (k = 0) and (l = 0)
leqAntiSymmetricIndirect : {a : Nat} -> {b : Nat} ->
						(proofLEQLeft : (LEQ a b)) -> (proofLEQRight : (LEQ b a)) ->
						((fst proofLEQLeft) = Z, (fst proofLEQRight) = Z)
leqAntiSymmetricIndirect {a} {b} (k ** proofEqLeft) (l ** proofEqRight) =
	sumZeroImpliesZero (plusLeftCancel a (k + l) Z inductionStep) where
	inductionStep = rewrite (plusAssociative a Z (k + l)) in
					rewrite (plusZeroRightNeutral a) in
					rewrite (plusAssociative a k l) in
					trans (cong {f = (\n => n + l)} proofEqLeft) proofEqRight

|||Proof that LEQ is antisymmetric
leqAntiSymmetric : {a : Nat} -> {b : Nat} -> (LEQ a b) -> (LEQ b a) -> (a = b)
leqAntiSymmetric {a} {b} (k ** proofEqLeft) (l ** proofEqRight) =
	rewrite (plusCommutative Z a) in
	rewrite (sym (fst (leqAntiSymmetricIndirect {a} {b} (k ** proofEqLeft) (l ** proofEqRight)))) in proofEqLeft

|||Proof that LEQ is transitive
leqTransitive : {a : Nat} -> {b : Nat} -> {c : Nat} -> (LEQ a b) -> (LEQ b c) -> (LEQ a c)
leqTransitive {a} {b} {c} (k ** proofEqLeft) (l ** proofEqRight) = ((k + l) ** proofEq) where
	proofEq = rewrite (plusAssociative a k l) in
			trans (cong {f = (\n => n + l)} proofEqLeft) (proofEqRight)

|||Proof that a <= b implies a <= b + c
leqPlusRight : {a : Nat} -> {b : Nat} -> (c : Nat) -> (LEQ a b) -> (LEQ a (b + c))
leqPlusRight {a} {b} c (k ** proofEq) = ((k + c) ** rewrite (plusAssociative a k c) in (cong {f = (\n => (n + c))} proofEq))

|||Proof that a + c <= b implies a <= b
ltePlusLeft : {a : Nat} -> {b : Nat} -> {c : Nat} -> (LEQ (a + c) b) -> (LEQ a b)
ltePlusLeft {a} {b} {c} (k ** proofEq) = ((c + k) ** rewrite (plusAssociative a c k) in proofEq)

|||Proof that a <= b implies (c + a) <= (c + b)
leqPlusConstantLeft : {a : Nat} -> {b : Nat} -> (c : Nat) -> (LEQ a b) -> (LEQ (c + a) (c + b))
leqPlusConstantLeft {a} {b} c (k ** proofEq) = (k ** proofFinalEq) where
	proofFinalEq = rewrite (sym (plusAssociative c a k)) in (cong {f = (\n => c + n)} proofEq)

|||Proof that a <= b implies (a + c) <= (b + c)
leqPlusConstantRight : {a : Nat} -> {b : Nat} -> (c : Nat) -> (LEQ a b) -> (LEQ (a + c) (b + c))
leqPlusConstantRight {a} {b} c proofLEQ = rewrite (plusCommutative a c) in
									rewrite (plusCommutative b c) in
									(leqPlusConstantLeft c proofLEQ)

|||Proof that (c + a) <= (c + b) implies a <= b
leqMinusConstantLeft : {a : Nat} -> {b : Nat} -> {c : Nat} -> (LEQ (c + a) (c + b)) -> (LEQ a b)
leqMinusConstantLeft {a} {b} {c} (k ** proofEq) = (k ** proofFinalEq) where
	proofFinalEq = (plusLeftCancel c (a + k) b (rewrite (plusAssociative c a k) in proofEq))

|||Proof that (a + c) <= (b + c) implies a <= b
leqMinusConstantRight : {a : Nat} -> {b : Nat} -> {c : Nat} -> (LEQ (a + c) (b + c)) -> (LEQ a b)
leqMinusConstantRight {a} {b} {c} proofLEQ = leqMinusConstantLeft {a} {b} {c} proofFinalEq where
	proofFinalEq = rewrite (plusCommutative c a) in
				rewrite (plusCommutative c b) in proofLEQ

|||Proof that if a <= b, and c <= d, then (a + c) <= (b + d)
leqPlusIsLEQ : {a : Nat} -> {b : Nat} -> {c : Nat} -> {d : Nat} ->
			(LEQ a b) -> (LEQ c d) -> (LEQ (a + c) (b + d))
leqPlusIsLEQ {a = a0} {b = b0} {c = c0} {d = d0} proofLeftLEQ proofRightLEQ =
	leqTransitive (leqPlusConstantRight {a = a0} {b = b0} c0 proofLeftLEQ) (leqPlusConstantLeft {a = c0} {b = d0} b0 proofRightLEQ)

|||Proof that a <= b implies (c * a) <= (c * b)
leqMultConstantLeft : {a : Nat} -> {b : Nat} -> (c : Nat) -> (LEQ a b) -> (LEQ (c * a) (c * b))
leqMultConstantLeft {a} {b} c (k ** proofEq) = ((c * k) ** proofFinalEq) where
	proofFinalEq = rewrite (sym (multDistributesOverPlusRight c a k)) in
				cong {f = (\n => c * n)} proofEq

|||Proof that a <= b implies (a * c) <= (b * c)
leqMultConstantRight : {a : Nat} -> {b : Nat} -> (c : Nat) -> (LEQ a b) -> (LEQ (a * c) (b * c))
leqMultConstantRight {a} {b} c (k ** proofEq) = ((k * c) ** proofFinalEq) where
	proofFinalEq = rewrite (sym (multDistributesOverPlusLeft a k c)) in
				cong {f = (\n => n * c)} proofEq

|||Proof that if a <= b, and c <= d, then (a * c) <= (b * d)
leqMultIsLEQ : {a : Nat} -> {b : Nat} -> {c : Nat} -> {d : Nat} ->
			(LEQ a b) -> (LEQ c d) -> (LEQ (a * c) (b * d))
leqMultIsLEQ {a = a0} {b = b0} {c = c0} {d = d0} proofLeftLEQ proofRightLEQ =
	leqTransitive (leqMultConstantRight {a = a0} {b = b0} c0 proofLeftLEQ) (leqMultConstantLeft {a = c0} {b = d0} b0 proofRightLEQ)

|||Proof that (c * a) <= (c * b) and c != 0 implies a <= b
leqDivConstantLeft : {a : Nat} -> {b : Nat} -> {c : Nat} -> (Not (c = Z)) ->
					(LEQ (c * a) (c * b)) -> (LEQ a b)
--To be proved

|||Proof that (a * c) <= (b * c) and c != 0 implies a <= b
leqDivConstantRight : {a : Nat} -> {b : Nat} -> {c : Nat} -> (Not (c = Z)) ->
					(LEQ (a * c) (b * c)) -> (LEQ a b)
--To be proved

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

|||Convert from LEQ to LTE
leqToLTE : {a : Nat} -> {b : Nat} -> (LEQ a b) -> (LTE a b)
leqToLTE {a = Z} {b} _ = LTEZero
leqToLTE {a = S m} {b = Z} proofLEQ = void(succNotLEQzero proofLEQ)
leqToLTE {a = S m} {b = S n} (k ** proofEq) = LTESucc (leqToLTE {a = m} {b = n} (k ** predEqual proofEq))

|||Convert from LTE to LEQ
lteToLEQ : {a : Nat} -> {b : Nat} -> (LTE a b) -> (LEQ a b)
lteToLEQ {a = Z} {b} _ = LEQZero
lteToLEQ {a = S m} {b = Z} proofLTE = void(succNotLTEzero proofLTE)
lteToLEQ {a = S m} {b = S n} (LTESucc proofLTE) = LEQSucc (lteToLEQ {a = m} {b = n} proofLTE)

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


import Data.Vect
import Data.Nat

my_filter : (a -> Bool) -> Vect n a -> (p ** Vect p a)
my_filter p [] = ( _ ** [] )
my_filter p (x :: xs) with (filter p xs)
  my_filter p (x :: xs) | ( _ ** xs' ) = if (p x) then ( _ ** x :: xs' ) else ( _ ** xs' )

foo : Int -> Int -> Bool
foo n m with (n + 1)
  foo _ m | 2 with (m + 1)
    foo _ _ | 2 | 3 = True
    foo _ _ | 2 | _ = False
  foo _ _ | _ = False

data Parity : Nat -> Type where
     Even : {n : _} -> Parity (n + n)
     Odd  : {n : _} ->Parity (S (n + n))

-- parity : (n : Nat) -> Parity n
-- parity Z = Even {n = Z}
-- parity (S Z) = Odd {n = Z}
-- parity (S (S k)) with (parity k)
--   parity (S (S (j + j))) | Even
--       = rewrite plusSuccRightSucc j j in Even {n = S j}
--   parity (S (S (S (j + j)))) | Odd
--       = rewrite plusSuccRightSucc j j in Odd {n = S j}

helpEven : (j : Nat) -> Parity (S j + S j) -> Parity (S (S (plus j j)))
helpEven j p = rewrite plusSuccRightSucc j j in p

helpOdd : (j : Nat) -> Parity (S (S (j + S j))) -> Parity (S (S (S (j + j))))
helpOdd j p = rewrite plusSuccRightSucc j j in p

parity : (n:Nat) -> Parity n
parity Z     = Even {n=Z}
parity (S Z) = Odd {n=Z}
parity (S (S k)) with (parity k)
  parity (S (S (j + j)))     | Even = helpEven j (Even {n = S j})
  parity (S (S (S (j + j)))) | Odd  = helpOdd j (Odd {n = S j})

natToBin : Nat -> List Bool
natToBin Z = Nil
natToBin k with (parity k)
   natToBin (j + j)     | Even = False :: natToBin j
   natToBin (S (j + j)) | Odd  = True  :: natToBin j


||| Implementation of ordering relations for `Nat`ural numbers
module Data.Nat.Order

import Control.Relation
import Data.Nat
import Data.Fun
import Data.Rel
import Decidable.Decidable
import Decidable.Equality

%default total

public export
zeroNeverGreater : Not (LTE (S n) Z)
zeroNeverGreater LTEZero     impossible
zeroNeverGreater (LTESucc _) impossible

public export
zeroAlwaysSmaller : {n : Nat} -> LTE Z n
zeroAlwaysSmaller = LTEZero

public export
ltesuccinjective : {0 n, m : Nat} -> Not (LTE n m) -> Not (LTE (S n) (S m))
ltesuccinjective disprf (LTESucc nLTEm) = void (disprf nLTEm)
ltesuccinjective disprf LTEZero         impossible

public export
decideLTE : (n : Nat) -> (m : Nat) -> Dec (LTE n m)
decideLTE    Z      y  = Yes LTEZero
decideLTE (S x)     Z  = No  zeroNeverGreater
decideLTE (S x)   (S y) with (decEq (S x) (S y))
  decideLTE (S x)   (S y) | Yes eq      = rewrite eq in Yes (reflexive {rel = LTE})
  decideLTE (S x)   (S y) | No _ with (decideLTE x y)
    decideLTE (S x)   (S y) | No _   | Yes nLTEm = Yes (LTESucc nLTEm)
    decideLTE (S x)   (S y) | No _   | No  nGTm  = No (ltesuccinjective nGTm)

public export
Decidable 2 [Nat,Nat] LTE where
  decide = decideLTE

public export
Decidable 2 [Nat,Nat] LT where
  decide m = decideLTE (S m)

public export
lte : (m : Nat) -> (n : Nat) -> Dec (LTE m n)
lte m n = decide {ts = [Nat,Nat]} {p = LTE} m n

public export
shift : (m : Nat) -> (n : Nat) -> LTE m n -> LTE (S m) (S n)
shift m n mLTEn = LTESucc mLTEn