-- $ chcp 65001
-- $ idris
-- > :l G1UpperProverNewyear
module G1UpperProverNewyear

import G4LowerFormalPSpring
import G3MiddleFunctionsWinter
import G2Postulate

%default total

gx1 : GNat
gx1 = 17

-- 46
data IsProvable : GNat -> Type where
  Yes : (p ** True = proves p x) -> IsProvable x

postulate p315 : (c, d : GNat) -> True = proves c (forall d (r d)) -> IsProvable (r c)

||| Double negation elimination
--DNE : Type -> Type
--DNE p = Not $ Not p -> p
-- 二重否定除去
postulate dne : ((a -> Void) -> Void) -> a
dne2 : a -> ((a -> Void) -> Void)
dne2 x f = f x

||| The converse of contraposition: (p -> q) -> Not q -> Not p
Transposition : Type -> Type -> Type
Transposition p q = (Not q -> Not p) -> p -> q

-- E1
fuga2 : Transposition ((Not . IsProvable) 17) ((t:GNat) -> False = proves t 17)
fuga2 prf p = let ans1 = flip prf p in let ans2 = dne ans1 in ans2
fuga4 : (((t : Nat) -> False = proves t 17) -> Void) -> IsProvable 17
fuga3 : (((t : Nat) -> False = proves t 17) -> Void) -> (IsProvable 17 -> Void) -> Void
fuga3 = dne2 . fuga4
fuga5 : (IsProvable 17 -> Void) -> (t : Nat) -> False = proves t 17
fuga5 = fuga2 fuga3

hoge : IsProvable (forall 17 (r 17)) -> GNat
hoge (Yes dep) -- = ?rhs1
  = let s = fst dep in let prf = snd dep in
    let d5 = p315 s gx1 prf in ?rhs1

namespace a
  -- 46_2
  data IsProvable2 : Bool -> GNat -> Type where
    Yes : (p ** True = proves p x) -> IsProvable2 True x
    No : ((t : GNat) -> False = proves t x) -> IsProvable2 False x

  huga : IsProvable2 True (forall 17 (r 17)) -> GNat
  huga (Yes dep) = 3


import Data.String

Interpolation Bool where
  interpolate True = "TT"
  interpolate False = "FF"

Interpolation Nat where
  interpolate Z = "0"
  interpolate (S n) = "S \{n}"

Interpolation Integer where
  interpolate = show

test : String
test = "\{2}\{True}: \{True}\{True}"


module FreeSelectiveDelivery

-- Based on https://www.staff.ncl.ac.uk/andrey.mokhov/selective-functors.pdf,
--          https://www.cs.ox.ac.uk/jeremy.gibbons/publications/delivery.pdf

interface Applicative f => Selective (f : Type -> Type) where
  select : f (Either a b) -> f (a -> b) -> f b

data Select : (Type -> Type) -> Type -> Type where
  Pure : a -> Select f a
  SelectC : Select f (Either a b) -> f (a -> b) -> Select f b

Functor f => Functor (Select f) where
  map f (Pure x) = Pure $ f x
  map f (SelectC fe ff) = SelectC (map (map f) fe) (map (f .) ff)

Functor f => Applicative (Select f) where
  pure x = Pure x

  (Pure f) <*> fa = map f fa
  (SelectC fe ff) <*> fa = SelectC (pure (\e, a => either (\x => Left (x, a)) (\f => Right (f a)) e) <*> fe <*> fa)
                                   (map uncurry ff)

Functor f => Selective (Select f) where
  select fe (Pure f) = map (either f id) fe
  select fe (SelectC fe' ff') = SelectC (select (map f fe) (map g fe')) (map uncurry ff')
    where
      f : {a, a', b : Type} -> Either a b -> Either a (Either (a', a) b)
      f e = map Right e
      g : {a, a', b : Type} -> Either a' (a -> b) -> a -> Either (a', a) b
      g e a = either (\a' => Left (a', a)) (\f => Right $ f a) e

runSelect : Selective g => ({x : Type} -> f x -> g x) -> Select f a -> g a
runSelect nat (Pure x) = pure x
runSelect nat (SelectC fe ff) = select (runSelect nat fe) (nat ff)

whenS : Selective f => f Bool -> f () -> f ()
whenS fb fu = select (selector fb) (effect fu)
  where
    selector : f Bool -> f (Either () ())
    selector fb = map (\x => if x then Left () else Right ()) fb
    effect : f () -> f (() -> ())
    effect fu = map const fu

Selective IO where
  select fb ff = fb >>= \e => case e of Left a => map (\f => f a) ff
                                        Right b => pure b

data Teletype : Type -> Type where
  GetLine : Teletype String
  PutStrLn : String -> Teletype ()

interface Serialize (f : Type -> Type) where
  serialize : f a -> (String, String)
interface Deserialize (f : Type -> Type) where
  deserialize : f a -> (String, String) -> Maybe (f a)

Serialize Teletype where
  serialize GetLine = ("GetLine", "")
  serialize (PutStrLn x) = ("PutStrLn", x)

Deserialize Teletype where
  deserialize GetLine ("GetLine", "") = Just $ GetLine
  deserialize (PutStrLn _) ("PutStrLn", s) = Just $ PutStrLn s
  deserialize _ _ = Nothing

data Action : (Type -> Type) -> Type -> Type where
  ActionC : f r -> (r -> a) -> Action f a

Functor (Action f) where
  map f (ActionC c k) = ActionC c (f . k)

Program : (Type -> Type) -> Type -> Type
Program f a = Select (Action f) a

serializeP : Serialize f => Program f a -> List (String, String)
serializeP (Pure _) = []
serializeP (SelectC fs (ActionC c _)) = serialize c :: serializeP fs

deserializeP : Deserialize f => Program f a -> List (String, String) -> Maybe (Program f a)
deserializeP (Pure x) [] = Just $ Pure x
deserializeP (SelectC ff (ActionC c k)) (s :: ss) with (deserialize c s)
  deserializeP (SelectC ff (ActionC c k)) (s :: ss) | Nothing = Nothing
  deserializeP (SelectC ff (ActionC c k)) (s :: ss) | (Just c') = deserializeP ff ss >>= (\ff' => pure $ SelectC ff' (ActionC c' k))
deserializeP _ _ = Nothing

effect : f r -> Program f r
effect c = SelectC (Pure (Left ())) (ActionC c const)

getLine : Program Teletype String
getLine = effect GetLine

putStrLn : String -> Program Teletype ()
putStrLn s = effect (PutStrLn s)

pingPongS : String -> Program Teletype ()
pingPongS s = whenS (map (== "ping") getLine) (putStrLn s)

toIO : Action Teletype a -> IO a
toIO (ActionC GetLine k) = getLine >>= (\x => pure $ k x)
toIO (ActionC (PutStrLn s) k) = putStrLn s >>= (\x => pure $ k x)

testIO : IO ()
testIO = runSelect toIO (pingPongS "pong")

testSerialize : List (String, String)
testSerialize = serializeP (pingPongS "ppong")

testDeserialize : IO ()
testDeserialize = case deserializeP (pingPongS "pong") testSerialize of
                      Nothing => pure ()
                      Just p => runSelect toIO p


module Main

import Data.Vect

----------------------------------------------------------------------
-- Hilffunktion

remove : (x : a) -> (xs : Vect (S n) a) -> { auto prf : Elem x xs } -> Vect n a
remove {prf = Here} x (x :: ys) = ys
remove {prf = (There Here)} x (y :: (x :: xs)) = y :: xs
remove {prf = (There (There later))} x (y :: (z :: xs)) = y :: remove x (z::xs)

----------------------------------------------------------------------
-- Spiel-Zustand

data GameState : (guessesRemaining : Nat) -> (letters : Nat) -> Type where
  MkGameState : (word : String)
                -> (missing : Vect letters Char)
                -> GameState guessesRemaining letters

data Finished : Type where
  Lost : (game : GameState 0 (S letters)) -> Finished
  Won : (game : GameState (S guesses) 0) -> Finished

----------------------------------------------------------------------
-- Spielereingabe prüfen

data ValidInput : List Char -> Type where
  Letter : (c : Char) -> ValidInput [c]

toFew : ValidInput [] -> Void
toFew (Letter _) impossible

