> module Vect.Properties

> import Data.Vect
> import Data.Vect.Quantifiers
> import Data.Fin

> import Vect.Operations
> import Sigma.Sigma
> import Sigma.Operations
> import Pairs.Operations
> import Exists.Operations
> import Decidable.Predicates
> import Rel.TotalPreorder
> import Rel.TotalPreorderOperations
> import Nat.LTProperties
> import Fin.Properties
> import Fun.Operations

> %default total
> %access public export
> %auto_implicits on

> {-
> implementation Uninhabited (Elem {a} x Nil) where
>   uninhabited Here impossible
>   uninhabited (There p) impossible
> -}

Injectivity (of |flip index|):

> ||| Injectivity (one direction)
> Injective1 : Vect n t -> Type
> Injective1 {n} xs = (i : Fin n) -> (j : Fin n) -> index i xs = index j xs -> i = j

> ||| Injectivity (the other direction)
> Injective2 : Vect n t -> Type
> Injective2 {n} xs = (i : Fin n) -> (j : Fin n) -> Not (i = j) -> Not (index i xs = index j xs)

> ||| Injective1 implies Injective2
> injectiveLemma : (xs : Vect n t) -> Injective1 xs -> Injective2 xs
> injectiveLemma xs i1xs i j contra = contra . (i1xs i j)
> %freeze injectiveLemma

> ||| Tail preserves injectivity (one direction)
> injectiveTailLemma1 : (xs : Vect (S n) t) -> Injective1 xs -> Injective1 (tail xs)
> injectiveTailLemma1  Nil      p _ _ _ impossible
> injectiveTailLemma1 (x :: xs) p i j q = FSInjective i j (p (FS i) (FS j) q') where
>   q' : index (FS i) (x :: xs) = index (FS j) (x :: xs)
>   q' = q
> %freeze injectiveTailLemma1

> ||| Tail preserves injectivity (the other direction)
> injectiveTailLemma2 : (xs : Vect (S n) t) -> Injective2 xs -> Injective2 (tail xs)
> injectiveTailLemma2  Nil      p _ _ _ impossible
> injectiveTailLemma2 (x :: xs) p i j q = p (FS i) (FS j) (fsInjective2 i j q)
> %freeze injectiveTailLemma2

Indexing and lookup

> |||
> indexLemma : (k : Fin n) -> (xs : Vect n t) -> Elem (index k xs) xs
> indexLemma {n = Z}       k   Nil      = absurd k
> indexLemma {n = S m}  FZ    (x :: xs) = Here
> indexLemma {n = S m} (FS k) (x :: xs) = There (indexLemma k xs)
> %freeze indexLemma

> |||
> indexLookupLemma : (x : alpha) ->
>                    (xs : Vect n alpha) ->
>                    (prf : Elem x xs) ->
>                    index (lookup x xs prf) xs = x
> indexLookupLemma x  Nil        prf        = absurd prf
> indexLookupLemma x (x :: xs)   Here       = Refl
> indexLookupLemma x (x' :: xs) (There prf) =
>   let ih = indexLookupLemma x xs prf in rewrite ih in Refl
> {-
> indexLookupLemma x (x' :: xs) (There prf) = trans s1 (trans s2 s3) where
>   s1 : index (lookup x (x' :: xs) (There prf)) (x' :: xs)
>        =
>        index (FS (lookup x xs prf)) (x' :: xs)
>   s1 = Refl
>   s2 : index (FS (lookup x xs prf)) (x' :: xs)
>        =
>        index (lookup x xs prf) xs
>   s2 = Refl
>   s3 : index (lookup x xs prf) xs
>        =
>        x
>   s3 = indexLookupLemma x xs prf
> -}
> %freeze indexLookupLemma

> |||
> lookupIndexLemma : (k : Fin n) ->
>                    (xs : Vect n t) ->
>                    (p : Injective2 xs) ->
>                    (q : Elem (index k xs) xs) ->
>                    lookup (index k xs) xs q = k
> lookupIndexLemma  FZ     Nil      _  _        impossible
> lookupIndexLemma  FZ    (x :: xs) p  Here     = Refl
> lookupIndexLemma  FZ    (x :: xs) p (There q) = s5 where
>   s1 : Not (FZ = lookup x (x :: xs) (There q))
>   s1 Refl impossible
>   s2 : index FZ (x :: xs) = x
>   s2 = Refl
>   s3 : index (lookup x (x :: xs) (There q)) (x :: xs) = x
>   s3 = indexLookupLemma x (x :: xs) (There q)
>   s4 : index FZ (x :: xs) = index (lookup x (x :: xs) (There q)) (x :: xs)
>   s4 = trans s2 (sym s3)
>   s5 : lookup (index FZ (x :: xs)) (x :: xs) (There q) = FZ
>   s5 = void ((p FZ (lookup x (x :: xs) (There q)) s1) s4)
> lookupIndexLemma (FS k)  Nil      _  _        impossible
> lookupIndexLemma (FS k) (_ :: xs) p  Here     = s5 where
>   s1 : Not (FZ = FS k)
>   s1 Refl impossible
>   s2 : index FZ ((index k xs) :: xs) = index (FS k) ((index k xs) :: xs)
>   s2 = Refl
>   s5 : lookup (index (FS k) ((index k xs) :: xs)) ((index k xs) :: xs) Here = (FS k)
>   s5 = void ((p FZ (FS k) s1) s2)
> lookupIndexLemma (FS k) (x :: xs) p (There q) =
>   let p' = injectiveTailLemma2 (x :: xs) p in
>   let ih = lookupIndexLemma k xs p' q in
>   rewrite ih in Refl
> %freeze lookupIndexLemma

> |||
> -- indexToVectLemma : {A : Type} -> (f : Fin n -> A) -> (k : Fin n) -> f k = index k (toVect f)

Membership, quantifiers:

> ||| Membership => non emptyness
> elemLemma : {A : Type} -> {n : Nat} ->
>             (a : A) -> (as : Vect n A) ->
>             Elem a as -> LT Z n
> elemLemma {n = Z}   a Nil p = absurd p
> elemLemma {n = S m} a as  p = ltZS m
> %freeze elemLemma

> |||
> AnySigmaLemma : {A : Type} -> {P : A -> Type} -> {as : Vect n A} ->
>                 Any P as -> Sigma A P
> AnySigmaLemma (Here px) = MkSigma _ px
> AnySigmaLemma (There prf) = AnySigmaLemma prf

> |||
> AnyExistsLemma : {A : Type} -> {P : A -> Type} -> {as : Vect n A} ->
>                  Any P as -> Exists P
> AnyExistsLemma (Here px) = Evidence _ px
> AnyExistsLemma (There prf) = AnyExistsLemma prf

> ElemAnyLemma : {A : Type} -> {P : A -> Type} -> P a -> Elem a as -> Any P as
> ElemAnyLemma p Here = Here p
> ElemAnyLemma p (There e) = There (ElemAnyLemma p e)
> %freeze ElemAnyLemma

> |||
> decAny : {A : Type} -> {P : A -> Type} -> Dec1 P -> Dec1 (Any P)
> decAny d1P = any d1P
> %freeze decAny

> elemNotHeadTail : Elem a (a' :: as) -> Not (a = a') -> Elem a as
> elemNotHeadTail  Here     q = void (q Refl)
> elemNotHeadTail (There p) _ = p

> decElemTailDecElemHeadTail : {A : Type} -> {n : Nat} -> (DecEq A) =>
>                              (a : A) -> (a' : A) -> (as : Vect n A)->
>                              Dec (Elem a as) -> Dec (Elem a (a' :: as))
> decElemTailDecElemHeadTail a a' as (Yes p) = Yes (There p)
> decElemTailDecElemHeadTail a a' as (No  p) with (decEq a a')
>   | Yes q = Yes (replace {P = \ X => Elem a (X :: as)} q Here)
>   |  No q =  No (\ prf => p (elemNotHeadTail prf q))

> |||
> decElem : {A : Type} -> {n : Nat} -> (DecEq A) =>
>           (a : A) -> (as : Vect n A)-> Dec (Elem a as)
> decElem a Nil = No absurd
> decElem a (a' :: as) with (decEq a a')
>   | Yes p  = Yes (replace {P = \ X => Elem a (X :: as)} p Here)
>   | No  p  = decElemTailDecElemHeadTail a a' as (decElem a as)

Container monad properties

> |||
> mapLemma : {A, B : Type} -> (as : Vect n A) -> (f : A -> B) ->
>            (a : A) -> Elem a as -> Elem (f a) (map f as)
> mapLemma  Nil      f a aeas = absurd aeas
> mapLemma (a :: as) f a   Here       = Here
> mapLemma (a :: as) f a' (There prf) = There (mapLemma as f a' prf)
> %freeze mapLemma

> |||
> mapIdfLemma : {A, B : Type} -> (as : Vect n A) -> (f : A -> B) ->
>               (ab : (A,B)) -> Elem ab (map (pair (Prelude.Basics.id,f)) as) ->
>               f (fst ab) = snd ab
> mapIdfLemma  Nil      f  ab     p        = absurd p
> mapIdfLemma (a :: as) f (a, _)  Here     = Refl
> mapIdfLemma (a :: as) f  ab    (There p) = mapIdfLemma as f ab p
> %freeze mapIdfLemma

Filtering

> ||| |filter| preserves membership
> filterLemma : {A : Type} ->
>               {P : A -> Type} ->
>               (d1P : Dec1 P) ->
>               (a : A) ->
>               (as : Vect n A) ->
>               Elem a as ->
>               (p : P a) ->
>               Elem a (getProof (filter d1P as))
> filterLemma d1P a   Nil       prf  p = absurd prf
> filterLemma d1P a1 (a1 :: as) Here p with (filter d1P as)
>   | (MkSigma n as') with (d1P a1)
>     | (Yes _) = Here {x = a1} {xs = as'}
>     | (No  contra) = void (contra p)
> filterLemma {A} {P} d1P a1 (a2 :: as) (There prf) p with (filter d1P as) proof itsEqual
>   | (MkSigma n as') with (d1P a2)
>     | (Yes _) = -- There {x = a1} {xs = as'} {y = a2} (filterLemma d1P a1 as prf p)
>                 There {x = a1} {xs = as'} {y = a2} $
>                   replace {P = \rec => Elem a1 (getProof rec)} (sym itsEqual) $
>                     filterLemma {A} {P} d1P a1 as prf p
>     | (No  _) = -- filterLemma {A} {P} d1P a1 as prf p
>                 replace {P = \rec => Elem a1 (getProof rec)} (sym itsEqual) $
>                   filterLemma {P = P} d1P a1 as prf p
> %freeze filterLemma

> ||| |filterTagSigma| preserves membership
> filterTagSigmaLemma : {A : Type} ->
>                       {P : A -> Type} ->
>                       {n : Nat} ->
>                       (d1P : Dec1 P) ->
>                       (a : A) ->
>                       (as : Vect n A) ->
>                       Elem a as ->
>                       (p : P a) ->
>                       Elem a (map Sigma.Operations.outl (Sigma.Operations.outr (filterTagSigma d1P as)))
> filterTagSigmaLemma d1P a   Nil       prf  p = absurd prf
> filterTagSigmaLemma d1P a1 (a1 :: as) Here p with (filterTagSigma d1P as)
>   | (MkSigma n aps') with (d1P a1)
>     | (Yes _) = Here {x = a1} {xs = map Sigma.Operations.outl aps'}
>     | (No  contra) = void (contra p)
> filterTagSigmaLemma d1P a1 (a2 :: as) (There prf) p with (filterTagSigma d1P as) proof itsEqual
>   | (MkSigma n aps') with (d1P a2)
>     | (Yes _) = -- There {x = a1} {xs = map Sigma.Operations.outl aps'} {y = a2} (filterTagSigmaLemma d1P a1 as prf p)
>                 There {x = a1} {xs = map Sigma.Operations.outl aps'} {y = a2} $
>                   replace {P = \rec => Elem a1 (map Sigma.Operations.outl (getProof rec))} (sym itsEqual) $
>                     filterTagSigmaLemma d1P a1 as prf p
>     | (No  _) = -- filterTagSigmaLemma d1P a1 as prf p
>                 replace {P = \rec => Elem a1 (map Sigma.Operations.outl (getProof rec))} (sym itsEqual) $
>                   filterTagSigmaLemma d1P a1 as prf p
> %freeze filterTagSigmaLemma

> ||| |filterTagExists| preserves membership
> filterTagExistsLemma : {A : Type} ->
>                        {P : A -> Type} ->
>                        {n : Nat} ->
>                        (d1P : Dec1 P) ->
>                        (a : A) ->
>                        (as : Vect n A) ->
>                        Elem a as ->
>                        (p : P a) ->
>                        Elem a (map Exists.getWitness (Sigma.getProof (filterTagExists d1P as)))
> filterTagExistsLemma d1P a   Nil       prf  p = absurd prf
> filterTagExistsLemma d1P a1 (a1 :: as) Here p with (filterTagExists d1P as)
>   | (MkSigma n aps') with (d1P a1)
>     | (Yes _) = Here {x = a1} {xs = map getWitness aps'}
>     | (No  contra) = void (contra p)
> filterTagExistsLemma d1P a1 (a2 :: as) (There prf) p with (filterTagExists d1P as) proof itsEqual
>   | (MkSigma n aps') with (d1P a2)
>     | (Yes _) = -- There {x = a1} {xs = map getWitness aps'} {y = a2} (filterTagExistsLemma d1P a1 as prf p)
>                 There {x = a1} {xs = map getWitness aps'} {y = a2} $
>                   replace {P = \rec => Elem a1 (map Exists.getWitness (getProof rec))} (sym itsEqual) $
>                     filterTagExistsLemma d1P a1 as prf p
>     | (No  _) = -- filterTagExistsLemma d1P a1 as prf p
>                 replace {P = \rec => Elem a1 (map Exists.getWitness (getProof rec))} (sym itsEqual) $
>                   filterTagExistsLemma d1P a1 as prf p
> %freeze filterTagExistsLemma

> ||| |filterTagSubset| preserves membership
> filterTagSubsetLemma : {A : Type} ->
>                        {P : A -> Type} ->
>                        {n : Nat} ->
>                        (d1P : Dec1 P) ->
>                        (a : A) ->
>                        (as : Vect n A) ->
>                        Elem a as ->
>                        (p : P a) ->
>                        Elem a (map Subset.getWitness (Sigma.getProof (filterTagSubset d1P as)))
> filterTagSubsetLemma d1P a   Nil       prf  p = absurd prf
> filterTagSubsetLemma d1P a1 (a1 :: as) Here p with (filterTagSubset d1P as)
>   | (MkSigma n aps') with (d1P a1)
>     | (Yes _) = Here {x = a1} {xs = map getWitness aps'}
>     | (No  contra) = void (contra p)
> filterTagSubsetLemma d1P a1 (a2 :: as) (There prf) p with (filterTagSubset d1P as) proof itsEqual
>   | (MkSigma n aps') with (d1P a2)
>     | (Yes _) = -- There {x = a1} {xs = map getWitness aps'} {y = a2} (filterTagLemma d1P a1 as prf p)
>                 There {x = a1} {xs = map getWitness aps'} {y = a2} $
>                   replace {P = \rec => Elem a1 (map Subset.getWitness (getProof rec))} (sym itsEqual) $
>                     filterTagSubsetLemma d1P a1 as prf p
>     | (No  _) = -- filterTagLemma d1P a1 as prf p
>                 replace {P = \rec => Elem a1 (map Subset.getWitness (getProof rec))} (sym itsEqual) $
>                   filterTagSubsetLemma d1P a1 as prf p
> %freeze filterTagSubsetLemma

> {- Just commented out for testing
> ||| |filter| preserves injectivity
> injectiveFilterLemma : {A : Type} ->
>                        {P : A -> Type} ->
>                        {n : Nat} ->
>                        (dP : Dec1 P) ->
>                        (as : Vect n A) ->
>                        Injective1 as ->
>                        Injective1 (getProof (filter dP as))
> -}
> {-
>     = s0 where
>   n'  : Nat
>   n'  = getWitness (filter dP (a :: as))
>   as' : Vect n' A
>   as' = getProof (filter dP (a :: as))
>   s0 : Injective1 (getProof (filter dP (a :: as)))
>   s0 i j q with (filter dP as)
>     | (n' ** as') with (dP a)
>       | (Yes _) = ?liko
>       | (No  _) = s5 where
>         s5 : i = j
>         s5 = ?lala -- injectiveFilterLemma dP as (injectiveTailLemma1 (a :: as) iaas) i j q
> -}

> {-
> ||| |filterTagSigma| preserves injectivity
> injectiveFilterTagSigmaLemma : {A : Type} ->
>                                {P : A -> Type} ->
>                                (dP : Dec1 P) ->
>                                (as : Vect n A) ->
>                                Injective1 as ->
>                                Injective1 (getProof (filterTagSigma d1P as))
> injectiveFilterTagSigmaLemma {n = Z}   dP Nil i1as i j p = absurd i

> ||| |filterTagSubset| preserves injectivity
> injectiveFilterTagSubsetLemma : {A : Type} ->
>                                 {P : A -> Type} ->
>                                 (dP : Dec1 P) ->
>                                 (as : Vect n A) ->
>                                 Injective1 as ->
>                                 Injective1 (getProof (filterTagSubset d1P as))
> injectiveFilterTagSubsetLemma {n = Z}   dP Nil i1as i j p = absurd i
> -}

Max and argmax

> |||
> maxLemma : {A : Type} -> {R : A -> A -> Type} ->
>            (tp : TotalPreorder R) ->
>            (a : A) -> (as : Vect n A) -> (p : LT Z n) -> a `Elem` as ->
>            R a (max tp as p)
> maxLemma {R} {n = Z}       tp a        Nil          p  _          = absurd p
> maxLemma {R} {n = S Z}     tp a (a  :: Nil)         _  Here       = reflexive tp a
> maxLemma {R} {n = S Z}     tp a (a' :: Nil)         _ (There prf) = absurd prf
> maxLemma {R} {n = S (S m)} tp a (a :: (a'' :: as))  _  Here with (argmaxMax tp (a'' :: as) (ltZS m))
>   | (k, max) with (totalPre tp a max)
>     | (Left  p) = p
>     | (Right _) = reflexive tp a
> maxLemma {R} {n = S (S m)} tp a (a' :: (a'' :: as)) _ (There prf) with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max) with (totalPre tp a' max)
>     | (Left  _) = replace {P = \rec => R a (snd rec)}
>                           (sym itsEqual)
>                           (maxLemma {n = S m} tp a (a'' :: as) (ltZS m) prf)
>     | (Right p) = s3 where
>       s1 : R a (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m)))
>       s1 = maxLemma {n = S m} tp a (a'' :: as) (ltZS m) prf
>       s2 : R (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m))) a'
>       s2 = replace {P = \rec => R (snd rec) a'} itsEqual p
>       s3 : R a a'
>       s3 = transitive tp a (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m))) a' s1 s2
> %freeze maxLemma

> |||
> argmaxLemma : {A : Type} -> {R : A -> A -> Type} ->
>               (tp : TotalPreorder R) ->
>               (as : Vect n A) -> (p : LT Z n) ->
>               index (argmax tp as p) as = max tp as p
> argmaxLemma {n = Z}       tp  Nil              p = absurd p
> argmaxLemma {n = S Z}     tp (a :: Nil)        p = Refl
> argmaxLemma {n = S (S m)} tp (a' :: (a'' :: as)) p with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max') with (totalPre tp a' max')
>     | (Left   _) = assert_total (replace {P = \rec => Data.Vect.index (fst rec) (a'' :: as) = snd rec}
>                                          (sym itsEqual)
>                                          (argmaxLemma tp (a'' :: as) (ltZS m)))
>     | (Right  _) = Refl
> %freeze argmaxLemma

> |||
> maxElemLemma : {A : Type} -> {R : A -> A -> Type} ->
>                (tp : TotalPreorder R) ->
>                (as : Vect n A) -> (p : LT Z n) ->
>                Elem (max tp as p) as
> maxElemLemma {n = Z}       tp  Nil                p = absurd p
> maxElemLemma {n = S Z}     tp (a :: Nil)          p = Here
> maxElemLemma {n = S (S m)} tp (a' :: (a'' :: as)) p with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max) with (totalPre tp a' max)
>     | (Left   _) = assert_total (replace {P = \ ZUZU => Elem ZUZU (a' :: (a'' :: as))}
>                                          (cong {f = Prelude.Basics.snd} (sym itsEqual))
>                                          (There {y = a'} (maxElemLemma tp (a'' :: as) (ltZS m))))
>     | (Right  _) = Here
> %freeze maxElemLemma

> {-

> |||
> maxLemma : {A : Type} ->
>            (tp : TotalPreorder A) ->
>            (a : A) -> (as : Vect n A) -> (p : LT Z n) -> a `Elem` as ->
>            R tp a (max tp as p)
> maxLemma {n = Z}       tp a        Nil          p  _          = absurd p
> maxLemma {n = S Z}     tp a (a  :: Nil)         _  Here       = reflexive tp a
> maxLemma {n = S Z}     tp a (a' :: Nil)         _ (There prf) = absurd prf
> maxLemma {n = S (S m)} tp a (a :: (a'' :: as))  _  Here with (argmaxMax tp (a'' :: as) (ltZS m))
>   | (k, max) with (totalPre tp a max)
>     | (Left  p) = p
>     | (Right _) = reflexive tp a
> maxLemma {n = S (S m)} tp a (a' :: (a'' :: as)) _ (There prf) with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max) with (totalPre tp a' max)
>     | (Left  _) = replace {P = \rec => R tp a (snd rec)}
>                           (sym itsEqual)
>                           (maxLemma {n = S m} tp a (a'' :: as) (ltZS m) prf)
>     | (Right p) = s3 where
>       s1 : R tp a (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m)))
>       s1 = maxLemma {n = S m} tp a (a'' :: as) (ltZS m) prf
>       s2 : R tp (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m))) a'
>       s2 = replace {P = \rec => R tp (snd rec) a'} itsEqual p
>       s3 : R tp a a'
>       s3 = transitive tp a (snd (Vect.Operations.argmaxMax tp (a'' :: as) (ltZS m))) a' s1 s2
> %freeze maxLemma

> |||
> argmaxLemma : {A : Type} ->
>               (tp : TotalPreorder A) ->
>               (as : Vect n A) -> (p : LT Z n) ->
>               index (argmax tp as p) as = max tp as p
> argmaxLemma {n = Z}       tp  Nil              p = absurd p
> argmaxLemma {n = S Z}     tp (a :: Nil)        p = Refl
> argmaxLemma {n = S (S m)} tp (a' :: (a'' :: as)) p with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max') with (totalPre tp a' max')
>     | (Left   _) = assert_total (replace {P = \rec => Data.Vect.index (fst rec) (a'' :: as) = snd rec}
>                                          (sym itsEqual)
>                                          (argmaxLemma tp (a'' :: as) (ltZS m)))
>     | (Right  _) = Refl
> %freeze argmaxLemma

> |||
> maxElemLemma : {A : Type} ->
>                (tp : TotalPreorder A) ->
>                (as : Vect n A) -> (p : LT Z n) ->
>                Elem (max tp as p) as
> maxElemLemma {n = Z}       tp  Nil                p = absurd p
> maxElemLemma {n = S Z}     tp (a :: Nil)          p = Here
> maxElemLemma {n = S (S m)} tp (a' :: (a'' :: as)) p with (argmaxMax tp (a'' :: as) (ltZS m)) proof itsEqual
>   | (k, max) with (totalPre tp a' max)
>     | (Left   _) = assert_total (replace {P = \ ZUZU => Elem ZUZU (a' :: (a'' :: as))}
>                                          (cong {f = Prelude.Basics.snd} (sym itsEqual))
>                                          (There {y = a'} (maxElemLemma tp (a'' :: as) (ltZS m))))
> {-
>     | (Left   _) = s4 where
>       s1 : Elem (snd (argmaxMax tp (a'' :: as) (ltZS m))) (a' :: (a'' :: as))
>       s1 = There {y = a'} (maxElemLemma tp (a'' :: as) (ltZS m))
>       s2 : argmaxMax tp (a'' :: as) (ltZS m) = (k, max)
>       s2 = sym itsEqual
>       s3 : snd (argmaxMax tp (a'' :: as) (ltZS m)) = max
>       s3 = cong {f = Prelude.Basics.snd} s2
>       s4 : Elem max (a' :: (a'' :: as))
>       s4 = replace {P = \ ZUZU => Elem ZUZU (a' :: (a'' :: as))} s3 s1
> -}
>     | (Right  _) = Here
> %freeze maxElemLemma

> -}

> {-

> |||
> maxLemma : {A : Type} -> {TO : A -> A -> Type} -> Preordered A TO =>
>            (a : A) -> (as : Vect (S n) A) -> a `Elem` as ->
>            TO a (max as)
> maxLemma {TO} {n = Z}   a (a :: Nil)          Here       = reflexive a
> maxLemma {TO} {n = Z}   a (a' :: Nil)        (There prf) = absurd prf
> maxLemma {TO} {n = S m} a (a :: (a'' :: as))  Here with (preorder a (max (a'' :: as)))
>   | (Left  p) = p
>   | (Right _) = reflexive a
> maxLemma {TO} {n = S m} a (a' :: (a'' :: as)) (There prf) with (preorder a' (max (a'' :: as)))
>   | (Left  _) = maxLemma a (a'' :: as) prf
>   | (Right p) = s3 where
>     s1 : TO a (max (a'' :: as))
>     s1 = maxLemma a (a'' :: as) prf
>     s2 : TO (max (a'' :: as)) a'
>     s2 = p
>     s3 : TO a a'
>     s3 = transitive a (max (a'' :: as)) a' s1 s2

> |||
> argmaxLemma : {A : Type} -> {TO : A -> A -> Type} -> Preordered A TO =>
>               (as : Vect (S n) A) ->
>               index (argmax as) as = max as
> argmaxLemma {TO} {n = Z}   (a :: Nil) = Refl
> argmaxLemma {TO} {n = S m} (a :: (a' :: as)) with (preorder a (max (a' :: as)))
>   | (Left   _) = argmaxLemma (a' :: as)
>   | (Right  _) = Refl

> -}

> {-

> |||
> maxLemma : {A, F : Type} -> {TO : F -> F -> Type} -> Ordered F TO =>
>            (af : (A,F)) -> (afs : Vect (S n) (A,F)) -> af `Elem` afs ->
>            TO (snd af) (max afs)
> maxLemma {A} {F} {TO} {n = Z}   af (af :: Nil)   Here       = reflexive (snd af)
> maxLemma {A} {F} {TO} {n = Z}   af (af' :: Nil) (There prf) = absurd prf
> maxLemma {A} {F} {TO} {n = S m} af (af :: (af'' :: afs)) Here with (order (snd af) (snd (argmaxMax (af'' :: afs))))
>   | (Left  p) = p
>   | (Right _) = reflexive (snd af)
> maxLemma {A} {F} {TO} {n = S m} af (af' :: (af'' :: afs)) (There prf) with (order (snd af') (snd (argmaxMax (af'' :: afs))))
>   | (Left  _) = maxLemma {A} {F} {TO} {n = m} af (af'' :: afs) prf
>   | (Right p) = s3 where
>     s1 : TO (snd af) (max (af'' :: afs))
>     s1 = maxLemma {A} {F} {TO} {n = m} af (af'' :: afs) prf
>     s2 : TO (max (af'' :: afs)) (snd af')
>     s2 = p
>     s3 : TO (snd af) (snd af')
>     s3 = transitive (snd af) (VectOperations.max (af'' :: afs)) (snd af') s1 s2

> -}


||| Position and geometry of Hexagonal grid system
||| https://www.redblobgames.com/grids/hexagons/
module Bautzen.Pos

import Data.Fin
import Data.Nat
import Data.Nat.DivMod
import Data.Nat.Parity
import Decidable.Equality
import Data.List

import public Data.ZZ
import public Data.ZZ.Extra

%default total

-- Positions & Map

||| A position/hex of the game board encoded as a pair of `Fin`s.
||| The type is indexed by the bounds on the number of rows and cols
||| which are basically dependent on the size of the terrain.
public export
data Loc : (c : Nat) -> (r : Nat) -> Type where
  Hex : (col : Fin c) -> (row : Fin r) -> Loc c r

colInjective : Hex c r = Hex col r' -> c = col
colInjective Refl = Refl

rowInjective : Hex c r = Hex c' row -> r = row
rowInjective Refl = Refl

public export
Eq (Loc c r) where
  (==) (Hex col row) (Hex col' row') = col == col' && row == row'

public export
DecEq (Loc c r) where
  decEq (Hex col row) (Hex k j ) with (decEq col k)
    decEq (Hex col row) (Hex k j ) | No contra = No $ \ h => contra (colInjective h)
    decEq (Hex col row) (Hex k j ) | Yes p with (decEq row j)
      decEq (Hex col row) (Hex k j ) | Yes p | No contra = No $ \ h => contra (rowInjective h)
      decEq (Hex col row) (Hex col row) | Yes Refl | Yes Refl = Yes Refl

public export
Show (Loc c r) where
  show (Hex c r) = "Hex " ++ show2Digits (finToNat c) ++ " " ++ show2Digits (finToNat r)
    where
      show2Digits : Nat -> String
      show2Digits n =
        if n <= 9
        then "0" ++ show n
        else show n

public export
Ord (Loc c r) where
  compare (Hex col row) (Hex col' row') =
    case compare col col' of
      LT => LT
      EQ => compare row row'
      GT => GT

||| Cube coordinates.
||| Cube coordinates stem from the observation a 2-D hexagonal grid is equivalent
||| to a diagonal "slice" of a 3-D cubic grid. Using cubic coordinates makes it
||| much easier to compute geometric values.
||| We only store the `x` (column) and `z` (depth) coordinates instead
||| of a triple as the `y` dimension can be simply recovered as `-x -z`.
public export
data Cube : Type where
  MkCube : (x : ZZ) -> (z : ZZ) -> Cube

||| Compute the L1 distance between 2 `Cube`s
||| see [Red Blob Games](https://www.redblobgames.com/grids/hexagons/#distances-cube) page
||| for details on the (pretty cool) algorithm.
public export
cubeDistance : Cube -> Cube -> Nat
cubeDistance (MkCube x z) (MkCube x' z') =
  let y  = negate x - z
      y' = negate x' - z'
  in max (max (absZ (x - x')) (absZ (y - y'))) (absZ (z - z'))

public export
posToCube : Loc c r -> Cube
posToCube (Hex col row) =
  let x = Pos (finToNat col)
      sign : ZZ
      sign = if odd (finToNat col) then 1 else 0
      z : ZZ
      z = Pos (finToNat row) - divZZNZ (x - sign) 2 {z = PosSIsNotZ }
  in MkCube x z

mutual
  public export
  data Distance : Type where
    IsDistance : (d : Nat) -> Distance

  export
  Num Distance where
    fromInteger = IsDistance . fromInteger
    IsDistance d1 * IsDistance d2 = IsDistance $ d1 * d2 -- meaningless
    IsDistance d1 + IsDistance d2 = IsDistance $ d1 + d2

  public export
  distance : {c : Nat} -> {r : Nat} -> Loc c r -> Loc c r -> Distance
  distance x y = IsDistance (cubeDistance (posToCube x) (posToCube y))

public export
data Mvmt : Type where
  Dec : Mvmt
  Neut : Mvmt
  Inc : Mvmt

public export
succNotLTEZ : Not (LTE (S m) Z)
succNotLTEZ LTEZero impossible

public export
fromLTESucc : LTE (S m) (S n) -> LTE m n
fromLTESucc (LTESucc x) = x

public export
lteSuccR : LTE n m -> LTE n (S m)
lteSuccR LTEZero     = LTEZero
lteSuccR (LTESucc x) = LTESucc (lteSuccR x)

public export
lteSuccL : LTE (S n) m -> LTE n m
lteSuccL (LTESucc x) = lteSuccR x

public export
isLte : (m, n : Nat) -> Dec (LTE m n)
isLte Z n = Yes LTEZero
isLte (S k) Z = No succNotLTEZ
isLte (S k) (S j)
    = case isLte k j of
           No contra => No (contra . fromLTESucc)
           Yes prf => Yes (LTESucc prf)

public export
shiftLoc : {bound : Nat} -> (x : Nat) -> Mvmt -> Maybe (Fin bound)
shiftLoc Z Dec = Nothing
shiftLoc (S k) Dec = natToFin k bound
shiftLoc x Neut = natToFin x bound
shiftLoc x Inc {bound} = natToFin (S x) bound

public export
makeLoc : {c : Nat} -> {r : Nat} -> (x : Nat) -> (y : Nat) -> (Mvmt, Mvmt) -> Maybe (Loc c r)
makeLoc col row (a, b) = do
  c' <- shiftLoc col a
  r' <- shiftLoc row b
  pure $ Hex c' r'

public export
oddShifts : List (Mvmt, Mvmt)
oddShifts = [ (Dec, Neut)
            , (Neut, Dec)
            , (Inc, Neut)
            , (Inc, Inc)
            , (Neut, Inc)
            , (Dec, Inc)
            ]

public export
evenShifts : List (Mvmt, Mvmt)
evenShifts = [ (Dec, Dec)
            , (Neut, Dec)
            , (Inc, Dec)
            , (Inc, Neut)
            , (Neut, Inc)
            , (Dec, Neut)
            ]

neighbours' : {col : Nat} -> {row : Nat} -> (x : Nat) -> (y : Nat) -> List (Loc col row)
neighbours' x y with (x `divMod` (S Z))
  neighbours' x@(Z + (q * 2)) y     | (MkDivMod q Z _) = catMaybes $ map (makeLoc x y) evenShifts
  neighbours' x@((S r) + (q * 2)) y | (MkDivMod q (S r) _) = catMaybes $ map (makeLoc x y) oddShifts
  neighbours' (S (S r) + (q * 2)) _ | (MkDivMod q (S (S r)) LTEZero) impossible

||| Compute the neighbours of a given position
||| There are at most 6 neighbours, with side and corner hexes having of
||| course less.
public export
neighbours : {c : Nat} -> {r : Nat} -> (pos : Loc c r) -> List (Loc c r)
neighbours (Hex col row) = neighbours' (finToNat col) (finToNat row)

namespace LocTest

  neighbours1_test : (neighbours {c=22} {r=12} (Hex 3 3) = [ Hex 2 3, Hex 3 2, Hex 4 3, Hex 4 4, Hex 3 4, Hex 2 4] )
  neighbours1_test = Refl

  neighbours_test : (neighbours {c=22} {r=12} (Hex 2 2) = [ Hex 1 1, Hex 2 1, Hex 3 1, Hex 3 2, Hex 2 3, Hex 1 2] )
  neighbours_test = Refl

  distance_to_odd_neighbours_is_1 : map (distance (Hex 3 2)) (neighbours {c=22} {r=12} (Hex 3 2)) = [ 1, 1, 1, 1, 1, 1 ]
  distance_to_odd_neighbours_is_1 = Refl

  distance_to_even_neighbours_is_1 : map (distance (Hex 2 2)) (neighbours {c=22} {r=12} (Hex 2 2)) = [ 1, 1, 1, 1, 1, 1 ]
  distance_to_even_neighbours_is_1 = Refl

  distance_test : distance {c=22} {r=12} (Hex 3 2) (Hex 4 4) = 2
  distance_test = Refl


module Data.Functor.Representable

import Data.Vect
import Data.Vect.Extra

%default total

public export
interface Functor f => Representable f rep | f where
  index : f a -> rep -> a
  tabulate : (rep -> a) -> f a

public export
[Compose] Representable f rep => Representable g rep' => Representable (f . g) (rep, rep') using Functor.Compose where
  index x (i, j) = (x `index` i) `index` j
  tabulate = map tabulate . tabulate . curry

public export
{n : Nat} -> Representable (Vect n) (Fin n) where
  index = flip index
  tabulate f = mapWithIndex (\i, () => f i) $ replicate n ()


> module Opt.Operations

> import Control.Isomorphism
> import Data.Fin
> import Data.Vect
> import Syntax.PreorderReasoning

> import Finite.Predicates
> import Finite.Operations
> import Finite.Properties
> import Rel.TotalPreorder
> import Rel.TotalPreorderOperations
> import Vect.Operations
> import Vect.Properties
> import Fun.Operations
> import Nat.LTProperties

> %default total

> %access public export

> |||
> argmaxMax : {A, B : Type} -> {R : B -> B -> Type} ->
>             TotalPreorder R ->
>             (fA : Finite A) ->
>             (ne : CardNotZ fA) ->
>             (f : A -> B) -> (A, B)
> argmaxMax {A} {B} tp fA nefA f = max (extendLeftLemma tp) abs ltZn where
>   n    : Nat
>   n    = card fA
>   ltZn : LT Z n
>   ltZn = notZisgtZ nefA
>   abs  : Vect n (A,B)
>   abs  = map (pair (id, f)) (toVect fA)

> |||
> max : {A, B : Type} -> {R : B -> B -> Type} ->
>       TotalPreorder R ->
>       (fA : Finite A) ->
>       (ne : CardNotZ fA) ->
>       (f : A -> B) -> B
> max tp fA nefA f = snd (argmaxMax tp fA nefA f)

> |||
> argmax : {A, B : Type} -> {R : B -> B -> Type} ->
>          TotalPreorder R ->
>          (fA : Finite A) ->
>          (ne : CardNotZ fA) ->
>          (f : A -> B) -> A
> argmax tp fA nefA f = fst (argmaxMax tp fA nefA f)

> |||
> maxSpec : {A, B : Type} -> {R : B -> B -> Type} ->
>           (tp : TotalPreorder R) ->
>           (fA : Finite A) ->
>           (nefA : CardNotZ fA) ->
>           (f : A -> B) ->
>           (a : A) -> R (f a) (max tp fA nefA f)
> maxSpec {A} {B} {R} tp fA nefA f a = s4 where
>   n    : Nat
>   n    = card fA
>   ltZn : LT Z n
>   ltZn = notZisgtZ nefA
>   abs  : Vect n (A,B)
>   abs  = map (pair (id, f)) (toVect fA)
>   s1   : Elem (a, f a) abs
>   s1   = mapLemma (toVect fA) (pair (id, f)) a (toVectComplete fA a)
>   s2   : (extendLeft R) (a, f a) (max (extendLeftLemma tp) abs ltZn)
>   s2   = maxLemma (extendLeftLemma tp) (a, f a) abs ltZn s1
>   s3   : R (f a) (snd (max (extendLeftLemma tp) abs ltZn))
>   s3   = s2
>   s4   : R (f a) (max tp fA nefA f)
>   s4   = s3

> |||
> argmaxSpec : {A, B : Type} -> {R : B -> B -> Type} ->
>              (tp : TotalPreorder R) ->
>              (fA : Finite A) ->
>              (nefA : CardNotZ fA) ->
>              (f : A -> B) ->
>              (max tp fA nefA f) = f (argmax tp fA nefA f)
> argmaxSpec {A} {B} tp fA nefA f = s3 where
>   ab : (A,B)
>   ab = argmaxMax tp fA nefA f
>   s1 : Elem ab (map (pair (Prelude.Basics.id, f)) (toVect fA))
>   s1 = maxElemLemma (extendLeftLemma tp) (map (pair (id, f)) (toVect fA)) (notZisgtZ nefA)
>   s2 : f (fst ab) = snd ab
>   s2 = mapIdfLemma (toVect fA) f ab s1
>   s3 : max tp fA nefA f = f (argmax tp fA nefA f)
>   s3 = sym s2

> {-

> argmaxMax : {A, B : Type} ->
>             TotalPreorder B ->
>             (fA : Finite A) ->
>             (ne : CardNotZ fA) ->
>             (f : A -> B) -> (A, B)
> argmaxMax {A} {B} tp fA nefA f = max (fromTotalPreorder2 tp) abs ltZn where
>   n    : Nat
>   n    = card fA
>   ltZn : LT Z n
>   ltZn = notZisgtZ nefA
>   abs  : Vect n (A,B)
>   abs  = map (pair (id, f)) (toVect fA)

> max : {A, B : Type} ->
>       TotalPreorder B ->
>       (fA : Finite A) ->
>       (ne : CardNotZ fA) ->
>       (f : A -> B) -> B
> max tp fA nefA f = snd (argmaxMax tp fA nefA f)

> argmax : {A, B : Type} ->
>          TotalPreorder B ->
>          (fA : Finite A) ->
>          (ne : CardNotZ fA) ->
>          (f : A -> B) -> A
> argmax tp fA nefA f = fst (argmaxMax tp fA nefA f)

> maxSpec : {A, B : Type} ->
>           (tp : TotalPreorder B) ->
>           (fA : Finite A) ->
>           (nefA : CardNotZ fA) ->
>           (f : A -> B) ->
>           (a : A) -> R tp (f a) (max tp fA nefA f)
> maxSpec {A} {B} tp fA nefA f a = s4 where
>   n    : Nat
>   n    = card fA
>   ltZn : LT Z n
>   ltZn = notZisgtZ nefA
>   abs  : Vect n (A,B)
>   abs  = map (pair (id, f)) (toVect fA)
>   s1   : Elem (a, f a) abs
>   s1   = mapLemma (toVect fA) (pair (id, f)) a (toVectComplete fA a)
>   s2   : (from2 (R tp)) (a, f a) (max (fromTotalPreorder2 tp) abs ltZn)
>   s2   = maxLemma (fromTotalPreorder2 tp) (a, f a) abs ltZn s1
>   s3   : R tp (f a) (snd (max (fromTotalPreorder2 tp) abs ltZn))
>   s3   = s2
>   s4   : R tp (f a) (max tp fA nefA f)
>   s4   = s3

> argmaxSpec : {A, B : Type} ->
>              (tp : TotalPreorder B) ->
>              (fA : Finite A) ->
>              (nefA : CardNotZ fA) ->
>              (f : A -> B) ->
>              (max tp fA nefA f) = f (argmax tp fA nefA f)
> argmaxSpec {A} {B} tp fA nefA f = s3 where
>   ab : (A,B)
>   ab = argmaxMax tp fA nefA f
>   s1 : Elem ab (map (pair (Prelude.Basics.id, f)) (toVect fA))
>   s1 = maxElemLemma (fromTotalPreorder2 tp) (map (pair (id, f)) (toVect fA)) (notZisgtZ nefA)
>   s2 : f (fst ab) = snd ab
>   s2 = mapIdfLemma (toVect fA) f ab s1
>   s3 : max tp fA nefA f = f (argmax tp fA nefA f)
>   s3 = sym s2

> {-

> argmaxMax : {A, B : Type} -> {TO : B -> B -> Type} ->
>             Preordered B TO =>
>             (fA : Finite A) -> (ne : CardNotZ fA) ->
>             (f : A -> B) -> (A,B)
> argmaxMax {A} {B} {TO} fA nefA f =
>   VectOperations.max {A = (A,B)} {TO = sndType TO} abs ltZn where
>     n    : Nat
>     n    = card fA
>     ltZn : LT Z n
>     ltZn = notZisgtZ nefA
>     abs  : Vect n (A,B)
>     abs  = map (pair (id, f)) (toVect fA)

> max : {A, B : Type} -> {TO : B -> B -> Type} ->
>       Preordered B TO =>
>       (fA : Finite A) -> (ne : CardNotZ fA) ->
>       (f : A -> B) -> B
> max fA nefA f = snd (argmaxMax fA nefA f)

> argmax : {A, B : Type} -> {TO : B -> B -> Type} ->
>          Preordered B TO =>
>          (fA : Finite A) -> (ne : CardNotZ fA) ->
>          (f : A -> B) -> A
> argmax fA nefA f = fst (argmaxMax fA nefA f)

> maxSpec : {A, B : Type} -> {TO : B -> B -> Type} ->
>           Preordered B TO =>
>           (fA : Finite A) -> (nefA : CardNotZ fA) ->
>           (f : A -> B) ->
>           (a : A) -> TO (f a) (max fA nefA f)
> maxSpec {A} {B} {TO} fA nefA f a = s4 where
>   n    : Nat
>   n    = card fA
>   ltZn : LT Z n
>   ltZn = notZisgtZ nefA
>   abs  : Vect n (A,B)
>   abs  = map (pair (id, f)) (toVect fA)
>   s1   : Elem (a, f a) abs
>   s1   = mapLemma (toVect fA) (pair (id, f)) a (toVectComplete fA a)
>   s2   : (sndType TO) (a, f a) (max abs ltZn)
>   s2   = maxLemma (a, f a) abs ltZn s1
>   s3   : TO (f a) (snd (max abs ltZn))
>   s3   = s2
>   s4   : TO (f a) (max fA nefA f)
>   s4   = s3

> argmaxSpec : {A, B : Type} -> {TO : B -> B -> Type} ->
>              Preordered B TO =>
>              (fA : Finite A) -> (nefA : CardNotZ fA) ->
>              (f : A -> B) ->
>              (max fA nefA f) = f (argmax fA nefA f)
> argmaxSpec {A} {B} fA nefA f = s3 where
>   ab : (A,B)
>   ab = argmaxMax fA nefA f
>   s1 : Elem ab (map (pair (id, f)) (toVect fA))
>   s1 = maxElemLemma (map (pair (id, f)) (toVect fA)) (notZisgtZ nefA)
>   s2 : f (fst ab) = snd ab
>   s2 = mapIdfLemma (toVect fA) f ab s1
>   s3 : max fA nefA f = f (argmax fA nefA f)
>   s3 = sym s2

> -}

> ---}


module Dec

import Basic
import Unit
import Void
import Coproduct

import Path
import General

public export
Dec : Type -> Type
Dec a = a + Neg a

public export
DecEq : Type -> Type
DecEq a = (x, y : a) -> Dec (x == y)


module Instances.Notation

import public Data.Vect
import public Data.Rel
import public Decidable.Decidable
import Common.Abbrev
import Common.Interfaces
import Specifications.DiscreteOrderedGroup
import Specifications.OrderedRing

%default total
%access public export

specifyMonoid : Ringops s => Type
specifyMonoid {s} = MonoidSpec {s} (+) Zero

specifyGroup : Ringops s => Type
specifyGroup {s} = GroupSpec {s} (+) Zero Ng

specifyPartialOrder : Decidable [s,s] leq => Type
specifyPartialOrder {leq} = PartialOrderSpec leq

specifyTotalOrder : Decidable [s,s] leq => Type
specifyTotalOrder {leq} = TotalOrderSpec leq

specifyPartiallyOrderedMagma : (Ringops s, Decidable [s,s] leq) => Type
specifyPartiallyOrderedMagma {leq} = PartiallyOrderedMagmaSpec (+) leq

specifyPartiallyOrderedGroup : (Ringops s, Decidable [s,s] leq) => Type
specifyPartiallyOrderedGroup {leq} = PartiallyOrderedGroupSpec (+) Zero Ng leq

specifyOrderedGroup : (Ringops s, Decidable [s,s] leq) => Type
specifyOrderedGroup {leq} = OrderedGroupSpec (+) Zero Ng leq

specifyDiscreteOrderedGroup : (Ringops s, Decidable [s,s] leq) => Type
specifyDiscreteOrderedGroup {leq} =
  DiscreteOrderedGroupSpec (+) Zero Ng leq One

specifyRing : Ringops s => Type
specifyRing {s} = RingSpec {s} (+) Zero Ng (*)

specifyPartiallyOrderedRing : (Ringops s, Decidable [s,s] leq) => Type
specifyPartiallyOrderedRing {leq} =
  PartiallyOrderedRingSpec (+) Zero Ng (*) leq

specifyOrderedRing : (Ringops s, Decidable [s,s] leq) => Type
specifyOrderedRing {leq} =
  OrderedRingSpec (+) Zero Ng (*) leq

specifyDiscreteOrderedRing : (Ringops s, Decidable [s,s] leq) => Type
specifyDiscreteOrderedRing {leq} =
  DiscreteOrderedRingSpec (+) Zero Ng (*) leq One


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

record MyDPair a p where
  constructor MkMyDPair
  dfst : a
  dsnd : p dfst

cons : t -> MyDPair Nat (\n => Vect n t) -> MyDPair Nat (\n => Vect n t)
cons val xs
    = { dfst := S (dfst xs),
        dsnd := val :: dsnd xs } xs

cons' : t -> MyDPair Nat (\n => Vect n t) -> MyDPair Nat (\n => Vect n t)
cons' val
    = { dfst $= S,
        dsnd $= (val ::) }

record Stats where
  constructor MkStats
  height : Int
  weight : Int

record Person where
  constructor MkPerson
  name : String
  age, shoesize : Int
  more : Stats

testPair : MyDPair Nat (\n => Vect n Int)
testPair = MkMyDPair _ [1,2,3,4,5]

testPerson : Person
testPerson = MkPerson "Fred" 1337 10 (MkStats 10 10)

grow : Person -> Person
grow = { more.height $= (+1) }


import Data.Vect

my_length : List a -> Nat
my_length [] = 0
my_length (x :: xs) = 1 + length xs

my_reverse : List a -> List a
my_reverse [] = []
my_reverse (x :: xs) = my_reverse xs ++ [x]

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

my_vmap : (a -> b) -> Vect n a -> Vect n b
my_vmap f [] = []
my_vmap f (x :: xs) = f x :: my_vmap f xs


import Data.Vect

data StackCmd : Type -> Nat -> Nat -> Type where
  Push : Integer -> StackCmd () height (S height)
  Pop : StackCmd Integer (S height) height
  Top : StackCmd Integer (S height) (S height)

  GetStr : StackCmd String height height
  PutStr : String -> StackCmd () height height

  Pure : ty -> StackCmd ty height height
  (>>=) : StackCmd a height1 height2 ->
          (a -> StackCmd b height2 height3) ->
          StackCmd b height1 height3

data StackIO : Nat -> Type where
   Do : StackCmd a height1 height2 ->
        (a -> Inf (StackIO height2)) -> StackIO height1

namespace StackDo
  (>>=) : StackCmd a height1 height2 ->
         (a -> Inf (StackIO height2)) -> StackIO height1
  (>>=) = Do

runStack : (stk: Vect inHeight Integer) ->
            StackCmd ty inHeight outHeight ->
            IO (ty, Vect outHeight Integer)
runStack stk (Push val) = pure ((), val :: stk)
runStack (val :: stk) Pop = pure (val, stk)
runStack (val :: stk) Top = pure (val, val :: stk)
runStack stk GetStr = do x <- getLine
                         pure (x, stk)
runStack stk (PutStr x) = do putStr x
                             pure (() , stk)
runStack stk (Pure x) = pure (x, stk)
runStack stk (cmd >>= next)
  = do (cmdRes, newStk) <- runStack stk cmd
       runStack newStk (next cmdRes)

testAdd : StackCmd Integer 0 0
testAdd = do Push 10
             Push 20
             val1 <- Pop
             val2 <- Pop
             Pure (val1 + val2)

doAdd : StackCmd () (S (S height)) (S height)
doAdd = do val1 <- Pop
           val2 <- Pop
           Push (val1 + val2)

data Fuel = Dry | More (Lazy Fuel)

forever : Fuel
forever = More forever

run : Fuel -> Vect height Integer -> StackIO height -> IO ()
run Dry stk p = pure ()
run (More fuel) stk (Do c f) = do (res, newStk) <- runStack stk c
                                  run fuel newStk (f res)

data StkInput = Number Integer
              | Add

strToInput : String -> Maybe StkInput
strToInput "" = Nothing
strToInput "add" = Just Add
strToInput x = if all isDigit (unpack x)
                  then Just (Number (cast x))
                  else Nothing

mutual
  tryAdd : StackIO height
  tryAdd {height = S(S h)}= do doAdd
                               result <- Top
                               PutStr (show result ++ "\n")
                               stackCalc
  tryAdd = do PutStr "Fewer than two items on the stack \n"
              stackCalc

  stackCalc : StackIO height
  stackCalc = do PutStr "> "
                 input <- GetStr
                 case strToInput input of
                      Nothing => do PutStr "Invalid input\n"
                                    stackCalc
                      Just (Number x) => do Push x
                                            stackCalc
                      Just Add => tryAdd

main: IO ()
main = run forever [] stackCalc


module GuardedRecursion

%default total

data Later : Type -> Type where
  next  : a -> Later a

compose : Later (a -> b) -> Later a -> Later b
compose (next t) (next u) = next (t u)

instance Functor Later where
  map f (next t) = next (f t)

instance Applicative Later where
  pure = next
  (next t) <$> (next u) = compose (next t) (next u)
{-
data Box : Type where
  Boxed   : Box
  Unboxed : Box

data GT : Box -> Type -> Type where
  Guard : (b : Box) -> a -> GT b a

instance Functor (GT b) where
  map f g = case g of
              Guard b a => Guard b (f a)
-}

partial
fix : (Later a -> a) -> a
fix f = f (next (fix f))

nextid : (u : Later a) -> ((next id) <$> u) = u
nextid (next x) = Refl

nextcompose : (s : Later (b -> c)) -> (t : Later (c -> b)) -> (u : Later c) -> ((next (.) <$> s) <$> t) <$> u = s <$> (t <$> u)
nextcompose (next s') (next t') (next u') = Refl

nextcompose' : (t : (a -> b)) -> (u : a) -> next t <$> next u = next (t u)
nextcompose' t' u' = Refl

nextright : (u : Later (a -> b)) -> (t : a) -> u <$> next t =  next(\g => g t) <$> u
nextright (next u') t = Refl

laterDist : Later (Later (a -> b)) -> Later (Later a -> Later b)
laterDist (next t) = next (t <$>)

nLater : Nat -> Type -> Type
nLater Z a = a
nLater (S n) a = Later (nLater n a)

nCompose : (n : Nat) -> nLater (S n) (a -> b) -> nLater (S n) a -> nLater (S n) b
nCompose Z t u = compose t u
nCompose (S n') (next t) (next u) = next (nCompose n' t u)

doubleCompose : Later (Later (a -> b)) -> Later (Later a) -> Later (Later b)
doubleCompose = nCompose 1

oneComposeIsCompose : (nCompose 0 t u) = compose t u
oneComposeIsCompose = Refl

twoComposeIsDoubleCompose : (nCompose 1 t u) = doubleCompose t u
twoComposeIsDoubleCompose = Refl

unsafeForce : Later a -> a
unsafeForce (next t) = t

app1 : Later (a -> b) -> a -> Later b
app1 f a = f <$> (next a)

app1' : nLater (S n) (a -> b) -> nLater n a -> nLater (S n) b
app1' {n} f a = nCompose n f (next a)

app1n : (n : Nat) -> nLater n (a -> b) -> a -> nLater n b
app1n (S Z) f a = f <$> (next a)
app1n (S n') (next f) a = next $ (app1n n' f a)

app1Eq : app1 f a = app1n 1 f a
app1Eq = Refl

app1Eq' : app1n 1 (next f) a = app1' {n = Z} (next f) a
app1Eq' = Refl

app2 : (a -> b) -> Later a -> Later b
app2 f (next a) = next (f a)

app2' : nLater n (a -> b) -> nLater (S n) a -> nLater (S n) b
app2' {n} f a = nCompose n (next f) a

app2n : (n : Nat) -> (a -> b) -> nLater n a -> nLater n b
app2n Z f a = f a
app2n (S Z) f (next a) = next (f a)
app2n (S n') f (next a) = next (app2n n' f a)

app2Eq : (eq : (next b) = a) -> app2 f a = app2n 1 f a
app2Eq = ?app2EqMeta

app2Eq' : (eq : (next b) = a) -> app2n 1 f a = app2' {n = Z} f a
app2Eq' = ?app2Eq'Meta

app3 : (a -> b) -> a -> Later b
app3 f a = next (f a)

app3' : nLater n (a -> b) -> nLater n a -> nLater (S n) b
app3' {n} f a = nCompose n (next f) (next a)

app3n : (n : Nat) -> (a -> b) -> a -> nLater n b
app3n Z f a = f a
app3n (S n') f a = next (app3n n' f a)

app4 : Later (a -> b) -> Later a -> Later b
app4 = nCompose 0

app4' : nLater (S n) (a -> b) -> nLater (S n) a -> nLater (S n) b
app4' {n} = nCompose n

app4n : (n : Nat) -> nLater (S n) (a -> b) -> nLater (S n) a -> nLater (S n) b
app4n n = app4' {n}

corecord Stream' : Type -> Type where
   hd : Stream' a -> a
   tl : Stream' a -> Later (Stream' a)
   constructor StreamCons

take' : (n : Nat) -> Stream' a -> Vect n a
take' Z _ = []
take' (S n) s = (hd s) :: (take' n (unsafeForce (tl s)))

partial
take'' : (n : Nat) -> Stream' a -> List a
take'' = fix(\t,n,s => case n of
                         Z    => []
                         S n' => (hd s) :: (unsafeForce ( ( t <$> (next n') ) <$> (tl s))))

partial
zeros : Stream' Nat
zeros = fix (\f => StreamCons 0 f)

partial
moreZeros : Stream' Nat
moreZeros = fix (\f => StreamCons 0 (app1 (app3 StreamCons 0) f))

partial
prepend : List a -> Stream' a -> Stream' a
prepend = fix (\p => \l => \s => case l of
                                   [] => s
                                   (x :: xs) => StreamCons x ((p <$> (next xs)) <$> (next s)))

{-
partial
prepend'Z : Vect Z a -> Stream' a -> Stream' a
prepend'Z = fix (\p, [], s => s)

prepend'S : Vect (S n) a -> Stream' a -> Stream' a
prepend'S = fix (\p, (x :: xs), s => StreamCons x (p <$> (next xs) <$> (next s)))
-}
partial
hMerge : Stream' a -> Stream' a -> Stream' a
hMerge = fix (\m => \xs => \ys => StreamCons (hd xs) (next (StreamCons (hd ys) (m <$> (tl xs) <$> (tl ys)))))

partial
hMerge' : Stream' a -> Stream' a -> Stream' a
hMerge' = fix (\m => \xs => \ys => StreamCons (hd xs) ((next StreamCons <$> next (hd ys)) <$> next ((m <$> (tl xs)) <$> (tl ys))))

partial
mergef : (a -> a -> Later (Stream' a) -> Stream' a) -> Stream' a -> Stream' a -> Stream' a
mergef f = fix (\m => \xs => \ys => f (hd xs) (hd ys) ((m <$> (tl xs)) <$> (tl ys)))

partial
mergef' : (a -> a -> Later (Stream' a) -> Stream' a) -> Stream' a -> Stream' a -> Stream' a
mergef' = fix (\m => \f => \xs => \ys => f (hd xs) (hd ys) (((m <$> (next f)) <$> (tl xs)) <$> (tl ys)))
{-
unguard : (GT Boxed a) -> a
unguard (Guard _ a) = a

partial
gMap : (a -> b) -> (GT x (Stream' a)) -> (GT x (Stream' b))
gMap = fix (\m, f, s => Guard x (StreamCons (map (f . hd) s)  ))

partial
gEven : (GT Boxed (Stream' a)) -> (GT Boxed (Stream' a))
-}
partial
sMap : (a -> b) -> Stream' a -> Stream' b
sMap = fix (\m => \f => \s => StreamCons (f (hd s)) ((m <$> (next f)) <$> (tl s)))

partial
sMap' : (a -> b) -> Stream' a -> Stream' b
sMap' = fix (\m => \f => \s => StreamCons (f (hd s)) (app4 (app1 m f) (tl s)))

partial
sMerge : Ord a => Stream' a -> Stream' a -> Stream' a
sMerge = fix (\m => \s => \t => StreamCons
                                 (if (hd s) <= (hd t) then hd s else hd t)
                                 (if (hd s) <= (hd t) then
                                     (if (hd t <= hd s) then
                                        app4 (app4 m (tl s)) (tl t)
                                     else
                                        app1 (app4 m (tl s)) t)
                                 else
                                    app4 (app1 m s) (tl t)))

partial
hamming : Stream' Nat
hamming = fix (\h => StreamCons 1
		            (((next sMerge) <$> ((next (sMap (\x => 2 * x)) <$> h))) <$>
                                               (((next sMerge) <$> (next (sMap (\x => 3 * x)) <$> h)) <$>
                                                                   (next (sMap (\x => 5 * x)) <$> h))))

partial
zipWith' : (a -> b -> c) -> Stream' a -> Stream' b -> Stream' c
zipWith' f = fix(\g => \s => \t => StreamCons (f (hd s) (hd t)) (g <$> (tl s) <$> (tl t)))

partial
zipWithNat : (Nat -> Nat -> Nat) -> Stream' Nat -> Stream' Nat -> Stream' Nat
zipWithNat = zipWith' {a=Nat} {b=Nat} {c=Nat}

partial
fib : Stream' Nat
fib = fix (\f => StreamCons 0 (app4 (app3 StreamCons 1) (app4' (app1' {n=1} (app3n 2 zipWithNat (+)) f) (app2 tl f))))

partial
nats : Stream' Nat
nats = fix (\n => StreamCons 0 ((next sMap <$> next S) <$> n))

partial tailtail : Stream' a -> Later (Later (Stream' a))
tailtail = fix(\t, s => (next tl) <$> (tl s))

hat : nLater (S n) (a -> b) -> (Later (nLater n a -> nLater n b))
hat = ?h

partial nats2 : Stream' Nat
nats2 = fix(\n => StreamCons 0 (app4 (app3 StreamCons 1) (app4 (hat {n=1} (app3n 2 sMap S)) (app2 tl n))))

partial bad : (Stream' Nat)
bad = fix(\b => b)

corecord Foo : Type where
  bar : Foo -> Bool
  baz : (f : Foo) -> (if bar f then (Later Foo) else Nat)

partial
alpha : Foo
alpha = fix (\a => MkFoo True (next alpha))

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

GuardedRecursion.app2EqMeta = proof
  intros
  rewrite eq
  compute
  refine Refl

GuardedRecursion.app2Eq'Meta = proof
  intros
  rewrite eq
  compute
  refine Refl


import Data.Vect
import Control.Monad.State

-- Page 332

-- Exercise 1
update : (stateType -> stateType) -> State stateType ()
update f = do current <- get
              put (f current)

-- Exercise 2
data Tree a = Empty | Node (Tree a) a (Tree a)

testTree : Tree String
testTree = Node (Node (Node Empty "Jim" Empty) "Fred"
                     (Node Empty "Sheila" Empty)) "Alice"
               (Node Empty "Bob" (Node Empty "Eve" Empty))

countEmpty : Tree a -> State Nat ()
countEmpty Empty = do
  current <- get
  put (S current)

countEmpty (Node left val right) = do
  countEmpty left
  countEmpty right

-- Exercise 3
countEmptyNode : Tree a -> State (Nat, Nat) ()
countEmptyNode Empty = do
  (empties, nodes) <- get
  put (S empties, nodes)
countEmptyNode (Node left val right) = do
  countEmptyNode left
  (empties, nodes) <- get
  put (empties, S nodes)
  countEmptyNode right

-- Page 350
record Score where
       constructor MkScore
       correct : Nat
       attempted : Nat

record GameState where
       constructor MkGameState
       score : Score
       difficulty : Int

data Command : Type -> Type where
  PutStr : String -> Command ()
  GetLine : Command String

  GetRandom : Command Int
  GetGameState : Command GameState
  PutGameState : GameState -> Command ()

  Pure : ty -> Command ty
  Bind : Command a -> (a -> Command b) -> Command b

-- Exercise 1

-- Exercise 2
mutual
  Functor Command where
    map func x = Bind x (\a => Pure (func a))

  Applicative Command where
    pure = Pure
    (<*>) f x = do f' <- f
                   x' <- x
                   Pure (f' x')

  Monad Command where
    (>>=) = Bind

-- Exercise 3
record Votes where
      constructor MkVotes
      upvotes : Integer
      downvotes : Integer

record Article where
      constructor MkArticle
      title : String
      url : String
      score : Votes

initPage : (title : String) -> (url : String) -> Article
initPage title url = MkArticle title url (MkVotes 0 0)

getScore : Article -> Integer
getScore (MkArticle title url (MkVotes upvotes downvotes)) = upvotes - downvotes

badSite : Article
badSite = MkArticle "Bad Page" "http://example.com/bad" (MkVotes 5 47)
goodSite : Article
goodSite = MkArticle "Good Page" "http://example.com/good" (MkVotes 101 7)

-- Exercise 4
addUpvote : Article -> Article
addUpvote = record {score->upvotes $= (+1)}

addDownvote : Article -> Article
addDownvote = record {score -> downvotes $= (+1)}

-- Page 362

-- Exercise 1
namespace ex1
  data DoorState = DoorClosed | DoorOpen

  data DoorCmd : Type -> DoorState -> DoorState -> Type where
    Open : DoorCmd     () DoorClosed DoorOpen
    Close : DoorCmd    () DoorOpen   DoorClosed
    RingBell : DoorCmd () ds         ds

    -- Pure : ty -> DoorCmd ty state state
    (>>=) : DoorCmd a state1 state2
            -> (a -> DoorCmd b state2 state3)
            -> DoorCmd b state1 state3

  doorProg : DoorCmd () DoorClosed DoorClosed
  doorProg = do RingBell
                Open
                RingBell
                Close

-- Exercise 2
namespace ex2
  data GuessCmd : Type -> Nat -> Nat -> Type where
    Try : Integer -> GuessCmd Ordering (S k) k
    Pure : ty -> GuessCmd ty state state
    (>>=) : GuessCmd a state1 state2
            -> (a -> GuessCmd b state2 state3)
            -> GuessCmd b state1 state3

  threeGuesses: GuessCmd () 3 0
  threeGuesses = do Try 10
                    Try 20
                    Try 15
                    Pure ()

  -- This shouldn't compile
  -- noGuesses : GuessCmd () 0 0
  -- noGuesses = do Try 10
  --                Pure ()

-- Exercise 3
namespace ex3
  data Matter = Solid | Liquid | Gas

  data MatterCmd : Type -> Matter -> Matter -> Type where
    Melt :     MatterCmd () Solid  Liquid
    Boil :     MatterCmd () Liquid Gas
    Condense : MatterCmd () Gas    Liquid
    Freeze :   MatterCmd () Liquid Solid

    Pure : ty -> MatterCmd ty state state
    (>>=) : MatterCmd a m1 m2
            -> (a -> MatterCmd b m2 m3)
            -> MatterCmd b m1 m3

  iceSteam : MatterCmd () Solid Gas
  iceSteam = do Melt
                Boil
  steamIce : MatterCmd () Gas Solid
  steamIce = do Condense
                Freeze


module Sub03Apply54x30

import ProofColDivSeqBase
import ProofColDivSeqPostulate

%default total
-- %language ElabReflection

-- 3(18x+10) --A[6,-4]->C[4,-4]--> 3(8x+3)
ac54x30To24x9 :
  (l:Nat) -> P (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                         (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))) 2
    -> P (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))) 2
ac54x30To24x9 l prf =
  let prf2 = lvDown (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                         (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))) 2
                    prf in
    let prf3 = ac54x30To24x9' l prf2 in lvDown (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))) 3 prf3

export
apply54x30 : P (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                         (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))) 2
  -> (m : Nat **
        (LTE (S m)
             (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                            (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))))),
         P m 2))
apply54x30 {l} col = let col2 = ac54x30To24x9 l col in ((S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))) ** (lte54x30 l, col2)) where
  lte54x30 : (l:Nat) -> LTE (S (S (S (S (plus (plus (plus l l) (plus l l)) (plus (plus l l) (plus l l)))))))
                            (S (S (S (plus (plus (plus (plus (plus l l) l) (S (plus (plus l l) l))) (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l))))))
                                           (S (S (plus (plus (plus l l) l) (S (plus (plus l l) l)))))))))
  lte54x30 Z = (lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . LTESucc . LTESucc . LTESucc . LTESucc) LTEZero
  lte54x30 (S k) = let lemma = lte54x30 k in
    rewrite (sym (plusSuccRightSucc k k)) in
    rewrite (sym (plusSuccRightSucc (plus k k) (S (plus k k)))) in
    rewrite (sym (plusSuccRightSucc (plus k k) (plus k k))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) (plus k k)) (S (S (S (plus (plus k k) (plus k k))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) (plus k k)) (S (S (plus (plus k k) (plus k k)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) (plus k k)) (S (plus (plus k k) (plus k k))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) (plus k k)) (plus (plus k k) (plus k k)))) in
    rewrite (sym (plusSuccRightSucc (plus k k) k)) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (S (S (S (plus (plus k k) k)))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (S (S (plus (plus k k) k))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus k k) k) (S (plus (plus k k) k)))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k)))))))) in
    rewrite (sym (plusSuccRightSucc (plus (plus (plus (plus k k) k) (S (plus (plus k k) k))) (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))  (S (S (plus (plus (plus k k) k) (S (plus (plus k k) k))))))) in
      (lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . lteSuccRight . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc . LTESucc) lemma


module MonadLogic

-- Based on http://okmij.org/ftp/papers/LogicT.pdf
-- http://hackage.haskell.org/package/logict-0.7.0.1/docs/Control-Monad-Logic.html

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

interface Monad m => MonadPlus (m : Type -> Type) where
    mplus : m a -> m a -> m a
    mzero : m a

interface MonadPlus m => MonadLogic (m : Type -> Type) where
  msplit : m a -> m (Maybe (a, () -> m a))
  interleave : m a -> m a -> m a
  bind : m a -> (a -> m b) -> m b
  ifte : m a -> (a -> m b) -> m b -> m b
  once : m a -> m a

reflect : MonadLogic m => Maybe (a, () -> m a) -> m a
reflect Nothing = mzero
reflect (Just (a, fk)) = pure a `mplus` (fk ())

interleave' : MonadLogic m => m a -> m a -> m a
interleave' m1 m2 = do r <- msplit m1
                       case r of
                        Nothing => m2
                        Just (a, fk) => (pure a) `mplus` (interleave' m2 (fk ()))

bind' : MonadLogic m => m a -> (a -> m b) -> m b
bind' ma f = do r <- msplit ma
                case r of
                 Nothing => mzero
                 Just (a, fk) => interleave (f a) ((fk ()) `bind'` f)

ifte' : MonadLogic m => m a -> (a -> m b) -> m b -> m b
ifte' t th el = do r <- msplit t
                   case r of
                    Nothing => el
                    Just (a, fk) => (th a) `mplus` ((fk ()) >>= th)

once' : MonadLogic m => m a -> m a
once' m = do r <- msplit m
             case r of
               Nothing => mzero
               Just (a, _) => pure a

data LogicT : (m : Type -> Type) -> (a : Type) -> Type where
    LT : ({r : Type} -> (a -> (() -> m r) -> m r) -> (() -> m r) -> m r) -> LogicT m a

Functor (LogicT m) where
  map f (LT g) = LT $ \sk, fk => g (sk . f) fk

Applicative (LogicT m) where
  pure a = LT $ \sk, fk => sk a fk
  (LT f) <*> (LT g) = LT $ \sk, fk => f (\sk', fk' => g (sk . sk') fk') fk

Monad (LogicT m) where
  (LT g) >>= f = LT $ \sk, fk => g (\a, fk' => let (LT g') = f a in g' sk fk') fk

MonadPlus (LogicT m) where
  mzero = LT $ \sk, fk => fk ()
  mplus (LT f) (LT f') = LT $ \sk, fk => f sk (\() => f' sk fk)

MonadTrans LogicT where
  lift m = LT $ \sk, fk => m >>= \a => sk a fk

Monad m => MonadLogic (LogicT m) where
  msplit (LT f) = lift $ f (\a, fk => pure $ Just (a, \() => lift (fk ()) >>= reflect)) (\() => pure Nothing)
  interleave = interleave'
  bind = bind'
  ifte = ifte'
  once = once'

observe : Monad m => Nat -> LogicT m a -> m (List a)
observe Z _ = pure []
observe (S n) m = let (LT f) = msplit m in f sk (\() => pure [])
  where
    sk : {a : Type, m : Type -> Type} -> Monad m => Maybe (a, () -> LogicT m a) -> (() -> m (List a)) -> m (List a)
    sk Nothing fk = pure []
    sk (Just (a, fk')) fk = map (\xs => a :: xs) (observe n (fk' ()))

ones : MonadPlus m => Int -> m Int
ones n = if n <= 0 then mzero else (pure 1) `mplus` (ones (n - 1))

zeros : MonadPlus m => Int -> m Int
zeros n = if n <= 0 then mzero else (pure 0) `mplus` (zeros (n - 1))

test : List (List Int)
test = observe 10 (ones 5 `interleave` zeros 5)


module Linear

public export
data Usage = Once | Many

public export
data Lin : (Type -> Type) -> Usage -> Type -> Type where
     Pure : (1 x : a) -> Lin m usage a
     Lift : (1 x : m a) -> Lin m Many a
     BindOnce : (1 act : Lin m Once a) ->
                (1 k : (1 x : a) -> Lin m t b) -> Lin m t b
     BindMany : (1 act : Lin m Many a) ->
                (1 k : (x : a) -> Lin m t b) -> Lin m t b

public export
data Unrestricted : Type -> Type where
     MkUn : (x : a) -> Unrestricted a

export
pure : (1 x : a) -> Lin m usage a
pure = Pure

export
lift : (1 x : m a) -> Lin m Many a
lift = Lift

public export
contType : (Type -> Type) -> Usage -> Usage -> Type -> Type -> Type
contType m Once q a b = (1 x : a) -> Lin m q b
contType m Many q a b = (x : a) -> Lin m q b

public export
(>>=) : {p : _} ->
        (1 f : Lin m p a) -> (1 k : contType m p q a b) -> Lin m q b
(>>=) {p=Once} = BindOnce
(>>=) {p=Many} = BindMany

export
run : Monad m => Lin m usage t -> m t
run (Pure x) = pure x
run (Lift p) = p
run (BindOnce act k)
    = do act' <- run act
         run (k act')
run (BindMany act k)
    = do act' <- run act
         run (k act')

public export
One : (Type -> Type) -> Type -> Type
One m = Lin m Once

public export
Any : (Type -> Type) -> Type -> Type
Any m = Lin m Many

{-
data DoorState = Closed | Open

data Door : DoorState -> Type where
     MkDoor : (isOpen : Bool) -> Door (if isOpen then Open else Closed)

newDoor    : One m (Door Closed)
knock      : (1 d : Door t)      -> One m (Door t)
openDoor   : (1 d : Door Closed) -> One m (Res Bool (\ok => Door (if ok then Open else Closed)))
closeDoor  : (1 d : Door Open)   -> One m (Door Closed)
deleteDoor : (1 d : Door Closed) -> Any m ()

doorProg : Any m ()
doorProg
    = do d <- newDoor
         ok @@ d' <- openDoor d
         d'' <- knock d'
         if ok
            then ?doorOK
            else ?doorBad
            -}


module Cast

%default total

{-
  interface Cast from to where
    caset : (orig : from) -> to

-}

Cast (Maybe elem) (List elem) where
  cast Nothing = []
  cast (Just x) = [x]

foo : List Integer
foo = cast (Just 100)

bar : List Integer
bar = cast (the (Maybe Integer) Nothing)

Cast (List elem) (Maybe elem) where
  cast [] = Nothing
  cast (x :: xs) = Just x

baz : Maybe Int
baz = cast  (the (List Int) [99])

quux : Maybe Int
quux = cast (the (List Int) [])

-- there is nothing special about the Cast interface. We can define our own version

interface Mould from to where
  mould : (oig : from) -> to

Mould (Maybe elem) (List elem) where
  mould Nothing = []
  mould (Just x) = [x]

foofoo : List String
foofoo = mould (the (Maybe String) (Just "Hello"))

barbar : List String
barbar = mould (the (Maybe String) Nothing)

Mould (List elem) (Maybe elem) where
  mould [] = Nothing
  mould (x :: xs) = Just x

bazbaz : Maybe String
bazbaz = mould (the (List String) ["world"])

quuxquux : Maybe String
quuxquux = mould (the (List String) [])


import DataStore
import Shape

testStore : DataStore (SString .+. SInt)
testStore = addToStore ("First", 1) $
            addToStore ("Second", 2) $
            empty

getValues : DataStore (SString .+. valSchema) -> List (SchemaType valSchema)
getValues x with (storeView x)
  getValues x | SNil = []
  getValues (addToStore (key, value) store) | (SAdd rec) = value :: getValues store

area : Shape -> Double
area s with (shapeView s)
  area (triangle base height) | (STriangle base height) = 0.5 * base * height
  area (rectangle width height) | (SRectangle width height) = width * height
  area (circle radius) | (SCircle radius) = pi * radius * radius


import Data.Binary
import Data.Binary.Digit
import Data.Fin
import Data.List
import Data.List1
import Data.Nat
import Data.Vect
import Data.String
import Data.String.Parser

import System.File

Show Digit where
  show I = "1"
  show O = "0"

Algorithm : Type
Algorithm = Vect 512 Bool

Image : Nat -> Type
Image n = Vect n (Vect n Bool)

showImg : Image n -> String
showImg n = fastAppend $ intersperse "\n" $ toList $ (\r => pack $ toList $ (\b => if b then '#' else '.') <$> r) <$> n

Input : Nat -> Type
Input n = (Algorithm, Image n)

pixelParser : Parser Bool
pixelParser = char '.' $> False <|> char '#' $> True

parser : Parser (n ** Input n)
parser = do alg <- ntimes 512 pixelParser
            spaces
            row <- some pixelParser <* char '\n'
            let n = length row
            Just v <- pure $ toVect n row
              | Nothing => fail "not length"
            Yes ItIsSucc <- pure $ isItSucc n
              | No _ => fail "Row is zero"
            vs <- ntimes (pred n) ((ntimes n pixelParser) <* char '\n')
            pure $ MkDPair n (alg, v :: vs)

grow : Bool -> (n : Nat) -> Image n -> Image (4 + n)
grow d n img = let gr = (flip snoc) d . (flip snoc) d . (::) d . (::) d <$> img
                   emptyrow = replicate (4 + n) d in
                   (flip snoc) emptyrow $ (flip snoc) emptyrow $ (::) emptyrow $ (::) emptyrow $ gr

shrink : {n : Nat} -> Image (4 + n) -> Image (2 + n)
shrink = map (init . tail) . init . tail

decFin : Fin n -> Fin (S n)
decFin FZ = FZ
decFin (FS x) = weaken $ weaken x

neighbours : Fin n -> Fin n -> List (Fin (S n), Fin (S n))
neighbours x y = let xs = [ decFin x, weaken x, shift 1 x ]
                     ys = [ decFin y, weaken y, shift 1 y ] in
                     join $ (\y => (,y) <$> xs) <$> ys

getNumber : Image n -> List (Fin n, Fin n) -> Nat
getNumber img l = toNat $ reverse $ (\b => if b then I else O) <$> (\(x, y) => index x $ index y img) <$> l

lookupAlg : Algorithm -> Nat -> Bool
lookupAlg alg n = case natToFin n 512 of
                       Nothing => False
                       (Just i) => index i alg

enhance : {n : Nat} -> Algorithm -> ((k : Nat) -> Image k -> Image (4 + k)) -> Image n -> Image (2 + n)
enhance alg g img = let img' = g n img
                        indices = map (shift 1) $ forget $ allFins (S n)
                        positions = join $ (\x => (x,) <$> indices) <$> indices
                        enhanced = foldl (\i => \(x, y) => let k = getNumber img' $ neighbours x y
                                                               b = lookupAlg alg k in
                                                               updateAt (weaken y) (replaceAt (weaken x) b) i) img' positions in
                        shrink enhanced

enhanceN : {n : Nat} -> Nat -> Algorithm -> (Bool -> (k : Nat) -> Image k -> Image (4 + k)) -> Image n -> (m ** Image m)
enhanceN 0 alg g img = MkDPair n img
enhanceN (S s) alg g img = let en = enhance alg (g ((modNatNZ (S s) 2 SIsNonZero) == 1)) img in enhanceN {n = (S (S n))} s alg g en

countLit : Image n -> Nat
countLit = sum . map (count id)

part1 : {n : Nat} -> Input n -> IO String
part1 (alg, img) = pure $ show $ countLit $ enhance alg (grow True) $ enhance alg (grow False) img

part2 : {n : Nat} -> Input n -> IO String
part2 (alg, img) = let MkDPair _ enn = enhanceN 50 alg grow img in
                       pure $ show $ countLit $ enn

main : IO ()
main = do Right input <- readFile "input.txt"
            | Left err => printLn err
          Right (MkDPair n a, _) <- pure $ parse parser input
            | Left err => printLn err
          part1 a >>= putStrLn
          part2 a >>= putStrLn


module Part2.Sec6_3b_datastore
import Data.Vect
import Util.MiniParser

%default total

infixr 5 .+.

||| ADT that represents parsed schema
data Schema = SString
            | SInt
            | (.+.) Schema Schema

||| Type level function that maps Schema to actual type, composite schema
||| is mapped to a tuple.
SchemaType : Schema -> Type
SchemaType SString = String
SchemaType SInt = Int
SchemaType (x .+. y) = (SchemaType x, SchemaType y)

{-
idris repl:
*src/Part2/Sec6_3_datastore> SchemaType (SInt .+. SString .+. SInt)
(Int, String, Int) : Type
-}

{- This needs schema type parameter to cary the schema information around -}
||| Type representing user commands (DSL)
data Command : Schema -> Type where
           -- works in context of old schema, polymorphic in the return type
           SetSchema : (newschema : Schema) -> Command schema
           Add : SchemaType schema -> Command schema
           Get : Integer -> Command schema
           Quit : Command schema

{- This is powerful. Some of this can be mimicked in Haskell, e.g.
`singletons` library could generate type family Schema :: DataStore -> Schema from
something like `data DataStore = DataStore{ schema :: Schema}`
but cross referencing between field values and field types is beyond what I can
do in Haskell, also record 'getters' are not automatically lifted by `DataKinds` -}
record DataStore where
       constructor MkData
       schema : Schema
       size : Nat
       items : Vect size (SchemaType schema)

{-
teststore = MkData (SInt .+. SString .+. SInt) 1 [(2, "Two", 42)]
-}

{- helper methods used with DataStore, note cool use of record getter at type level -}

addToStore : (store : DataStore) -> SchemaType (schema store) -> DataStore
addToStore (MkData schema size store) newitem
           = MkData schema _ (addToData store)
  where
    addToData : Vect oldsize (SchemaType schema) -> Vect (S oldsize) (SchemaType schema)
    addToData [] = [newitem]
    addToData (item :: items) = item :: addToData items

display : SchemaType schema -> String
display {schema = SString} item = show item
display {schema = SInt} item = show item
display {schema = (x .+. y)} (iteml, itemr)
            = display iteml ++ ", " ++ display itemr

{- Changed from the book version to emphasize type safe retrieval -}
retrieve : (pos : Integer) -> (store : DataStore) -> Maybe (SchemaType (schema store))
retrieve pos store =
      integerToFin pos (size store) >>= (\i => pure (index i (items store)))

getEntry : (pos : Integer) -> (store : DataStore) -> Maybe (String, DataStore)
getEntry pos store = case retrieve pos store of
             Nothing => Just ("Out of range\n", store)
             Just rec => Just ((display rec) ++ "\n", store)

setSchema : (store : DataStore) -> Schema -> Maybe DataStore
setSchema store schema = case size store of
       Z => Just (MkData schema _ [])
       S k => Nothing

{- Parsers -}
sstring : Parser Schema
sstring =  string "String" *> pure SString

sint : Parser Schema
sint = string "Int" *> pure SInt

scolumn : Parser Schema
scolumn = sstring <|> sint

covering
schemaBody : Parser Schema
schemaBody = do
     col  <- scolumn
     _    <- optional spaces
     rest <- optional schemaBody
     case rest of
        Nothing =>  pure col
        Just rest => pure (col .+. rest)

{- Note Idris has no problem with this name being overloaded -}
covering
schema : Parser Schema
schema = string "schema" *> spaces *> schemaBody

covering
schemaTypeBody : (sch : Schema) -> Parser (SchemaType sch)
schemaTypeBody SString = between (char '"') (char '"')
schemaTypeBody SInt = decimalInt
schemaTypeBody (schemal .+. schemar) = do
                parsed1 <- schemaTypeBody schemal
                _    <- spaces
                parsed2 <- schemaTypeBody schemar
                pure (parsed1, parsed2)

covering
schemaType : (sch : Schema) -> Parser (SchemaType sch)
schemaType sch = string "add" *> spaces *> schemaTypeBody sch

covering
command : (sc : Schema) -> Parser (Command sc)
command sc = (SetSchema <$> schema) <|>
             (string "quit" *> pure Quit) <|>
             (string "get") *> spaces *> (Get <$> decimalInteger) <|>
             (Add <$> schemaType sc)

{- End Parsers -}

||| replWith implementation method
covering
processInput : DataStore -> String -> Maybe (String, DataStore)
processInput store input
          = case parseAll (command (schema store)) input of
                 Left msg => Just ("Invalid command: " ++ msg ++ "\n", store)
                 Right (Add item) =>
                    Just ("ID " ++ show (size store) ++ "\n", addToStore store item)
                 Right (Get pos) => getEntry pos store
                 Right (SetSchema schema') => case setSchema store schema' of
                        Nothing => Just ("Can't update schema\n", store)
                        Just store' => Just ("OK\n", store')
                 Right Quit => Nothing

initDs : DataStore
initDs = MkData SString _ []

testDs : DataStore
testDs = MkData (SInt .+. SString) _ []

{- To execute in repl, idrs repl from src folder, :l this file and use :exec sec6_3b -}
export
covering
sec6_3b : IO ()
sec6_3b = replWith initDs "Command: " processInput


module Depling.Pull

import Depling
import Data.Fin
import Data.Vect
import Utils

mutual
	total
	pull_ℙ : Fin (S n) -> DAST (a + (S n)) -> Maybe (DAST (a + n))
	pull_ℙ {a=Z} pv ast = pull pv ast
	pull_ℙ {a=S a} {n} pv ast =
		assert_total $
		replace {P=Maybe . DAST} (sym $ plusSuccRightSucc _ _) $
		pull_ℙ (FS pv) $
		replace (plusSuccRightSucc _ _) $
		ast

	total
	export
	pull : Fin (S n) -> DAST (S n) -> Maybe (DAST n)
	pull pv (ʌ v) = case fcompare v pv of
		FLT lt => Just $ ʌ $ finDown v {lt}
		FEQ eq => Nothing
		FGT gt => Just $ ʌ $ finD v {gt}
	pull pv (λ at b) = do
		at' <- pull pv at
		b' <- pull (FS pv) b
		pure $ λ at' b'
	pull pv (λT at rt) = do
		at' <- pull pv at
		rt' <- pull (FS pv) rt
		pure $ λT at' rt'
	pull pv (f =!= a) = do
		f' <- pull pv f
		a' <- pull pv a
		pure $ f' =!= a'
	pull pv 𝕋 = Just 𝕋
	pull pv (𝔽 at rt b) = do
		at' <- pull          pv  at
		rt' <- pull (FS$     pv) rt
		b'  <- pull (FS$ FS$ pv) b
		pure $ 𝔽 at' rt' b'
	pull pv (𝕌 n t) = do
		t' <- pull pv t
		pure $ 𝕌 n t'
	pull pv (ℂ cn as) = do
		as' <- sequence $ map (assert_total $ pull pv) as
		pure $ ℂ cn as'
	pull pv (ℙ v cn t f) = do
		v' <- pull pv v
		t' <- pull_ℙ pv t
		f' <- pull pv f
		pure $ ℙ v' cn t' f'
	pull pv (𝔹 t) = do
		t' <- pull pv t
		pure $ 𝔹 t'


module Main

import Data.Vect

%default total

data CoNat
    = Co Nat
    | Infinity

S : CoNat -> CoNat
S (Co n)   = Co (S n)
S Infinity = Infinity

Sn_notzero : Main.S n = Co 0 -> Void
Sn_notzero = believe_me

S_Co_not_Inf : Main.S (Co n) = Infinity -> Void
S_Co_not_Inf = believe_me

S_inj : (n : CoNat) -> (m : CoNat) -> Main.S n = Main.S m -> n = m
S_inj (Co n)   (Co _)   Refl = Refl
S_inj (Co n)   Infinity p    = void (S_Co_not_Inf p)
S_inj Infinity (Co m)   p    = void (S_Co_not_Inf (sym p))
S_inj Infinity Infinity Refl = Refl

swap : {n : Nat} -> Vect n a -> Vect n a
swap Nil            = Nil
swap (x :: Nil)     = x :: Nil
swap (x :: y :: xs) = (y :: x :: (swap xs))

main : IO ()
main = printLn (swap [1,2,3,4,5])


module ErrorHandlingInterpreter

%default total

data Expr = Var String
          | Val Int
          | Add Expr Expr

data Eval : Type -> Type where
  MkEval : (List (String, Int) -> Maybe a) -> Eval a

fetch : String -> Eval Int
fetch s = MkEval (\e => fetchVal e) where
  fetchVal : List (String, a) -> Maybe a
  fetchVal [] = Nothing
  fetchVal ((v, val) :: xs) = if v == s
                                 then (Just val)
                                 else (fetchVal xs)

Functor Eval where
  -- map : (a -> b) -> Eval a -> Eval b
  map f (MkEval g) = MkEval (\x => map f (g x))

Applicative Eval where
  -- pure : Eval a
  pure x = MkEval (\e => Just x)

  -- (<*>) : Eval (a -> b) -> Eval a -> Eval b
  (MkEval f) <*> (MkEval g) = MkEval (\x => app (f x) (g x)) where
    app : Maybe (a -> b) -> Maybe a -> Maybe b
    app (Just fx) (Just gx) = Just (fx gx)
    app _ _ = Nothing

{-
eval : Expr -> Eval Int
eval (Var x) = fetch x
eval (Val n) = pure n
eval (Add x y) = pure (+) <*> eval x <*> eval y
-}

eval : Expr -> Eval Int
eval (Var x) = fetch x
eval (Val n) = [| n |]
eval (Add x y) = [| eval x + eval y |]

runEval : List (String, Int) -> Expr -> Maybe Int
runEval env e = case eval e of
                     MkEval envFn => envFn env


a : Integer
a = 3+4

b : Integer
b = 7

some_proposition : Bool
some_proposition = 4+7==11

another_proposition : Bool
another_proposition = 3+8==11

complicated : Bool
complicated = 4+5*3-7==13-1

p : Bool
p = 8>5

q : Bool
q = "Hello" == "Goodbye"

r : Bool
r = True

j : Integer
j = 9001

u : Bool
u = ("Hello" == "Goodbye") || (3==3)

s : String
s = ifThenElse (3==3) "tick" "tock"


module ListExt2

%default total
%access public export

smallerThanCons : (x : a) -> (xs : List a) -> Smaller xs (x :: xs)
smallerThanCons x [] = lteRefl
smallerThanCons x (y :: xs) = lteRefl

||| If Left True, never bother to call me back
eat : Eq a => (delim : List a) -> {auto prf : NonEmpty delim}
    -> (dish : List a)
    -> Either Bool (rem : List a ** Smaller rem dish)
eat (x :: xs) [] = Left True
eat (x :: xs) (y :: ys) =
  if x == y
  then
    case xs of
      [] => Right (ys ** smallerThanCons y ys)
      (z :: zs) =>
        case eat (z :: zs) ys of
          Left e => Left e
          Right (l ** prf) => Right (l ** lteSuccRight prf)
  else Left False

splitStep : Eq a => (xs : List a)
          -> ((ys : List a) -> Smaller ys xs -> List a
                            -> (List a, List (List a)))
          -> List a -> (List a, List (List a))
splitStep [] _ _ = ([], [])
splitStep xs@(_ :: _) _ [] = (xs, [])
splitStep (x :: xs) f delim@(_::_) =
  case eat delim (x :: xs) of
    Left True => (x :: xs, [])
    Left False =>
      let (l, ls) = f xs (smallerThanCons x xs) delim
      in (x :: l, ls)
    Right (rem ** prf) =>
      let (l, ls) = f rem prf delim
      in ([], l::ls)

splitWith : Eq a => List a -> List a -> List (List a)
splitWith delim splittee =
  let splitter = sizeRec splitStep splittee
  in uncurry (::) $ splitter delim

substitute : Eq a => List a -> List a -> List a -> List a
substitute needle replacement = intercalate replacement . splitWith needle

strReplace : String -> String -> String -> String
strReplace needle replacement =
  pack . substitute (unpack needle) (unpack replacement) . unpack


-- ---------------------------------------------- [ DependentStateMachines.idr ]
-- Module      : Chapter.DependentStateMachines
-- Description : Definitions from Chapter 14 of Edwin Brady's book,
--               "Type-Driven Development with Idris."
-- --------------------------------------------------------------------- [ EOH ]
module Chapter.DependentStateMachines

import Data.Vect

-- ------------------------------------------ [ 14.1.1 Refining the Door Model ]

public export
data DoorResult = OK | Jammed

||| A door is either open or closed.
public export
data DoorState = ||| The door is closed.
                 DoorClosed
               | ||| The door is open.
                 DoorOpen

namespace Door

    public export
    data DoorCmd : (ty : Type) -> DoorState -> (ty -> DoorState) -> Type where
         Open : DoorCmd DoorResult DoorClosed
                                   (\res => case res of
                                                 OK     => DoorOpen
                                                 Jammed => DoorClosed)

         Close : DoorCmd () DoorOpen (const DoorClosed)
         RingBell : DoorCmd () DoorClosed (const DoorClosed)

         Display : String -> DoorCmd () state (const state)

         Pure : (res : ty) -> DoorCmd ty (state_fn res) state_fn
         (>>=) : DoorCmd a state1 state2_fn ->
               ((res : a) -> DoorCmd b (state2_fn res) state3_fn) ->
         DoorCmd b state1 state3_fn

logOpen : DoorCmd DoorResult DoorClosed
                             (\res => case res of
                                           OK     => DoorOpen
                                           Jammed => DoorClosed)
logOpen = do Display "Trying to open the door"
             OK <- Open | Jammed => do Display "Jammed"
                                       Pure Jammed
             Display "Success"
             Pure OK

-- -------------------------------------- [ 14.2.2 Defining a Type for the ATM ]

||| A PIN is exactly four characters.
PIN : Type
PIN = Vect 4 Char

||| The possible states of the ATM.
data ATMState = Ready | CardInserted | Session

||| The possible results of checking a PIN, valid or invalid.
data PINCheck = CorrectPIN | IncorrectPIN

data HasCard : ATMState -> Type where
     HasCI      : HasCard CardInserted
     HasSession : HasCard Session

namespace ATM

    data ATMCmd : (ty : Type) -> ATMState -> (ty -> ATMState) -> Type where
         InsertCard : ATMCmd ()   Ready        (const CardInserted)
         EjectCard  : {auto prf : HasCard state} ->
                      ATMCmd () state (const Ready)
         GetPIN     : ATMCmd PIN  CardInserted (const CardInserted)

         CheckPIN   : PIN -> ATMCmd PINCheck CardInserted
                               (\check => case check of
                                               CorrectPIN => Session
                                               IncorrectPIN => CardInserted)
         GetAmount : ATMCmd Nat state (const state)

         Dispense : (amount : Nat) -> ATMCmd () Session (const Session)

         Message : String -> ATMCmd () state (const state)
         Pure  : (res : ty) -> ATMCmd ty (state_fn res) state_fn
         (>>=) : ATMCmd a state1 state2_fn ->
                 ((res : a) -> ATMCmd b (state2_fn res) state3_fn) ->
                 ATMCmd b state1 state3_fn

atm : ATMCmd () Ready (const Ready)
atm = do InsertCard
         Message "Checking Card"
         case !(CheckPIN !GetPIN) of
              CorrectPIN   => do Dispense !GetAmount
                                 EjectCard
                                 Message "Please remove your card and cash"
              IncorrectPIN => do Message "Incorrect PIN"
                                 EjectCard

testPIN : Vect 4 Char
testPIN = ['1', '2', '3', '4']

readVect : (n : Nat) -> IO (Vect n Char)
readVect Z = do discard <- getLine
                pure []
readVect (S k) = do ch <- getChar
                    chs <- readVect k
                    pure (ch :: chs)

runATM : ATMCmd res inState outState_fn -> IO res
runATM InsertCard = do putStrLn "Please insert your card (press enter)"
                       x <- getLine
                       pure ()
runATM EjectCard = putStrLn "Card ejected"
runATM GetPIN = do putStr "Enter PIN: "
                   readVect 4
runATM (CheckPIN pin) = if pin == testPIN
                           then pure CorrectPIN
                           else pure IncorrectPIN
runATM GetAmount = do putStr "How much would you like? "
                      pure (cast !getLine)
runATM (Dispense amount) = putStrLn ("Here is " ++ show amount)

runATM (Message msg) = putStrLn msg
runATM (Pure res) = pure res
runATM (x >>= f) = do x' <- runATM x
                      runATM (f x')

-- --------------------------------------------------------------------- [ EOF ]


module Data.List.Predicates.Interleaving

import Data.List

%access public export
%default total

data Interleaving : (xs, ys, zs : List type) -> Type where
  Empty   : Interleaving Nil Nil Nil
  LeftAdd : (x : type)
         -> (rest : Interleaving xs ys zs)
         -> Interleaving (x::xs) ys (x::zs)
  RightAdd : (y : type)
          -> (rest : Interleaving xs ys zs)
          -> Interleaving xs (y::ys) (y::zs)

leftEmpty : (ys : List type) -> Interleaving Nil ys ys
leftEmpty [] = Empty
leftEmpty (x :: xs) = RightAdd x (leftEmpty xs)

leftEmptyCons : (x : type) -> (xs : List type) -> Interleaving Nil (x::xs) (x::xs)
leftEmptyCons x [] = RightAdd x Empty
leftEmptyCons x (y :: xs) with (leftEmptyCons y xs)
  leftEmptyCons x (y :: xs) | (RightAdd y rest) = RightAdd x (RightAdd y rest)

rightEmpty : (xs : List type) -> Interleaving xs Nil xs
rightEmpty [] = Empty
rightEmpty (x :: xs) = LeftAdd x (rightEmpty xs)


module Control.MonadRec

import public Control.WellFounded
import Control.Monad.Either
import Control.Monad.Identity
import Control.Monad.Maybe
import Control.Monad.Reader
import Control.Monad.RWS
import Control.Monad.State
import Control.Monad.Writer

import Data.List
import Data.SnocList
import public Data.Fuel
import public Data.Nat

%default total

--------------------------------------------------------------------------------
--          Sized Implementations
--------------------------------------------------------------------------------

public export
Sized Fuel where
  size Dry      = 0
  size (More f) = S $ size f

public export
Sized (SnocList a) where
  size = length

--------------------------------------------------------------------------------
--          Step
--------------------------------------------------------------------------------

||| Single step in a recursive computation.
|||
||| A `Step` is either `Done`, in which case we return the
||| final result, or `Cont`, in which case we continue
||| iterating. In case of a `Cont`, we get a new seed for
||| the next iteration plus an updated state. In addition
||| we proof that the sequence of seeds is related via `rel`.
||| If `rel` is well-founded, the recursion will provably
||| come to an end in a finite number of steps.
public export
data Step :  (rel   : a -> a -> Type)
          -> (seed  : a)
          -> (accum : Type)
          -> (res   : Type)
          -> Type where

  ||| Keep iterating with a new `seed2`, which is
  ||| related to the current `seed` via `rel`.
  ||| `vst` is the accumulated state of the iteration.
  Cont :  (seed2 : a)
       -> (0 prf : rel seed2 seed)
       -> (vst   : st)
       -> Step rel seed st res

  ||| Stop iterating and return the given result.
  Done : (vres : res) -> Step rel v st res

public export
Bifunctor (Step rel seed) where
  bimap f _ (Cont s2 prf st) = Cont s2 prf (f st)
  bimap _ g (Done res)       = Done (g res)

  mapFst f (Cont s2 prf st) = Cont s2 prf (f st)
  mapFst _ (Done res)       = Done res

  mapSnd _ (Cont s2 prf st) = Cont s2 prf st
  mapSnd g (Done res)       = Done (g res)

--------------------------------------------------------------------------------
--          MonadRec
--------------------------------------------------------------------------------

||| Interface for tail-call optimized monadic recursion.
public export
interface Monad m => MonadRec m where
  ||| Implementers mus make sure they implement this function
  ||| in a tail recursive manner.
  ||| The general idea is to loop using the given `step` function
  ||| until it returns a `Done`.
  |||
  ||| To convey to the totality checker that the sequence
  ||| of seeds generated during recursion must come to an
  ||| end after a finite number of steps, this function
  ||| requires an erased proof of accessibility.
  total
  tailRecM :  {0 rel : a -> a -> Type}
           -> (seed  : a)
           -> (ini   : st)
           -> (0 prf : Accessible rel seed)
           -> (step  : (seed2 : a) -> st -> m (Step rel seed2 st b))
           -> m b

public export %inline
||| Monadic tail recursion over a sized structure.
trSized :  MonadRec m
        => (0 _ : Sized a)
        => (seed : a)
        -> (ini  : st)
        -> (step : (v : a) -> st -> m (Step Smaller v st b))
        -> m b
trSized x ini = tailRecM x ini (sizeAccessible x)

--------------------------------------------------------------------------------
--          Base Implementations
--------------------------------------------------------------------------------

public export
MonadRec Identity where
  tailRecM seed st1 (Access rec) f = case f seed st1 of
    Id (Done b)         => Id b
    Id (Cont y prf st2) => tailRecM y st2 (rec y prf) f

public export
MonadRec Maybe where
  tailRecM seed st1 (Access rec) f = case f seed st1 of
    Nothing               => Nothing
    Just (Done b)         => Just b
    Just (Cont y prf st2) => tailRecM y st2 (rec y prf) f

public export
MonadRec (Either e) where
  tailRecM seed st1 (Access rec) f = case f seed st1 of
    Left e                 => Left e
    Right (Done b)         => Right b
    Right (Cont y prf st2) => tailRecM y st2 (rec y prf) f

trIO :  (x : a)
     -> (ini : st)
     -> (0 _ : Accessible rel x)
     -> (f : (v : a) -> st -> IO (Step rel v st b))
     -> IO b
trIO x ini acc f = fromPrim $ run x ini acc
  where run :  (y : a) -> (st1 : st)
            -> (0 _ : Accessible rel y)
            -> (1 w : %World)
            -> IORes b
        run y st1 (Access rec) w = case toPrim (f y st1) w of
          MkIORes (Done b) w2          => MkIORes b w2
          MkIORes (Cont y2 prf st2) w2 => run y2 st2 (rec y2 prf) w2

public export %inline
MonadRec IO where
  tailRecM = trIO

--------------------------------------------------------------------------------
--          Transformer Implementations
--------------------------------------------------------------------------------

---------------------------
-- StateT

%inline
convST :  Functor m
       => (f : (v : a) -> st -> StateT s m (Step rel v st b))
       -> (v : a)
       -> (st,s)
       -> m (Step rel v (st,s) (s,b))
convST f v (st1,s1) =   (\(s2,stp) => bimap (,s2) (s2,) stp)
                    <$> runStateT s1 (f v st1)

public export
MonadRec m => MonadRec (StateT s m) where
  tailRecM x ini acc f =
    ST $ \s1 => tailRecM x (ini,s1) acc (convST f)

---------------------------
-- EitherT

convE :  Functor m
      => (f : (v : a) -> st -> EitherT e m (Step rel v st b))
      -> (v : a)
      -> (ini : st)
      -> m (Step rel v st (Either e b))
convE f v s1 = map conv $ runEitherT (f v s1)
  where conv : Either e (Step rel v st b) -> Step rel v st (Either e b)
        conv (Left err)                = Done (Left err)
        conv (Right $ Done b)          = Done (Right b)
        conv (Right $ Cont v2 prf st2) = Cont v2 prf st2

public export
MonadRec m => MonadRec (EitherT e m) where
  tailRecM x ini acc f =
    MkEitherT $ tailRecM x ini acc (convE f)

---------------------------
-- MaybeT

convM :  Functor m
      => (f : (v : a) -> st -> MaybeT m (Step rel v st b))
      -> (v : a)
      -> (ini : st)
      -> m (Step rel v st (Maybe b))
convM f v s1 = map conv $ runMaybeT (f v s1)
  where conv : Maybe (Step rel v st b) -> Step rel v st (Maybe b)
        conv Nothing                  = Done Nothing
        conv (Just $ Done b)          = Done (Just b)
        conv (Just $ Cont v2 prf st2) = Cont v2 prf st2

public export
MonadRec m => MonadRec (MaybeT m) where
  tailRecM x ini acc f =
    MkMaybeT $ tailRecM x ini acc (convM f)

---------------------------
-- ReaderT

convR :  (f : (v : a) -> st -> ReaderT e m (Step rel v st b))
      -> (env : e)
      -> (v : a)
      -> (ini : st)
      -> m (Step rel v st b)
convR f env v s1 = runReaderT env (f v s1)

public export
MonadRec m => MonadRec (ReaderT e m) where
  tailRecM x ini acc f =
    MkReaderT $ \env => tailRecM x ini acc (convR f env)

---------------------------
-- WriterT

convW :  Functor m
      => (f : (v : a) -> st -> WriterT w m (Step rel v st b))
      -> (v : a)
      -> (st,w)
      -> m (Step rel v (st,w) (b,w))
convW f v (s1,w1) =   (\(stp,w2) => bimap (,w2) (,w2) stp)
                  <$> unWriterT (f v s1) w1

public export
MonadRec m => MonadRec (WriterT w m) where
  tailRecM x ini acc f =
    MkWriterT $ \w1 => tailRecM x (ini,w1) acc (convW f)

---------------------------
-- RWST

convRWS :  Functor m
        => (f : (v : a) -> st -> RWST r w s m (Step rel v st b))
        -> (env : r)
        -> (v : a)
        -> (st,s,w)
        -> m (Step rel v (st,s,w) (b,s,w))
convRWS f env v (st1,s1,w1) =   (\(stp,s2,w2) => bimap (,s2,w2) (,s2,w2) stp)
                            <$> unRWST (f v st1) env s1 w1

public export
MonadRec m => MonadRec (RWST r w s m) where
  tailRecM x ini acc f =
    MkRWST $ \r1,s1,w1 => tailRecM x (ini,s1,w1) acc (convRWS f r1)