toMuch : ValidInput (c::c'::cs) -> Void
toMuch (Letter _) impossible

isValidInput : (cs : List Char) -> Dec (ValidInput cs)
isValidInput [] = No toFew
isValidInput [c] = Yes (Letter c)
isValidInput (c::c'::cs) = No toMuch

isValidString : (s : String) -> Dec (ValidInput (unpack s))
isValidString s = isValidInput _

----------------------------------------------------------------------
-- Eingabe

readGuess : IO (c ** ValidInput c)
readGuess = do
  putStrLn "Buchstabe? "
  input <- getLine
  case isValidString (toUpper input) of
    Yes prf => pure (_ ** prf)
    No _ => do
      print "ungültige Eingabe"
      readGuess

----------------------------------------------------------------------
-- Verarbeitung

processGuess : (letter : Char) ->
               GameState (S guesses) (S letters) ->
               Either (GameState guesses (S letters))
                      (GameState (S guesses) letters)
processGuess letter (MkGameState word missing) =
  case isElem letter missing of
    Yes ind => Right (MkGameState word (remove letter missing))
    No _ => Left (MkGameState word missing)

----------------------------------------------------------------------
-- Spiel-Funktion

game : GameState (S guesses) (S letters) -> IO Finished
game {guesses} {letters} gameState = do
  (_ ** Letter letter) <- readGuess
  case processGuess letter gameState of
    Left nope => do
      putStrLn "falsch geraten"
      case guesses of
        Z => pure (Lost nope)
        S k => game nope
    Right yeah => do
      putStrLn "richtig geraten"
      case letters of
        Z => pure (Won yeah)
        S k => game yeah

----------------------------------------------------------------------
-- Main

main : IO ()
main = do
  ergebnis <- game {guesses=5}
	(MkGameState "DevOpenSpace" ['D','E','V','O','P','N','S','A','C'])
  case ergebnis of
    Lost (MkGameState word _) =>
      putStrLn ("Verloren - das Wort war " ++ word)
    Won _ =>
      putStrLn "Gewonnen!"


Ty : Type
Ty = (Ty   : Type)
 ->  (nat  : Ty)
 ->  (top  : Ty)
 ->  (bot  : Ty)
 ->  (arr  : Ty -> Ty -> Ty)
 ->  (prod : Ty -> Ty -> Ty)
 ->  (sum  : Ty -> Ty -> Ty)
 ->  Ty

nat : Ty
nat = \ _, nat, _, _, _, _, _ => nat
top : Ty
top = \ _, _, top, _, _, _, _ => top
bot : Ty
bot = \ _, _, _, bot, _, _, _ => bot

arr : Ty-> Ty-> Ty
arr = \ a, b, ty, nat, top, bot, arr, prod, sum =>
     arr (a ty nat top bot arr prod sum) (b ty nat top bot arr prod sum)

prod : Ty-> Ty-> Ty
prod = \ a, b, ty, nat, top, bot, arr, prod, sum =>
     prod (a ty nat top bot arr prod sum) (b ty nat top bot arr prod sum)

sum : Ty-> Ty-> Ty
sum = \ a, b, ty, nat, top, bot, arr, prod, sum =>
     sum (a ty nat top bot arr prod sum) (b ty nat top bot arr prod sum)

Con : Type
Con = (Con : Type)
 -> (nil  : Con)
 -> (snoc : Con -> Ty-> Con)
 -> Con

nil : Con
nil = \ con, nil, snoc => nil

snoc : Con -> Ty-> Con
snoc = \ g, a, con, nil, snoc => snoc (g con nil snoc) a

Var : Con -> Ty-> Type
Var = \ g, a =>
    (Var : Con -> Ty-> Type)
 -> (vz  : {g:_}->{a:_} -> Var (snoc g a) a)
 -> (vs  : {g:_}->{b:_}->{a:_} -> Var g a -> Var (snoc g b) a)
 -> Var g a

vz : {g:_}->{a:_} -> Var (snoc g a) a
vz = \ var, vz, vs => vz

vs : {g:_}->{b:_}->{a:_} -> Var g a -> Var (snoc g b) a
vs = \ x, var, vz, vs => vs (x var vz vs)

Tm : Con -> Ty-> Type
Tm = \ g, a =>
    (Tm    : Con -> Ty-> Type)
 -> (var   : {g:_}->{a:_}-> Var g a -> Tm g a)
 -> (lam   : {g:_}->{a:_}->{b:_} -> Tm (snoc g a) b -> Tm g (arr a b))
 -> (app   : {g:_}->{a:_}->{b:_} -> Tm g (arr a b) -> Tm g a -> Tm g b)
 -> (tt    : {g:_}-> Tm g top)
 -> (pair  : {g:_}->{a:_}->{b:_} -> Tm g a -> Tm g b -> Tm g (prod a b))
 -> (fst   : {g:_}->{a:_}->{b:_} -> Tm g (prod a b) -> Tm g a)
 -> (snd   : {g:_}->{a:_}->{b:_} -> Tm g (prod a b) -> Tm g b)
 -> (left  : {g:_}->{a:_}->{b:_} -> Tm g a -> Tm g (sum a b))
 -> (right : {g:_}->{a:_}->{b:_} -> Tm g b -> Tm g (sum a b))
 -> (split : {g:_}->{a:_}->{b:_}-> {c:_} -> Tm g (sum a b) -> Tm g (arr a c) -> Tm g (arr b c) -> Tm g c)
 -> (zero  : {g:_}-> Tm g nat)
 -> (suc   : {g:_}-> Tm g nat -> Tm g nat)
 -> (rec   : {g:_}->{a:_} -> Tm g nat -> Tm g (arr nat (arr a a)) -> Tm g a -> Tm g a)
 -> Tm g a

var : {g:_}->{a:_} -> Var g a -> Tm g a
var = \ x, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec => var x

lam : {g:_}->{a:_}->{b:_}-> Tm (snoc g a) b -> Tm g (arr a b)
lam = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     lam (t tm var lam app tt pair fst snd left right split zero suc rec)

app : {g:_}->{a:_}->{b:_} -> Tm g (arr a b) -> Tm g a -> Tm g b
app = \ t, u, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     app (t tm var lam app tt pair fst snd left right split zero suc rec)
         (u tm var lam app tt pair fst snd left right split zero suc rec)

tt : {g:_} -> Tm g Main.top
tt = \ tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec => tt

pair : {g:_}->{a:_}->{b:_} -> Tm g a -> Tm g b -> Tm g (prod a b)
pair = \ t, u, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     pair (t tm var lam app tt pair fst snd left right split zero suc rec)
          (u tm var lam app tt pair fst snd left right split zero suc rec)

fst : {g:_}->{a:_}->{b:_}-> Tm g (prod a b) -> Tm g a
fst = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     fst (t tm var lam app tt pair fst snd left right split zero suc rec)

snd : {g:_}->{a:_}->{b:_} -> Tm g (prod a b) -> Tm g b
snd = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     snd (t tm var lam app tt pair fst snd left right split zero suc rec)

left : {g:_}->{a:_}->{b:_} -> Tm g a -> Tm g (sum a b)
left = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     left (t tm var lam app tt pair fst snd left right split zero suc rec)

right : {g:_}->{a:_}->{b:_} -> Tm g b -> Tm g (sum a b)
right = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     right (t tm var lam app tt pair fst snd left right split zero suc rec)

split : {g:_}->{a:_}->{b:_}->{c:_} -> Tm g (sum a b) -> Tm g (arr a c) -> Tm g (arr b c) -> Tm g c
split = \ t, u, v, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     split (t tm var lam app tt pair fst snd left right split zero suc rec)
          (u tm var lam app tt pair fst snd left right split zero suc rec)
          (v tm var lam app tt pair fst snd left right split zero suc rec)

zero : {g:_} -> Tm g Main.nat
zero = \ tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec => zero

suc : {g:_} -> Tm g Main.nat -> Tm g Main.nat
suc = \ t, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
   suc (t tm var lam app tt pair fst snd left right split zero suc rec)

rec : {g:_}->{a:_} -> Tm g Main.nat -> Tm g (arr Main.nat (arr a a)) -> Tm g a -> Tm g a
rec = \ t, u, v, tm, var, lam, app, tt, pair, fst, snd, left, right, split, zero, suc, rec =>
     rec (t tm var lam app tt pair fst snd left right split zero suc rec)
         (u tm var lam app tt pair fst snd left right split zero suc rec)
         (v tm var lam app tt pair fst snd left right split zero suc rec)

v0 : {g:_}->{a:_} -> Tm (snoc g a) a
v0 = var vz

v1 : {g:_}->{a:_}->{b:_} -> Tm (snoc (snoc g a) b) a
v1 = var (vs vz)

v2 : {g:_}->{a:_}->{b:_}->{c:_} -> Tm (snoc (snoc (snoc g a) b) c) a
v2 = var (vs (vs vz))

v3 : {g:_}->{a:_}->{b:_}->{c:_}->{d:_} -> Tm (snoc (snoc (snoc (snoc g a) b) c) d) a
v3 = var (vs (vs (vs vz)))

tbool : Ty
tbool = sum top top

ttrue : {g:_} -> Tm g Main.tbool
ttrue = left tt

tfalse : {g:_} -> Tm g Main.tbool
tfalse = right tt

ifthenelse : {g:_}->{a:_} -> Tm g (arr Main.tbool (arr a (arr a a)))
ifthenelse = lam (lam (lam (split v2 (lam v2) (lam v1))))

times4 : {g:_}->{a:_} -> Tm g (arr (arr a a) (arr a a))
times4  = lam (lam (app v1 (app v1 (app v1 (app v1 v0)))))

add : {g:_} -> Tm g (arr Main.nat (arr Main.nat Main.nat))
add = lam (rec v0
       (lam (lam (lam (suc (app v1 v0)))))
       (lam v0))

mul : {g:_} -> Tm g (arr Main.nat (arr Main.nat Main.nat))
mul = lam (rec v0
       (lam (lam (lam (app (app add (app v1 v0)) v0))))
       (lam zero))

fact : {g:_} -> Tm g (arr Main.nat Main.nat)
fact = lam (rec v0 (lam (lam (app (app mul (suc v1)) v0)))
                 (suc zero))


module Prelude.Basics

import Builtin

import Prelude.Ops

%default total

public export
Not : Type -> Type
Not x = x -> Void

-----------------------
-- UTILITY FUNCTIONS --
-----------------------

||| Manually assign a type to an expression.
||| @ a the type to assign
||| @ x the element to get the type
public export %inline
the : (0 a : Type) -> (x : a) -> a
the _ x = x

||| Identity function.
public export %inline
id : (x : a) -> a
id x = x

||| Function that duplicates its input
public export
dup : a -> (a, a)
dup x = (x, x)

||| Constant function.  Ignores its second argument.
public export %inline
const : a -> b -> a
const x = \value => x

||| Function composition.
public export %inline
(.) : (b -> c) -> (a -> b) -> a -> c
(.) f g = \x => f (g x)

||| Composition of a two-argument function with a single-argument one.
||| `(.:)` is like `(.)` but the second argument and the result are two-argument functions.
||| This operator is also known as "blackbird operator".
public export %inline
(.:) : (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)

||| `on b u x y` runs the binary function b on the results of applying
||| unary function u to two arguments x and y. From the opposite perspective,
||| it transforms two inputs and combines the outputs.
|||
||| ```idris example
||| ((+) `on` f) x y = f x + f y
||| ```
|||
||| Typical usage:
|||
||| ```idris example
||| sortBy (compare `on` fst).
||| ```
public export
on : (b -> b -> c) -> (a -> b) -> a -> a -> c
on f g = \x, y => g x `f` g y

infixl 0 `on`

||| Takes in the first two arguments in reverse order.
||| @ f the function to flip
public export
flip : (f : a -> b -> c) -> b -> a -> c
flip f x y = f y x

||| Function application.
public export
apply : (a -> b) -> a -> b
apply f a = f a

public export
curry : ((a, b) -> c) -> a -> b -> c
curry f a b = f (a, b)

public export
uncurry : (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b

-- $ is compiled specially to shortcut any tricky unification issues, but if
-- it did have a type this is what it would be, and it might be useful to
-- use directly sometimes (e.g. in higher order functions)
public export
($) : forall a, b . ((x : a) -> b x) -> (x : a) -> b x
($) f a = f a

-------------------
-- PROOF HELPERS --
-------------------

||| Equality is a congruence.
public export
cong : (0 f : t -> u) -> (p : a = b) -> f a = f b
cong f Refl = Refl

||| Two-holed congruence.
export
-- These are natural in equational reasoning.
cong2 : (0 f : t1 -> t2 -> u) -> (p1 : a = b) -> (p2 : c = d) -> f a c = f b d
cong2 f Refl Refl = Refl

||| Irrelevant equalities can always be made relevant
export
irrelevantEq : (0 _ : a === b) -> a === b
irrelevantEq Refl = Refl

--------------
-- BOOLEANS --
--------------

||| Boolean Data Type.
public export
data Bool = False | True

||| Boolean NOT.
%inline
public export
not : (b : Bool) -> Bool
not True = False
not False = True

||| Boolean AND only evaluates the second argument if the first is `True`.
%inline
public export
(&&) : (b : Bool) -> Lazy Bool -> Bool
(&&) True x = x
(&&) False x = False

||| Boolean OR only evaluates the second argument if the first is `False`.
%inline
public export
(||) : (b : Bool) -> Lazy Bool -> Bool
(||) True x = True
(||) False x = x

||| Non-dependent if-then-else
%inline
public export
ifThenElse : (b : Bool) -> Lazy a -> Lazy a -> a
ifThenElse True l r = l
ifThenElse False l r = r

%inline
public export
intToBool : Int -> Bool
intToBool 0 = False
intToBool x = True

--------------
-- LISTS    --
--------------

||| Generic lists.
public export
data List a =
  ||| Empty list
  Nil

  | ||| A non-empty list, consisting of a head element and the rest of the list.
  (::) a (List a)

%name List xs, ys, zs

||| Snoc lists.
public export
data SnocList a =
  ||| Empty snoc-list
  Lin

  | ||| A non-empty snoc-list, consisting of the rest of the snoc-list and the final element.
  (:<) (SnocList a) a

%name SnocList sx, sy, sz


module Hoare

import Assn
import Expr
import Imp
import Logic
import Maps

%access public export

%default total

HoareTriple : (p : Assertion) -> (c : Com) -> (q : Assertion) -> Type
HoareTriple p c q = (st, st' : State) -> (c / st \\ st') -> p st -> q st'

hoare_post_true : ((st : State) -> q st) -> HoareTriple p c q
hoare_post_true f _ st' _ _ = f st'

hoare_pre_false : ((st : State) -> Not (p st)) -> HoareTriple p c q
hoare_pre_false f = \st, st', rel, p_st => absurd $ f st p_st

hoare_assign : (q : Assertion) -> HoareTriple (AssignSub x a q) (x ::= a) q
hoare_assign q = \st, _, (E_Ass prf), q_st => rewrite sym prf in q_st

assn_sub_example : HoareTriple (AssignSub X (X + 1) (\st => LT (st X) 5))
                               (X ::= X + 1)
                               (\st => LT (st X) 5)
assn_sub_example = hoare_assign (\st => LT (st X) 5)

hoare_assign_fwd : (m : Nat) -> (a : AExp) -> (p : Assertion) ->
                   HoareTriple (\st => (p st, st X = m))
                               (X ::= a)
                               (\st => ( p (t_update X m st)
                                       , st X = aeval (t_update X m st) a ))
hoare_assign_fwd m a p st _ (E_Ass {n} prf1) (p_st, prf2) =
  rewrite sym prf2
  in rewrite trans (t_update_shadow {x=X} {v2=st X} {v1=n} {m=st})
                   (t_update_same {x=X} {m=st})
  in (p_st, sym prf1)

hoare_assign_fwd_exists : (a : AExp) -> (p : Assertion) ->
                          HoareTriple
                            (\st => p st)
                            (X ::= a)
                            (\st => (m ** ( p (t_update X m st)
                                          , st X = aeval (t_update X m st) a )))
hoare_assign_fwd_exists a p st st' rel p_st with (st X) proof prf2
  hoare_assign_fwd_exists a p st st' rel p_st | m =
    (m ** hoare_assign_fwd m a p st st' rel (p_st, sym prf2))

hoare_consequence_pre : (p, p', q : Assertion) -> HoareTriple p' c q ->
                        (p ->> p') -> HoareTriple p c q
hoare_consequence_pre p p' q ht imp = \st, st', rel, p_st =>
  ht st st' rel (imp st p_st)

hoare_consequence_post : (p, q, q' : Assertion) -> HoareTriple p c q' ->
                         (q' ->> q) -> HoareTriple p c q
hoare_consequence_post p q q' ht imp = \st, st', rel, p_st =>
  imp st' (ht st st' rel p_st)

hoare_assign_example_1 : HoareTriple (const ()) (X ::= 1) (\st => st X = 1)
hoare_assign_example_1 =
  hoare_consequence_post (const ())
                         (\st => st X = 1)
                         (\st => (m : Nat ** ((), st X = 1)))
                         (hoare_assign_fwd_exists 1 (const ()))
                         (\_, (_ ** (_, prf)) => prf)

hoare_assign_example_2 : HoareTriple (\st => LT (st X) 4)
                                     (X ::= X + 1)
                                     (\st => LT (st X) 5)
hoare_assign_example_2 =
  hoare_consequence_pre (\st => LT (st X) 4)
                        (\st => LT (st X + 1) 5)
                        (\st => LT (st X) 5)
                        (hoare_assign (\st => LT (st X) 5))
                        (\st, p_st => replace {P=\x => LT x 5}
                                              (sym (plusCommutative (st X) 1))
                                              (LTESucc p_st))

hoare_consequence : (p, p', q, q' : Assertion) -> HoareTriple p' c q' ->
                    p ->> p' -> q' ->> q -> HoareTriple p c q
hoare_consequence p p' q q' ht p_imp_p' q'_imp_q =
  let ht' = hoare_consequence_pre p p' q' ht p_imp_p'
  in hoare_consequence_post p q q' ht' q'_imp_q

hoare_assign_example_5 : HoareTriple (\st => LTE (st X + 1) 5)
                                     (X ::= X + 1)
                                     (\st => LTE (st X) 5)
hoare_assign_example_5 = hoare_assign (\st => LTE (st X) 5)

hoare_assign_example_6 : HoareTriple (\st => (LTE 0 3, LTE 3 5))
                                     (X ::= 3)
                                     (\st => (LTE 0 (st X), LTE (st X) 5))
hoare_assign_example_6 = hoare_assign (\st => (LTE 0 (st X), LTE (st X) 5))

hoare_skip : (p : Assertion) -> HoareTriple p SKIP p
hoare_skip _ _ _ E_Skip p_st = p_st

hoare_seq : (p, q, r : Assertion) -> HoareTriple q c2 r -> HoareTriple p c1 q ->
            HoareTriple p (do c1; c2) r
hoare_seq p q r ht2 ht1 = \st, st', (E_Seq {st2} cc1 cc2), p_st =>
  let q_st2 = ht1 st st2 cc1 p_st
  in ht2 st2 st' cc2 q_st2

hoare_assign_example_3 : (a : AExp) -> (n : Nat) ->
                         HoareTriple (\st => aeval st a = n)
                                     (do X ::= a; SKIP)
                                     (\st => st X = n)
hoare_assign_example_3 a n =
  let hta = hoare_assign_fwd_exists a (\st => aeval st a = n)
      hta' = hoare_consequence_post
               (\st => aeval st a = n)
               (\st => st X = n)
               (\st => (m : Nat ** ( aeval (t_update X m st) a = n
                                   , st X = aeval (t_update X m st) a )))
               hta
               (\_, (_ ** (aeval_eq_n, st_X_eq_aeval)) =>
                  trans st_X_eq_aeval aeval_eq_n)
      hts = hoare_skip (\st => st X = n)
  in hoare_seq (\st => aeval st a = n) (\st => st X = n) (\st => st X = n)
               hts hta'

hoare_assign_example_4 : HoareTriple (const ())
                                     (do X ::= 1; Y ::= 2)
                                     (\st => (st X = 1, st Y = 2))
hoare_assign_example_4 =
  let htx = hoare_consequence_post
              (const ())
              (\st => st X = 1)
              (\st => (m : Nat ** ((), st X = 1)))
              (hoare_assign_fwd_exists 1 (const ()))
              (\_, (_ ** (_, q_st)) => q_st)
      hty = hoare_consequence_pre
              (\st => st X = 1)
              (AssignSub Y 2 (\st => (st X = 1, st Y = 2)))
              (\st => (st X = 1, st Y = 2))
              (hoare_assign (\st => (st X = 1, st Y = 2)))
              (\_, p_st => (p_st, Refl))
  in hoare_seq (const ()) (\st => st X = 1) (\st => (st X = 1, st Y = 2))
               hty htx

swap_program : Com
swap_program = do Z ::= X
                  X ::= Y
                  Y ::= Z

swap_exercise : HoareTriple (\st => LTE (st X) (st Y))
                            Hoare.swap_program
                            (\st => LTE (st Y) (st X))
swap_exercise =
  let htz = hoare_consequence_pre
              (\st => LTE (st X) (st Y))
              (AssignSub Z X (\st => LTE (st Z) (st Y)))
              (\st => LTE (st Z) (st Y))
              (hoare_assign (\st => LTE (st Z) (st Y)))
              (\_, p_st => p_st)
      htx = hoare_consequence_pre (\st => LTE (st Z) (st Y))
                                  (AssignSub X Y (\st => LTE (st Z) (st X)))
                                  (\st => LTE (st Z) (st X))
                                  (hoare_assign (\st => LTE (st Z) (st X)))
                                  (\_, p_st => p_st)
      hty = hoare_consequence_pre (\st => LTE (st Z) (st X))
                                  (AssignSub Y Z (\st => LTE (st Y) (st X)))
                                  (\st => LTE (st Y) (st X))
                                  (hoare_assign (\st => LTE (st Y) (st X)))
                                  (\_, p_st => p_st)
      htxy = hoare_seq (\st => LTE (st Z) (st Y))
                       (\st => LTE (st Z) (st X))
                       (\st => LTE (st Y) (st X))
                       hty
                       htx
  in hoare_seq (\st => LTE (st X) (st Y))
               (\st => LTE (st Z) (st Y))
               (\st => LTE (st Y) (st X))
               htxy
               htz

hoare_if : (p, q : Assertion) ->
           HoareTriple (\st => (p st, BAssn b st)) c1 q ->
           HoareTriple (\st => (p st, Not (BAssn b st))) c2 q ->
           HoareTriple p (CIf b c1 c2) q
hoare_if p q ht1 ht2 = \st, st', rel, p_st => case rel of
  E_IfTrue prf cc1 => ht1 st st' cc1 (p_st, prf)
  E_IfFalse prf cc2 => ht2 st st' cc2 (p_st, bexp_eval_false prf)

if_example : HoareTriple (const ())
                         (CIf (X == 0) (Y ::= 2) (Y ::= X + 1))
                         (\st => LTE (st X) (st Y))
if_example =
  let htt = hoare_consequence
              (\st => ((), BAssn (X == 0) st))
              (AssignSub Y 2 (\st => (LTE (st X) (st Y), BAssn (X == 0) st)))
              (\st => LTE (st X) (st Y))
              (\st => (LTE (st X) (st Y), BAssn (X == 0) st))
              (hoare_assign (\st => (LTE (st X) (st Y), BAssn (X == 0) st)))
              (\st, (_, p_st) => ( replace {P=\x => LTE x 2}
                                           (sym (fst (nat_beq_iff (st X) 0)
                                                p_st))
                                           LTEZero
                                 , p_st ))
              (\_, (q_st, _) => q_st)
      htf = hoare_consequence
              (\st => ((), Not (BAssn (X == 0) st)))
              (AssignSub Y (X + 1) (\st => ( LTE (st X) (st Y)
                                           , Not (BAssn (X == 0) st) )))
              (\st => LTE (st X) (st Y))
              (\st => (LTE (st X) (st Y), Not (BAssn (X == 0) st)))
              (hoare_assign (\st => ( LTE (st X) (st Y)
                                      , Not (BAssn (X == 0) st) )))
              (\st, (_, p_st) => (lteAddRight (st X), p_st))
              (\_, (q_st, _) => q_st)
  in hoare_if (const ()) (\st => LTE (st X) (st Y)) htt htf

if_minus_plus : HoareTriple (const ())
                            (CIf (X <= Y) (Z ::= Y - X) (Y ::= X + Z))
                            (\st => st Y = st X + st Z)
if_minus_plus =
  let htt = hoare_consequence
              (\st => ((), BAssn (X <= Y) st))
              (AssignSub Z (Y - X) (\st => ( st Y = st X + st Z
                                           , BAssn (X <= Y) st )))
              (\st => st Y = st X + st Z)
              (\st => (st Y = st X + st Z, BAssn (X <= Y) st))
              (hoare_assign (\st => (st Y = st X + st Z, BAssn (X <= Y) st)))
              (\st, (_, p_st) =>
                 ( sym (lte_plus_minus (fst (lte_beq_iff (st X) (st Y)) p_st))
                 , p_st ))
              (\_, (q_st, _) => q_st)
      htf = hoare_consequence_pre
              (\st => ((), Not (BAssn (X <= Y) st)))
              (AssignSub Y (X + Z) (\st => st Y = st X + st Z))
              (\st => st Y = st X + st Z)
              (hoare_assign (\st => st Y = st X + st Z))
              (\_, _ => Refl)
  in hoare_if (const ()) (\st => st Y = st X + st Z) htt htf

hoare_while : {b : BExp} -> {c : Com} -> (p : Assertion) ->
              HoareTriple (\st => (p st, BAssn b st)) c p ->
              HoareTriple p (WHILE b c) (\st => (p st, Not (BAssn b st)))
hoare_while {b} {c} p ht st _ (E_WhileEnd prf) p_st =
  (p_st, snd not_true_iff_false prf)
hoare_while {b} {c} p ht st st' (E_WhileLoop {st1} prf cbody cnext) p_st =
  hoare_while p ht st1 st' cnext (ht st st1 cbody (p_st, prf))

while_example : HoareTriple (\st => LTE (st X) 3)
                            (CWhile (X <= 2) (X ::= X + 1))
                            (\st => st X = 3)
while_example st st' rel lte_prf =
  let htc =  hoare_assign (\st => LTE (st X) 3)
      htc' = hoare_consequence_pre
               (\st => (LTE (st X) 3, beval st (X <= 2) = True))
               (\st => LTE (st X + 1) 3)
               (\st => LTE (st X) 3)
               htc
               (\st, p_st => replace {P=\x => LTE x 3}
                                     (sym (plusCommutative (st X) 1))
                                     (LTESucc (fst (lte_beq_iff (st X) 2)
                                                   (snd p_st))))
      htw = hoare_while (\st => LTE (st X) 3) htc'
      (below, contra) = htw st st' rel lte_prf
  in bounded__eq below (fst (lte_nbeq_iff (st' X) 2)
                            (fst not_true_iff_false contra))

always_loop_hoare : (p, q : Assertion) -> HoareTriple p (WHILE BTrue SKIP) q
always_loop_hoare p q =
  let htc = hoare_consequence_pre
              (\st => (p st, BAssn BTrue st))
              p
              p
              (hoare_skip p)
              (\st, (p_st, _) => p_st)
      htw = hoare_while p htc
  in hoare_consequence_post
           p
           q
           (\st => (p st, Not (BAssn BTrue st)))
           htw
           (\st, (_, contra) => absurd (contra Refl))

hoare_if1 : {b : BExp} -> {c : Com} -> (p, q : Assertion) ->
            HoareTriple (\st => (p st, BAssn b st)) c q ->
            (\st => (p st, Not (BAssn b st))) ->> q ->
            HoareTriple p (CIf1 b c) q
hoare_if1 {b} {c} p q htc _ st st' (E_If1True prf cc) p_st =
  htc st st' cc (p_st, prf)
hoare_if1 {b} {c} p q _ imp st _ (E_If1False prf) p_st =
  imp st (p_st, snd not_true_iff_false prf)

hoare_if1_good : HoareTriple (\st => st X + st Y = st Z)
                             (IF1 not (Y == 0) THEN
                                  X ::= X + Y
                              FI)
                             (\st => st X = st Z)
hoare_if1_good =
  let htc = hoare_consequence
              (\st => (st X + st Y = st Z, not (st Y == 0) = True))
              (AssignSub X (X + Y)
                         (\st => (st X = st Z, not (st Y == 0) = True)))
              (\st => st X = st Z)
              (\st => (st X = st Z, not (st Y == 0) = True))
              (hoare_assign (\st => (st X = st Z, not (st Y == 0) = True)))
              (\_, p_st => p_st)
              (\st, (q_st, _) => q_st)
      hts = hoare_consequence
              (\st => (st X + st Y = st Z, Not (not (st Y == 0) = True)))
              (\st => (st X = st Z, Not (not (st Y == 0) = True)))
              (\st => st X = st Z)
              (\st => (st X = st Z, Not (not (st Y == 0) = True)))
              (hoare_skip (\st => (st X = st Z, Not (not (st Y == 0) = True))))
              (\st, (plus_st_X_st_Y__st_Z, prf) =>
                  let st_Y_eq_0 = fst (nat_beq_iff (st Y) 0)
                                      (trans (sym (notInvolutive (st Y == 0)))
                                             (cong {f=not}
                                                   (fst not_true_iff_false prf)))
                      pf = trans (sym (plusZeroRightNeutral (st X)))
                                 (replace {P=\x => st X + x = st Z}
                                          st_Y_eq_0 plus_st_X_st_Y__st_Z)
                  in (pf, prf))
              (\st, (q_st, _) => q_st)
      imp = \st, (plus_st_X_st_Y__st_Z, prf) =>
        let st_Y_eq_0 = fst (nat_beq_iff (st Y) 0)
                            (trans (sym (notInvolutive (st Y == 0)))
                                   (cong {f=not} (fst not_true_iff_false prf)))
        in trans (sym (plusZeroRightNeutral (st X)))
                 (replace {P=\x => st X + x = st Z}
                          st_Y_eq_0 plus_st_X_st_Y__st_Z)
  in hoare_if1 (\st => st X + st Y = st Z) (\st => st X = st Z) htc imp

hoare_for : {init, updt, body : Com} -> {cond : BExp} -> (p, q : Assertion) ->
            HoareTriple p init q ->
            HoareTriple (\st => (q st, BAssn cond st)) (do body; updt) q ->
            HoareTriple p
                        (CFor init cond updt body)
                        (\st => (q st, Not (BAssn cond st)))
hoare_for {init} {updt} {body} {cond} p q ht_init ht_body_updt st st'
          (E_For ci (E_WhileEnd prf)) p_st =
  (ht_init st st' ci p_st, bexp_eval_false prf)
hoare_for {init} {updt} {body} {cond} p q ht_init ht_body_updt st st'
          (E_For ci {st2} (E_WhileLoop {st1} prf cb cn)) p_st =
  let q_st2 = ht_init st st2 ci p_st
      q_st1 = ht_body_updt st2 st1 cb (q_st2, prf)
  in hoare_while q ht_body_updt st1 st' cn q_st1

hoare_repeat : {c : Com} -> {b : BExp} -> (p, q : Assertion) ->
               HoareTriple p c q -> (\st => (q st, Not (BAssn b st))) ->> p ->
               HoareTriple p (CRepeat c b) (\st => (q st, BAssn b st))
hoare_repeat {c} {b} p q htc imp st st' (E_Repeat cc (E_WhileEnd prf)) p_st =
  let q_st' = htc st st' cc p_st
      btrue = trans (sym (notInvolutive (beval st' b))) (cong {f=not} prf)
  in (q_st', btrue)
hoare_repeat {c} {b} p q htc imp st st'
             r@(E_Repeat {st1} cc1 (E_WhileLoop prf cc2 cnext)) p_st =
  let q_st1 = htc st st1 cc1 p_st
      bfalse = bexp_eval_false (trans (sym (notInvolutive (beval st1 b)))
                                      (cong {f=not} prf))
      p_st1 = imp st1 (q_st1, bfalse)
  in hoare_repeat p q htc imp st1 st'
                  (assert_smaller r (E_Repeat cc2 cnext)) p_st1

hoare_repeat_good : HoareTriple (\st => LT 0 (st X))
                                (REPEAT do
                                   Y ::= X
                                   X ::= X - 1
                                 UNTIL X == 0 END)
                                (\st => (st X = 0, LT 0 (st Y)))
hoare_repeat_good =
  let hty = hoare_assign (\st => LT 0 (st Y))
      htx = hoare_assign (\st => LT 0 (st Y))
      htc = hoare_seq (\st => LT 0 (st X))
                      (\st => LT 0 (st Y))
                      (\st => LT 0 (st Y))
                      htx
                      hty
      ht_repeat = hoare_repeat
                    (\st => LT 0 (st X))
                    (\st => LT 0 (st Y))
                    htc
                    (\st, (_, prf) =>
                        notZeroImpliesGTZero
                          (fst (nat_nbeq_iff (st X) 0)
                          (fst not_true_iff_false prf)))
  in hoare_consequence_post (\st => LT 0 (st X))
                            (\st => (st X = 0, LT 0 (st Y)))
                            (\st => (LT 0 (st Y), BAssn (X == 0) st))
                            ht_repeat
                            (\st, (lte_prf, prf) =>
                              let pf = fst (nat_beq_iff (st X) 0) prf
                              in (pf, lte_prf))


module Toolkit.Data.DList.Interleaving

import public Toolkit.Data.List.Interleaving

import Toolkit.Data.DList

%default total

public export
data Interleaving : (a : Type)
                 -> (type : a -> Type)
                 -> {ls, rs, os : List a}
                 -> (lefts  : DList a type ls)
                 -> (rights : DList a type rs)
                 -> (orig   : DList a type os)
                 -> (prf    : Interleaving ls rs os)
                           -> Type
  where
    Empty : Interleaving a type Nil Nil Nil Empty

    Left  : {a : Type}
         -> {type : a -> Type}
         -> {x : a}
         -> {xs, ys, zs : List a}
         -> {ls   : DList a type xs}
         -> {rs   : DList a type ys}
         -> {os   : DList a type zs}
         -> {prf  : Interleaving xs ys zs}
         -> (head : type x)
         -> (tail : Interleaving a type        ls  rs        os         prf)
                 -> Interleaving a type (head::ls) rs (head::os) (x <:: prf)

    Right : {a : Type}
         -> {type : a -> Type}
         -> {y : a}
         -> {xs, ys, zs : List a}
         -> {ls   : DList a type xs}
         -> {rs   : DList a type ys}
         -> {os   : DList a type zs}
         -> {prf  : Interleaving xs ys zs}
         -> (head : type y)
         -> (tail : Interleaving a type ls        rs         os         prf)
                 -> Interleaving a type ls (head::rs) (head::os) (y ::> prf)

(<::) : {a : Type}
     -> {type : a -> Type}
     -> {x    : a}
     -> {xs, ys, zs : List a}
     -> {ls   : DList a type xs}
     -> {rs   : DList a type ys}
     -> {os   : DList a type zs}
     -> {prf  : Interleaving xs ys zs}
     -> (head : type x)
     -> (tail : Interleaving a type        ls  rs        os         prf)
             -> Interleaving a type (head::ls) rs (head::os) (x <:: prf)
(<::) = Left

(::>) : {a : Type}
     -> {type : a -> Type}
     -> {xs, ys, zs : List a}
     -> {ls   : DList a type xs}
     -> {rs   : DList a type ys}
     -> {os   : DList a type zs}
     -> {prf  : Interleaving xs ys zs}
     -> {y    : a}
     -> (head : type y)
     -> (tail : Interleaving a type ls        rs         os         prf)
             -> Interleaving a type ls (head::rs) (head::os) (y ::> prf)
(::>) = Right


twice : Char -> Char -> Char -> Char -> Nat -> (Nat, Nat)
twice w x y z Z = (Z, Z)
twice m n o p (S x)
    = case twice m n o p x of
           (a, b) => (S a, S b)

bothS : Int -> String -> (Nat, Nat) -> (Nat, Nat)
bothS test dummy = \(c, d) => (S c, S d)

pf : (x : Nat) -> twice 'a' 'b' 'c' 'd' (S x)
                     = bothS 99 "red balloons" (twice 'a' 'b' 'c' 'd' x)
pf k = Refl -- Refl


||| Reasoning about elements in a DList based on their index.
|||
||| Module    : DList/AtIndex.idr
||| Copyright : (c) Jan de Muijnck-Hughes
||| License   : see LICENSE
|||
module Toolkit.Data.DList.AtIndex

import Decidable.Equality

import Toolkit.Decidable.Informative

import Toolkit.Data.List.AtIndex
import Toolkit.Data.DList

%default total

||| Proof that some element satisfies the predicate
|||
||| @idx   The type of the element's index.
||| @type  The type of the list element.
||| @p     A predicate
||| @xs      The list itself.
public export
data HoldsAtIndex : (type   : Type)
                 -> (item   : type -> Type)
                 -> (p      : {i : type} -> (x : item i) -> Type)
                 -> {is     : List type}
                 -> (xs     : DList type item is)
                 -> (idx    : Nat)
                           -> Type
    where
      ||| Proof that the element is at the front of the list.
      Here : {p   : {i : type} -> (x : item i) -> Type}
          -> {i   : type}
          -> {x   : item i}
          -> (prf : p x)
                 -> HoldsAtIndex type item p (x::xs) Z

      ||| Proof that the element is found later in the list.
      There : {p      : {i : type} -> (x : item i) -> Type}
           -> (contra : p x' -> Void)
           -> (later  : HoldsAtIndex type item p      xs     loc)
                     -> HoldsAtIndex type item p (x'::xs) (S loc)

namespace Find
  namespace HoldsAtIndex
    public export
    data Error type = IsEmpty
                    | Later type (HoldsAtIndex.Error type)

  isEmpty : {p  : {i : type} -> (x : item i) -> Type}
         -> DPair Nat (HoldsAtIndex type item p [])
         -> Void
  isEmpty (MkDPair loc prf) with (prf)
    isEmpty (MkDPair loc prf) | (MkDPair _ (Here _)) impossible
    isEmpty (MkDPair loc prf) | (MkDPair _ (There _)) impossible

  notLater : {p : {i : type} -> (x : item i) -> Type}
          -> (DPair Nat (HoldsAtIndex type item p rest) -> Void)
          -> (p i -> Void)
          -> DPair Nat (HoldsAtIndex type item p (i :: rest))
          -> Void
  notLater f g (MkDPair _ (Here prf))
    = g prf
  notLater f g (MkDPair _ (There contra later))
    = f (MkDPair _ later)

  export
  holdsAtIndex : {p  : {i : type} -> (x : item i) -> Type}
              -> (f  : {i : type} -> (x : item i) -> DecInfo err (p x))
              -> (xs : DList type item is)
                    -> DecInfo (HoldsAtIndex.Error err)
                               (DPair Nat (HoldsAtIndex type item p xs))
  holdsAtIndex f Nil
    = No IsEmpty (isEmpty)

  holdsAtIndex f (x :: y) with (f x)
    holdsAtIndex f (x :: y) | (Yes prf)
      = Yes (MkDPair 0 (Here prf))

    holdsAtIndex f (x :: y) | (No msg contra) with (holdsAtIndex f y)
      holdsAtIndex f (x :: y) | (No msg contra) | (Yes (MkDPair loc prf))
        = Yes (MkDPair (S loc) (There contra prf))

      holdsAtIndex f (x :: y) | (No msg contra) | (No msgR contraR)
        = No (Later msg msgR)
             (notLater contraR contra)

-- [ EOF ]


module Data.List.Lazy

import Data.Fuel
import Data.Stream
import Data.Colist
import Data.Colist1

%default total

-- All functions here are public export
-- because their definitions are pretty much the specification.

public export
data LazyList : Type -> Type where
  Nil : LazyList a
  (::) : (x : a) -> (xs : Lazy (LazyList a)) -> LazyList a

--- Truly lazy functions ---

public export
foldrLazy : (func : elem -> Lazy acc -> acc) -> (init : Lazy acc) -> (input : LazyList elem) -> acc
foldrLazy _  init [] = init
foldrLazy op init (x::xs) = x `op` foldrLazy op init xs

public export
(++) : LazyList a -> Lazy (LazyList a) -> LazyList a
(++) = flip $ foldrLazy (::)

-- Specialized variant of `concatMap` with both `t` and `m` being `LazyList`.
public export
bindLazy : (a -> LazyList b) -> LazyList a -> LazyList b
bindLazy f = foldrLazy ((++) . f) []

public export
choice : Alternative f => LazyList (f a) -> f a
choice = foldrLazy (<|>) empty

public export
choiceMap : Alternative f => (a -> f b) -> LazyList a -> f b
choiceMap g = foldrLazy ((<|>) . g) empty

public export
any : (a -> Bool) -> LazyList a -> Bool
any p = foldrLazy ((||) . p) False

public export
all : (a -> Bool) -> LazyList a -> Bool
all p = foldrLazy ((&&) . p) True

--- Interface implementations ---

public export
Eq a => Eq (LazyList a) where
  [] == [] = True
  x :: xs == y :: ys = x == y && xs == ys
  _ == _ = False

public export
Ord a => Ord (LazyList a) where
  compare [] [] = EQ
  compare [] (x :: xs) = LT
  compare (x :: xs) [] = GT
  compare (x :: xs) (y :: ys)
     = case compare x y of
            EQ => compare xs ys
            c => c

export
Show a => Show (LazyList a) where
  show []       = "[]"
  show (h :: t) = "[" ++ show' "" h t ++ "]"
    where
      -- Idris didn't like the lazyness involved when using the
      -- same implementation as for `List`, therefore, this was
      -- adjusted to first force the head and tail of the list.
      show' : String -> a -> LazyList a -> String
      show' acc h Nil       = acc ++ show h
      show' acc h (x :: xs) = show' (acc ++ show h ++ ", ") x xs

public export
Semigroup (LazyList a) where
  xs <+> ys = xs ++ ys

public export
Monoid (LazyList a) where
  neutral = []

public export
Foldable LazyList where
  foldr op nil [] = nil
  foldr op nil (x :: xs) = x `op` foldr op nil xs

  foldl op acc [] = acc
  foldl op acc (x :: xs) = foldl op (acc `op` x) xs

  foldlM fm init xs = foldrLazy (\x, k, z => fm z x >>= k) pure xs init

  null []     = True
  null (_::_) = False

  foldMap f [] = neutral
  foldMap f (x :: xs) = f x <+> foldMap f xs

public export
Functor LazyList where
  map f [] = []
  map f (x :: xs) = f x :: map f xs

public export
Applicative LazyList where
  pure x = [x]
  fs <*> vs = bindLazy (\f => map f vs) fs

public export
Alternative LazyList where
  empty = []
  (<|>) = (++)

public export
Monad LazyList where
  m >>= f = bindLazy f m

-- There is no Traversable instance for lazy lists.
-- The result of a traversal will be a non-lazy list in general
-- (you can't delay the "effect" of an applicative functor).
public export
traverse : Applicative f => (a -> f b) -> LazyList a -> f (List b)
traverse g [] = pure []
traverse g (x :: xs) = [| g x :: traverse g xs |]

public export %inline
for : Applicative f => LazyList a -> (a -> f b) -> f (List b)
for = flip traverse

public export
sequence : Applicative f => LazyList (f a) -> f (List a)
sequence = traverse id

public export
Zippable LazyList where
  zipWith _ [] _ = []
  zipWith _ _ [] = []
  zipWith f (x::xs) (y::ys) = f x y :: zipWith f xs ys

  zipWith3 _ [] _ _ = []
  zipWith3 _ _ [] _ = []
  zipWith3 _ _ _ [] = []
  zipWith3 f (x::xs) (y::ys) (z::zs) = f x y z :: zipWith3 f xs ys zs

  unzip xs = (fst <$> xs, snd <$> xs)
  unzipWith = unzip .: map

  unzip3 xs = (fst <$> xs, fst . snd <$> xs, snd . snd <$> xs)
  unzipWith3 = unzip3 .: map

--- Lists creation ---

public export
fromList : List a -> LazyList a
fromList []      = []
fromList (x::xs) = x :: fromList xs

covering
public export
iterate : (f : a -> Maybe a) -> (x : a) -> LazyList a
iterate f x = x :: case f x of
  Nothing => []
  Just y  => iterate f y

covering
public export
unfoldr : (b -> Maybe (a, b)) -> b -> LazyList a
unfoldr f c = case f c of
  Nothing     => []
  Just (a, n) => a :: unfoldr f n

public export
iterateN : Nat -> (a -> a) -> a -> LazyList a
iterateN Z     _ _ = []
iterateN (S n) f x = x :: iterateN n f (f x)

public export
replicate : (n : Nat) -> (x : a) -> LazyList a
replicate Z     _ = []
replicate (S n) x = x :: replicate n x

--- Functions acquiring parts of list ---

public export
head' : LazyList a -> Maybe a
head' []     = Nothing
head' (x::_) = Just x

public export
tail' : LazyList a -> Maybe (LazyList a)
tail' []      = Nothing
tail' (_::xs) = Just xs

--- Functions for acquiring different types of sublists ---

public export
take : Nat -> LazyList a -> LazyList a
take (S k) (x::xs) = x :: take k xs
take _ _ = []

public export
drop : Nat -> LazyList a -> LazyList a
drop Z     xs      = xs
drop (S _) []      = []
drop (S n) (_::xs) = drop n xs

public export
takeWhile : (a -> Bool) -> LazyList a -> LazyList a
takeWhile p []      = []
takeWhile p (x::xs) = if p x then x :: takeWhile p xs else []

public export
dropWhile : (a -> Bool) -> LazyList a -> LazyList a
dropWhile p []      = []
dropWhile p (x::xs) = if p x then dropWhile p xs else x::xs

public export
filter : (a -> Bool) -> LazyList a -> LazyList a
filter p []      = []
filter p (x::xs) = if p x then x :: filter p xs else filter p xs

public export
mapMaybe : (a -> Maybe b) -> LazyList a -> LazyList b
mapMaybe f []      = []
mapMaybe f (x::xs) = case f x of
  Nothing => mapMaybe f xs
  Just y  => y :: mapMaybe f xs

namespace Stream

  public export
  take : Fuel -> Stream a -> LazyList a
  take Dry _ = []
  take (More f) (x :: xs) = x :: take f xs

namespace Colist

  public export
  take : Fuel -> Colist a -> LazyList a
  take Dry _ = []
  take _ [] = []
  take (More f) (x :: xs) = x :: take f xs

namespace Colist1

  public export
  take : Fuel -> Colist1 a -> LazyList a
  take fuel as = take fuel (forget as)

--- Functions for extending lists ---

public export
mergeReplicate : a -> LazyList a -> LazyList a
mergeReplicate sep []      = []
mergeReplicate sep (y::ys) = sep :: y :: mergeReplicate sep ys

public export
intersperse : a -> LazyList a -> LazyList a
intersperse sep []      = []
intersperse sep (x::xs) = x :: mergeReplicate sep xs

public export
intercalate : (sep : LazyList a) -> (xss : LazyList (LazyList a)) -> LazyList a
intercalate sep xss = choice $ intersperse sep xss

--- Functions converting lazy lists to something ---

public export
toColist : LazyList a -> Colist a
toColist [] = []
toColist (x::xs) = x :: toColist xs


= Maps: Total and Partial Maps

> module Maps
>
> import Logic
> import IndProp
>
> %access public export
>

Maps (or dictionaries) are ubiquitous data structures both generally and in the
theory of programming languages in particular; we're going to need them in many
places in the coming chapters. They also make a nice case study using ideas
we've seen in previous chapters, including building data structures out of
higher-order functions (from `Basics` and `Poly`) and the use of reflection to
streamline proofs (from `IndProp`).

We'll define two flavors of maps: _total_ maps, which include a "default"
element to be returned when a key being looked up doesn't exist, and _partial_
maps, which return a \idr{Maybe} to indicate success or failure. The latter is
defined in terms of the former, using \idr{Nothing} as the default element.

== The Idris Standard Library

\todo[inline]{Edit}

One small digression before we get to maps.

Unlike the chapters we have seen so far, this one does not `Require Import` the
chapter before it (and, transitively, all the earlier chapters). Instead, in
this chapter and from now, on we're going to import the definitions and theorems
we need directly from Idris's standard library stuff. You should not notice much
difference, though, because we've been careful to name our own definitions and
theorems the same as their counterparts in the standard library, wherever they
overlap.

```coq
Require Import Idris.Arith.Arith.
Require Import Idris.Bool.Bool.
Require Import Idris.Strings.String.
Require Import Idris.Logic.FunctionalExtensionality.
```

Documentation for the standard library can be found at
\url{https://www.idris-lang.org/docs/current/}.

The \idr{:search} command is a good way to look for theorems involving objects
of specific types. Take a minute now to experiment with it.

== Identifiers

First, we need a type for the keys that we use to index into our maps. For this
purpose, we again use the type \idr{Id} from the `Lists` chapter. To make this
chapter self contained, we repeat its definition here, together with the
equality comparison function for \idr{Id} and its fundamental property.

> data Id : Type where
>   MkId : String -> Id
>
> beq_id : (x1, x2 : Id) -> Bool
> beq_id (MkId n1) (MkId n2) = decAsBool $ decEq n1 n2
>

\todo[inline]{Edit}

(The function \idr{decEq} comes from Idris's string library. If you check its
result type, you'll see that it does not actually return a \idr{Bool}, but
rather a type that looks like \idr{Either (x = y) (Not (x = y))}, called a
{Dec}, which can be thought of as an "evidence-carrying boolean." Formally, an
element of \idr{Dec (x=y)} is either a proof that two things are equal or a
proof that they are unequal, together with a tag indicating which. But for
present purposes you can think of it as just a fancy \idr{Bool}.)

> beq_id_refl : (x : Id) -> True = beq_id x x
> beq_id_refl (MkId n) with (decEq n n)
>   beq_id_refl _ | Yes _     = Refl
>   beq_id_refl _ | No contra = absurd $ contra Refl
>

The following useful property of  \idr{beq_id} follows from an analogous lemma
about strings:

> beq_id_true_iff : (beq_id x y = True) <-> x = y
> beq_id_true_iff = (bto, bfro)
>   where
>     bto : (beq_id x y = True) -> x = y
>     bto {x=MkId n1} {y=MkId n2} prf with (decEq n1 n2)
>       bto Refl | Yes eq = cong {f=MkId} eq
>       bto Refl | No _ impossible
>
>     idInj : MkId x = MkId y -> x = y
>     idInj Refl = Refl
>
>     bfro : (x = y) -> beq_id x y = True
>     bfro {x=MkId n1} {y=MkId n2} prf with (decEq n1 n2)
>       bfro _   | Yes _     = Refl
>       bfro prf | No contra = absurd $ contra $ idInj prf
>

Similarly:

> beq_id_false_iff : (beq_id x y = False) <-> Not (x = y)
> beq_id_false_iff = (to, fro)
>   where
>     to : (beq_id x y = False) -> Not (x = y)
>     to beqf = (snd not_true_iff_false) beqf . (snd beq_id_true_iff)
>
>     fro : (Not (x = y)) -> beq_id x y = False
>     fro noteq = (fst not_true_iff_false) $ noteq . (fst beq_id_true_iff)
>

== Total Maps

Our main job in this chapter will be to build a definition of partial maps that
is similar in behavior to the one we saw in the `Lists` chapter, plus
accompanying lemmas about its behavior.

This time around, though, we're going to use _functions_, rather than lists of
key-value pairs, to build maps. The advantage of this representation is that it
offers a more _extensional_ view of maps, where two maps that respond to queries
in the same way will be represented as literally the same thing (the very same
function), rather than just "equivalent" data structures. This, in turn,
simplifies proofs that use maps.

We build partial maps in two steps. First, we define a type of _total maps_ that
return a default value when we look up a key that is not present in the map.

> TotalMap : Type -> Type
> TotalMap a = Id -> a
>

Intuitively, a total map over an element type \idr{a} is just a function that
can be used to look up \idr{Id}s, yielding \idr{a}s.

The function \idr{t_empty} yields an empty total map, given a default element;
this map always returns the default element when applied to any id.

> t_empty : (v : a) -> TotalMap a
> t_empty v = \_ => v
>

We can also write this as:

```idris
t_empty = const
```

More interesting is the \idr{update} function, which (as before) takes a map
\idr{m}, a key \idr{x}, and a value \idr{v} and returns a new map that takes
\idr{x} to \idr{v} and takes every other key to whatever \idr{m} does.

> t_update : (x : Id) -> (v : a) -> (m : TotalMap a) -> TotalMap a
> t_update x v m = \x' => if beq_id x x' then v else m x'
>

This definition is a nice example of higher-order programming: \idr{t_update}
takes a _function_ \idr{m} and yields a new function \idr{\x' => ...} that
behaves like the desired map.

For example, we can build a map taking \idr{Id}s to \idr{Bool}s, where \idr{Id
3} is mapped to \idr{True} and every other key is mapped to \idr{False}, like
this:

\todo[inline]{Seems like a wrong description in the book here}

> examplemap : TotalMap Bool
> examplemap = t_update (MkId "foo") False $
>              t_update (MkId "bar") True $
>              t_empty False
>

This completes the definition of total maps. Note that we don't need to define a
\idr{find} operation because it is just function application!

> update_example1 : examplemap (MkId "baz") = False
> update_example1 = Refl
>
> update_example2 : examplemap (MkId "foo") = False
> update_example2 = Refl
>
> update_example3 : examplemap (MkId "quux") = False
> update_example3 = Refl
>
> update_example4 : examplemap (MkId "bar") = True
> update_example4 = Refl
>

To use maps in later chapters, we'll need several fundamental facts about how
they behave. Even if you don't work the following exercises, make sure you
thoroughly understand the statements of the lemmas! (Some of the proofs require
the functional extensionality axiom, which is discussed in the `Logic` chapter.)

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

First, the empty map returns its default element for all keys:

> t_apply_empty : t_empty v x = v
> t_apply_empty = ?t_apply_empty_rhs
>

$\square$

==== Exercise: 2 stars, optional (t_update_eq)

Next, if we update a map \idr{m} at a key \idr{x} with a new value \idr{v} and
then look up \idr{x} in the map resulting from the \idr{update}, we get back
\idr{v}:

> t_update_eq : (t_update x v m) x = v
> t_update_eq = ?t_update_eq_rhs
>

$\square$

==== Exercise: 2 stars, optional (t_update_neq)

On the other hand, if we update a map \idr{m} at a key \idr{x1} and then look up
a _different_ key \idr{x2} in the resulting map, we get the same result that
\idr{m} would have given:

> t_update_neq : Not (x1 = x2) -> (t_update x1 v m) x2 = m x2
> t_update_neq neq = ?t_update_neq_rhs
>

$\square$

==== Exercise: 2 stars, optional (t_update_shadow)

If we update a map \idr{m} at a key \idr{x} with a value \idr{v1} and then
update again with the same key \idr{x} and another value \idr{v2}, the resulting
map behaves the same (gives the same result when applied to any key) as the
simpler map obtained by performing just the second \idr{update} on \idr{m}:

> t_update_shadow : t_update x v2 $ t_update x v1 m = t_update x v2 m
> t_update_shadow = ?t_update_shadow_rhs
>

$\square$

For the final two lemmas about total maps, it's convenient to use the reflection
idioms introduced in chapter `IndProp`. We begin by proving a fundamental
_reflection lemma_ relating the equality proposition on \idr{Id}s with the
boolean function \idr{beq_id}.

==== Exercise: 2 stars, optional (beq_idP)

Use the proof of \idr{beq_natP} in chapter `IndProp` as a template to prove the
following:

> beq_idP : {x, y : Id} -> Reflect (x = y) (beq_id x y)
> beq_idP = ?beq_idP_rhs
>

$\square$

Now, given \idr{Id}s \idr{x1} and \idr{x2}, we can use \idr{with (beq_idP x1
x2)} to simultaneously perform case analysis on the result of \idr{beq_id x1 x2}
and generate hypotheses about the equality (in the sense of \idr{=}) of \idr{x1}
and \idr{x2}.

==== Exercise: 2 stars (t_update_same)

With the example in chapter `IndProp` as a template, use \idr{beq_idP} to prove
the following theorem, which states that if we update a map to assign key
\idr{x} the same value as it already has in \idr{m}, then the result is equal to
\idr{m}:

> t_update_same : t_update x (m x) m = m
> t_update_same = ?t_update_same_rhs
>

$\square$

==== Exercise: 3 stars, recommended (t_update_permute)

Use \idr{beq_idP} to prove one final property of the \idr{update} function: If
we update a map \idr{m} at two distinct keys, it doesn't matter in which order
we do the updates.

> t_update_permute : Not (x2 = x1) -> t_update x1 v1 $ t_update x2 v2 m
>                                   = t_update x2 v2 $ t_update x1 v1 m
> t_update_permute neq = ?t_update_permute_rhs
>

$\square$

== Partial maps

Finally, we define _partial maps_ on top of total maps. A partial map with
elements of type \idr{a} is simply a total map with elements of type \idr{Maybe
a} and default element \idr{Nothing}.

> PartialMap : Type -> Type
> PartialMap a = TotalMap (Maybe a)
>
> empty : PartialMap a
> empty = t_empty Nothing
>
> update : (x : Id) -> (v : a) -> (m : PartialMap a) -> PartialMap a
> update x v m = t_update x (Just v) m
>

We now straightforwardly lift all of the basic lemmas about total maps to
partial maps.

> apply_empty : empty {a} x = Nothing {a}
> apply_empty = Refl
>
> update_eq : (update x v m) x = Just v
> update_eq {v} = t_update_eq {v=Just v}
>
> update_neq : Not (x2 = x1) -> (update x2 v m) x1 = m x1
> update_neq {x1} {x2} {v} = t_update_neq {x1=x2} {x2=x1} {v=Just v}
>
> update_shadow : update x v2 $ update x v1 m = update x v2 m
> update_shadow {v1} {v2} = t_update_shadow {v1=Just v1} {v2=Just v2}
>
> update_same : m x = Just v -> update x v m = m
> update_same prf = rewrite sym prf in t_update_same
>
> update_permute : Not (x2 = x1) -> update x1 v1 $ update x2 v2 m
>                                 = update x2 v2 $ update x1 v1 m
> update_permute {v1} {v2} = t_update_permute {v1=Just v1} {v2=Just v2}