module Qsort where
open import Sec4                -- The module with definition of propositions
import Sec2

-- postulate needed for ≤ on A
postulate leq : {A : Set} → A → A → Prop
postulate geq : {A : Set} → A → A → Prop
postulate tot-list : {A : Set} → (a b : A) → (leq a  b) ∨ (leq b a)
postulate trans-list :  {A : Set} → (a b c : A) → (leq a b) → (leq b c) → (leq a c)

{-
  Definition of a list
-}
data List (A : Set) : Set where
  []  : List A                  -- Empty list
  _∷_ : A → List A → List A     -- Cons

-- Proposition stating what is a non empty list
nelist : {A : Set} → (List A) → Prop
nelist [] = ⊥
nelist (x ∷ x₁) = ⊤

-- Head function that only works on non empty list
head : {A : Set} → (l : List A) → (p : nelist l) → A
head [] ()                      -- This can never happen
head (x ∷ _) ⋆ = x              -- This is the head of the list

-- The tail of the list only works on non empty list
tail : {A : Set} → (l : List A) → (p : nelist l) → (List A)
tail [] ()
tail (_ ∷ l) ⋆ = l

data Bool : Set where
  True  : Bool
  False : Bool

data ℕ : Set where
  Z : ℕ
  S : ℕ → ℕ

-- Addition of natural numbers
_+_ : ℕ → ℕ → ℕ
Z + y = y
S x + y = S (x + y)

-- Relation on natural numbers
_≤_ : ℕ → ℕ → Prop
Z ≤ Z = ⊤
Z ≤ (S y) = ⊤
S x ≤ Z = ⊥
S x ≤ S y = x ≤ y

-- {-# BUILTIN NATURAL ℕ #-}
-- {-# BUILTIN BOOL Bool #-}

-- ≤ is reflexive
≤-ref : ∀ (x : ℕ) → (x ≤ x) → (x ≤ x)
≤-ref _ y = y

-- ≤ is not symmetric
-- ≤-sym : ∀ (x y : ℕ) → (x ≤ y) → (y ≤ x)
-- ≤-sym Z Z p = ⋆
-- ≤-sym Z (S y) ⋆ = {!!}
-- ≤-sym (S x) Z ()
-- ≤-sym (S x) (S y) p = ≤-sym x y p

-- ≤ is transitive
≤-trans : ∀ (x y z : ℕ) → (x ≤ y) → (y ≤ z) → (x ≤ z)
≤-trans Z Z Z p1 p2 = ⋆
≤-trans Z Z (S z) p1 p2 = ⋆
≤-trans Z (S y) Z p1 p2 = ⋆
≤-trans Z (S y) (S z) p1 p2 = ⋆
≤-trans (S x) Z z () p2
≤-trans (S x) (S y) Z p1 ()
≤-trans (S x) (S y) (S z) p1 p2 = ≤-trans x y z p1 p2

-- length of a list
length : {A : Set} → (List A) → ℕ
length [] = Z
length (x ∷ l) = (S Z) + (length l)

-- filter' on a list
filter' : {A : Set} → (A → Bool) → (l : List A)
         → List A
filter' f [] = []
filter' f (x ∷ l) with (f x)
filter' f (x ∷ l) | True = (x ∷ (filter' f l))
filter' f (x ∷ l) | False = filter' f l

≤-cong : ∀ (x y : ℕ) → (x ≤ y) → (x ≤ (S y))
≤-cong Z y p = ⋆
≤-cong (S x) Z ()
≤-cong (S x) (S y) p = ≤-cong x y p

thm-filter' : {A : Set} → (l : List A) → (f : A → Bool) → length (filter' f l) ≤ S (length l)
thm-filter' [] f = ⋆
thm-filter' (x ∷ l) f with (f x)
thm-filter' (x ∷ l) f | True = thm-filter' l f
thm-filter' (x ∷ l) f | False = ≤-cong (length (filter' f l)) (S (length l)) (thm-filter' l  f)

filter : {A : Set} → (A → Bool) → (l : List A)
         → Exists (List A) (λ l' → (length l') ≤ (length l))
filter f [] = [ [] , ⋆ ]
filter f (x ∷ l) = [ filter' f l , thm-filter' l f ]

-- append two lists
_++_ : {A : Set} → (l : List A) → (l' : List A) → (List A)
[] ++ l' = l'
(x ∷ l) ++ l' = (x ∷ (l ++ l'))

leq-nat : ℕ → ℕ → Bool
leq-nat Z  _ = True
leq-nat (S n) Z = False
leq-nat (S n) (S m) = leq-nat n m

_<_ : ℕ → ℕ → Bool
Z < (S _) = True
Z < Z = False
(S n) < (S m) = n < m
(S n) < Z = False

_>_ : ℕ → ℕ → Bool
m > n = n < m

-- The sorting algorithm
-- The trick here is that we are reducing qsort on "n"
qsort' : ∀ (n : ℕ) → ∀ (l : List ℕ) → (p : (length l) ≤ n) → (List ℕ)
qsort' Z [] p = []
qsort' Z (x ∷ l) ()
qsort' (S n) [] p = []
qsort' (S n) (x ∷ l) p =
                       let
                         ll = (filter (leq-nat x) l)
                         rr = (filter ((_>_) x) l)
                         pl = elim2-exists ll
                         pr = elim2-exists rr
                         left = qsort' n (elim1-exists ll) (≤-trans (length (elim1-exists ll)) (length l) n pl p)
                         right = qsort' n (elim1-exists rr) (≤-trans (length (elim1-exists rr)) (length l) n pr p)
                       in
                       (left ++ (x ∷ [])) ++ right

l' : {A : Set} → (l : List A) → (length l ≤ length l)
l' [] = ⋆
l' (x ∷ l) = l' l

qsort : ∀ (l : List ℕ) → List ℕ
qsort l = qsort' (length l) l (l' l)

-- XXX: definition of an sorted list
all-sorted-list : {A : Set} → (a : A)
                   → (l : List A)
                   → Prop
all-sorted-list a [] = ⊤
all-sorted-list a (x ∷ l) = leq a x ∧ (all-sorted-list a l)

sorted-list : {A : Set} → List A → Prop
sorted-list [] = ⊤
sorted-list (x ∷ l) = (all-sorted-list x l) ∧ (sorted-list l)

lem-qsort' : (x₁ : ℕ) → (l : List ℕ) → sorted-list
      ((qsort' (S (length l)) (filter' (leq-nat x₁) l)
        (≤-trans (length (filter' (leq-nat x₁) l)) (S (length l))
         (S (length l)) (thm-filter' l (leq-nat x₁)) (l' l))
        ++ (x₁ ∷ []))
       ++
       qsort' (S (length l)) (filter' (_>_ x₁) l)
       (≤-trans (length (filter' (_>_ x₁) l)) (S (length l))
        (S (length l)) (thm-filter' l (_>_ x₁)) (l' l)))
lem-qsort' x [] = and ⋆ ⋆
lem-qsort' x (x₁ ∷ l) = {!!}

lem-qsort : (l : List ℕ) → (x : ℕ) →
  sorted-list
      ((qsort' (length l) (elim1-exists (filter (leq-nat x) l))
        (≤-trans (length (elim1-exists (filter (leq-nat x) l))) (length l)
         (length l) (elim2-exists (filter (leq-nat x) l)) (l' l))
        ++ (x ∷ []))
       ++
       qsort' (length l) (elim1-exists (filter (_>_ x) l))
       (≤-trans (length (elim1-exists (filter (_>_ x) l))) (length l)
        (length l) (elim2-exists (filter (_>_ x) l)) (l' l)))
lem-qsort [] x = and ⋆ ⋆
lem-qsort (x ∷ l) x₁ = lem-qsort' x₁ l

-- Theorem that given a list, qsort will actually sort the list
thm-qsort : ∀ (l : List ℕ) → sorted-list (qsort l)
thm-qsort [] = ⋆
thm-qsort (x ∷ l) = lem-qsort l x


------------------------------------------------------------------------------
-- Note on the equality type class using Kettelhoit's approach
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

-- Adapted from Kettelhoit's thesis.

module FOT.FOTC.TypeClasses.EqualityKettelhoit where

open import FOTC.Base
open import FOTC.Data.Bool
open import FOTC.Data.Bool.Type
open import FOTC.Data.Nat.Type

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

record Eq (P : D → Set) : Set₁ where
  field equal : ∀ {t₁ t₂} → P t₁ → P t₂ → Set

open Eq {{...}} public

boolEq : ∀ {b₁ b₂} → Bool b₁ → Bool b₂ → Set
boolEq btrue  btrue  = Bool true
boolEq bfalse bfalse = Bool true
{-# CATCHALL #-}
boolEq _      _      = Bool false

nEq : ∀ {m n} → N m → N n → Set
nEq nzero      nzero      = Bool true
nEq (nsucc Nm) (nsucc Nn) = Bool true
{-# CATCHALL #-}
nEq _          _          = Bool false

instance
  eqInstanceBool : Eq Bool
  eqInstanceBool = record { equal = boolEq }

  eqInstanceN : Eq N
  eqInstanceN = record { equal = nEq }

test₁ : Set
test₁ = equal nzero (nsucc nzero)

test₂ : Set
test₂ = equal bfalse bfalse

eqN-sym : ∀ {m n} → (Nm : N m) → (Nn : N n) → equal Nm Nn → equal Nn Nm
eqN-sym nzero      nzero      h = h
eqN-sym nzero      (nsucc Nn) h = h
eqN-sym (nsucc Nm) nzero      h = h
eqN-sym (nsucc Nm) (nsucc Nn) h = h

postulate eqN-sym' : ∀ {m n} → (Nm : N m) → (Nn : N n) → equal Nm Nn → equal Nn Nm
-- {-# ATP prove eqN-sym' #-}


module Progress where

open import Prelude
open import T

---- progress

module Progress where
  -- Define a datatype representing that a term satisfies progress
  data TProgress : ∀{A} → TCExp A → Set where
    prog-val : ∀{A} {e : TCExp A} → (D : TVal e) → TProgress e
    prog-step : ∀{A} {e e' : TCExp A} → (D : e ~> e') → TProgress e

  -- prove that all terms satisfy progress
  progress : ∀{A} (e : TCExp A) → TProgress e
  progress (var ())
  progress (Λ e) = prog-val val-lam
  progress (e₁ $ e₂) with progress e₁
  progress (e₁ $ e₂) | prog-step D = prog-step (step-app-l D)
  progress (.(Λ e) $ e₂) | prog-val (val-lam {_} {_} {e}) = prog-step step-beta
  progress zero = prog-val val-zero
  progress (suc e) with progress e
  ... | prog-val D = prog-val (val-suc D)
  ... | prog-step D' = prog-step (step-suc D')
  progress (rec e e₀ es) with progress e
  progress (rec .zero e₀ es) | prog-val val-zero = prog-step step-rec-z
  progress (rec .(suc _) e₀ es) | prog-val (val-suc  y) = prog-step (step-rec-s y)
  ... | prog-step D = prog-step (step-rec D)

open Progress public


module Oscar.Property.IsReflexive where

open import Oscar.Level

record IsReflexive {𝔬} (⋆ : Set 𝔬) {ℓ} (_≣_ : ⋆ → ⋆ → Set ℓ) : Set (𝔬 ⊔ ℓ) where
  field
    reflexivity : ∀ x → x ≣ x


module List.Permutation.Alternative.Correctness (A : Set) where

open import Data.List
open import List.Permutation.Alternative A renaming (_∼_ to _∼′_)
open import List.Permutation.Base A
open import List.Permutation.Base.Equivalence A

lemma-∼′-∼ : {xs ys : List A} → xs ∼′ ys → xs ∼ ys
lemma-∼′-∼ ∼refl = refl∼
lemma-∼′-∼ (∼trans xs∼′ys ys∼′zs) = trans∼ (lemma-∼′-∼ xs∼′ys) (lemma-∼′-∼ ys∼′zs)
lemma-∼′-∼ (∼head x xs∼′ys) = ∼x /head /head (lemma-∼′-∼ xs∼′ys)
lemma-∼′-∼ (∼swap xyxs∼′ys) = trans∼ (∼x /head (/tail /head) refl∼) (lemma-∼′-∼ xyxs∼′ys)


module Generic.Lib.Equality.Heteroindexed where

open import Data.Product

open import Generic.Lib.Equality.Propositional

data [_]_≅_ {ι α} {I : Set ι} {i} (A : I -> Set α) (x : A i) : ∀ {j} -> A j -> Set where
  irefl : [ A ] x ≅ x

inds : ∀ {ι α} {I : Set ι} {A : I -> Set α} {i j} {x : A i} {y : A j}
     -> [ A ] x ≅ y -> i ≡ j
inds irefl = refl

homo : ∀ {ι α} {I : Set ι} {A : I -> Set α} {i} {x y : A i}
     -> [ A ] x ≅ y -> x ≡ y
homo irefl = refl

inds-homo : ∀ {ι α} {I : Set ι} {A : Set α} {i j : I} {x y : A}
          -> [_]_≅_ {i = i} (λ _ -> A) x {j} y -> i ≡ j × x ≡ y
inds-homo irefl = refl , refl


{-# OPTIONS --no-coverage-check #-}
module FlexibleFunArity where

data Bool : Set where true false : Bool

g : Bool -> Bool -> Bool
g true = \ x -> true
g false true  = false
g false false = true

T : Bool -> Set
T true  = Bool
T false = Bool -> Bool

f : (b : Bool) -> T b
f true = true
f false true  = false
f false false = true

{- checking clause

  f false true

starts with

  f (b : Bool) : T b

splits on b

  f true   -- no match, discard
  f false  -- matches

instantiate type

  f false : T false = Bool -> Bool

extend clause

  f false (y : Bool) : Bool

split on y

  f false true  -- matches
  f false false -- no match, discard

done
-}

{- coverage check starts with

  f (x : Bool)

splits on x

  f true   -- finds clause 1
  f false

NEW: remaing clauses have bigger arity, so expands to

  f false (y : Bool)

splits on y

  f false true  -- finds clause 2
  f false false -- finds clause 3

done
-}


{-# OPTIONS --without-K --exact-split #-}

import 05-identity-types
open 05-identity-types public

-- Inversion is uninteresting and comes from:
inversion-proof :
  {i : Level} {A : UU i} (x : A) → (y : A) → Id x y → Id y x
inversion-proof x y = ind-Id x (λ y' p' → Id y' x) refl y

lemma-2-2-2-i :
  {i j : Level} {A : UU i} {B : UU j} (x y z : A) (f : A → B) → (p : Id x y) → (q : Id y z)
  → Id (ap f (p ∙ q)) ((ap f p) ∙ (ap f q))
lemma-2-2-2-i x y z f refl q = refl
-- We apparently don't need a second induction on q..

lemma-2-2-2-ii :
  {i j : Level} {A : UU i} {B : UU j} (x y : A) (f : A → B) → (p : Id x y)
  → Id (ap f (inv p)) (inv (ap f p))
lemma-2-2-2-ii x y f refl = refl

lemma-2-2-2-iii :
  {i j k : Level} {A : UU i} {B : UU j} {C : UU k} (x y : A) (f : A → B) → (g : B → C) → (p : Id x y)
  → Id (ap g (ap f p)) (ap (g ∘ f) p)
lemma-2-2-2-iii x y f g refl = refl

lemma-2-2-2-iv :
  {i j k : Level} {A : UU i} {B : UU j} {C : UU k} (x y : A) → (p : Id x y)
  → Id (ap (λ x → x) p) p
lemma-2-2-2-iv x y refl = refl

-- Path lifting property
lemma-2-3-2 :
  {i j : Level} {A : UU i} {P : A → UU j} (x y : A) (u : P x) → (p : Id x y)
  → Id (pair x u) (pair y (tr P p u))
lemma-2-3-2 x y u refl = refl

lemma-2-3-9 :
  {i j : Level} {A : UU i} {P : A → UU j} (x y z : A) (u : P x) → (p : Id x y)
  → (q : Id y z) → Id (tr P q (tr P p u)) (tr P (p ∙ q) u)
lemma-2-3-9 x y z u refl q = refl

lemma-2-3-10 :
  {i j k : Level} {A : UU i} {B : UU j} {P : B → UU k} (x y : A) (f : A → B) (u : P (f x)) → (p : Id x y)
  → Id (tr (P ∘ f) p u) (tr P (ap f p) u)
lemma-2-3-10 x y f u refl = refl

lemma-2-3-11 :
  {i j k : Level} {A : UU i} {P : A → UU j} {Q : A → UU k}
  (x y : A) (f : (z : A) → P z → Q z) (u : P x) → (p : Id x y)
  → Id (tr Q p (f x u)) (f y (tr P p u))
lemma-2-3-11 x y f u refl = refl

-- Eckert-Hamilton

-- First, the loop
loop-type : {i : Level} {A : UU i} (x : A) → UU i
loop-type x = Id x x

order-2-star :
  {i : Level} {A : UU i} (a b c : A) → (p q : Id a b) → (r s : Id b c)
   → (alpha : Id p q) → (beta : Id r s) → Id (concat p c r) (concat q c s)
order-2-star a b c refl q refl s refl refl = refl

left-whisker :
  {i : Level} {A : UU i} (a b c : A) → (p q : Id a b) → (r : Id b c)
   → (alpha : Id p q) → Id (concat p c r) (concat q c r)
left-whisker a b c refl refl r refl = refl

right-whisker :
  {i : Level} {A : UU i} (a b c : A) → (p : Id a b) → (r s : Id b c)
   → (beta : Id r s) → Id (concat p c r) (concat p c s)
right-whisker a b c p refl refl refl = refl

-- TODO: Eckmann Hilton

-- 2.4: the equivalence of functions, quasi-inverses

-- `Sim-By` might be poorly set up since it basically takes the witness of what it
-- actually should just be. (../07- has the correct stuff.)
data Homotopic-Fn {i j : Level} (A : UU i) (B : UU j) (f g : A → B) : UU (i ⊔ j) where
  Sim-By : ((x : A) → (Id (f x) (g x))) → Homotopic-Fn A B f g

data Is-Equiv {i j : Level} {A : UU i} {B : UU j} (f : A → B) : UU (i ⊔ j) where
  With-Qinv : (g : B → A) → (Homotopic-Fn A A (g ∘ f) (λ x → x))
                          → (Homotopic-Fn B B (f ∘ g) (λ x → x))
                          → Is-Equiv f

-- Relying on the book's lemma 2.4.12, we're just using the above for type equivalence
data Equiv-Types {i j : Level} (A : UU i) (B : UU j) : UU (i ⊔ j) where
  Eq-By : (Σ (A → B) Is-Equiv)  → Equiv-Types A B

-- 2.6.1
product-functoriality :
  {i j : Level} {A : UU i} {B : UU j} (x y : prod A B) → (Id x y)
  → (prod (Id (pr1 x) (pr1 y)) (Id (pr2 x) (pr2 y)))
product-functoriality x y refl = pair refl refl

-- 2.6.2
pair= :
  {i j : Level} {A : UU i} {B : UU j} (a₁ a₂ : A) → (b₁ b₂ : B)
  → (prod (Id a₁ a₂) (Id b₁ b₂)) → (Id (pair a₁ b₁) (pair a₂ b₂))
pair= a₁ a₂ b₁ b₂ (pair refl refl) = refl

-- This is a bit of laziness to exhibit both bits of the proof with the same a₁ etc.
-- The point is to show that product-functoriality and pair= are quasi-inverses.
functoriality-is-equiv-to-2-6-2 :
  {i j : Level} {A : UU i} {B : UU j} {a₁ a₂ : A} {b₁ b₂ : B} →
  -- First two bindings give us shorthands for the proper invocations of the above
  -- functions (currying away aₙ and bₙ).
  let functorial = (product-functoriality (pair a₁ b₁) (pair a₂ b₂))
      2-6-2 = (pair= a₁ a₂ b₁ b₂)
      thm-after-funct = (Homotopic-Fn (prod (Id a₁ a₂) (Id b₁ b₂))
                                  (prod (Id a₁ a₂) (Id b₁ b₂))
                                  (λ x → x)
                                  (functorial ∘ 2-6-2))
      funct-after-thm = (Homotopic-Fn (Id (pair a₁ b₁) (pair a₂ b₂))
                                  (Id (pair a₁ b₁) (pair a₂ b₂))
                                  (2-6-2 ∘ functorial)
                                  (λ x → x))
  in (prod thm-after-funct funct-after-thm)
functoriality-is-equiv-to-2-6-2 = pair (Sim-By (λ {(pair refl refl) → refl})) (Sim-By (λ {refl → refl}))

-- 2.6.4
pair-transport :
  {i j k : Level} {Z : UU k} {A : Z → UU i} {B : Z → UU j} (z w : Z) → (p : Id z w) → (x : prod (A z) (B z))
  → (Id (tr (λ z → (prod (A z) (B z))) p x) (pair (tr A p (pr1 x)) (tr B p (pr2 x))))
pair-transport z w refl (pair az bz) = refl

-- 2.6.5
ap-functorial :
  {i j k l : Level} {A : UU i} {A' : UU j} {B : UU k} {B' : UU l} (g : A → A') → (h : B → B')
  → (x y : prod A B) → (p : Id (pr1 x) (pr1 y)) → (q : Id (pr2 x) (pr2 y)) →
  let pf= = (pair= (pr1 x) (pr1 y) (pr2 x) (pr2 y))
      pgh = (pair= (g (pr1 x)) (g (pr1 y)) (h (pr2 x)) (h (pr2 y)))
      f = (λ x → pair (g (pr1 x)) (h (pr2 x)))
  in Id (ap f (pf= (pair p q))) (pgh (pair (ap g p) (ap h q)))
ap-functorial g h (pair aₓ bₓ) (pair ay by) refl refl = refl

-- 2.9: Extensionality
happly : {i j : Level} {A : UU i} {B : A → UU j} (f g : (x : A) → B(x)) →
          (Id f g) → ((x : A) → (Id (f x) (g x)))
happly f g refl = λ x → refl

postulate fn-ext : Is-Equiv happly

dependent-fn-transport : {i j : Level} {X : UU i} (A : X → UU j) → (B : (x : X) → A x → UU (i ⊔ j))
                         (x₁ x₂ : X) → (Id x₁ x₂) →
                         (f : (a : A x₁) → (B x₁ a)) → (A x₂) → ((a : A x₂) → (B x₂ a))
dependent-fn-transport A B x₁ x₂ refl f a = tr (B-hat B) (pair= x₁ x₂ (tr A refl a) a
                                                              (pair refl refl))
                                                      (f (tr A refl a))
 where
   B-hat :  {i j : Level} {X : UU i} {A : X → UU j} (B : (x : X) → A x → UU (i ⊔ j)) → (Σ X (λ x → A x)) → UU (i ⊔ j)
   B-hat B w = B (pr1 w) (pr2 w)


open import Relation.Binary hiding (_⇒_)
open import Level

module Relation.Unary.Monotone {c i}(pre : Preorder i c i) where

open Preorder pre renaming (Carrier to I; _∼_ to _≤_)
open import Relation.Unary
open import Data.Product hiding (map)
open import Data.List
open import Function

record Monotone {ℓ}(p : Pred I ℓ) : Set (i ⊔ ℓ) where
  field wk : ∀ {i j} → i ≤ j → p i → p j

open Monotone
open Monotone ⦃...⦄ public

∀≥[_] : ∀ {ℓ} → Pred I ℓ → Pred I (i ⊔ ℓ)
∀≥[ P ] i = ∀ {j} → i ≤ j → P j

∃≥[_] : ∀ {ℓ} → Pred I ℓ → Pred I (i ⊔ ℓ)
∃≥[ P ] i = ∃ λ j → i ≤ j × P j

infixr 4 _↗_
_↗_ : ∀ {ℓ} → Pred I ℓ → Pred I ℓ → Pred I (i ⊔ ℓ)
(P ↗ Q) = ∀≥[ P ⇒ Q ]

instance
  ∀≥-monotone : ∀ {ℓ}{P : Pred I ℓ} → Monotone ∀≥[ P ]
  wk ∀≥-monotone ext f ext' = f (trans ext ext')

  mono-∩ : ∀ {i j}{p : Pred I i}{q : Pred I j}
             ⦃ wp : Monotone p ⦄ ⦃ wq : Monotone q ⦄ → Monotone (p ∩ q)
  wk (mono-∩ ⦃ wp ⦄ ⦃ wq ⦄) ext (x , y) = (wk wp ext x , wk wq ext y)

  list-monotone : ∀ {B : Pred I i}⦃ wb : Monotone B ⦄ → Monotone (λ W → List (B W))
  wk (list-monotone ⦃ wₐ ⦄) ext v = map (wk wₐ ext) v

  open import Data.List.All as All
  all-monotone : ∀ {b i}{B : Set b}{xs : List B}{C : B → Pred I i}
                 ⦃ wₐ : ∀ x → Monotone (C x) ⦄ →
                 Monotone (λ ys → All (λ x → C x ys) xs)
  wk (all-monotone ⦃ wₐ ⦄) ext v = All.map (λ {a} y → wk (wₐ a) ext y) v

  open import Data.Vec using (Vec)
  open import Data.Vec.All as VAll
  vec-all-monotone : ∀ {b i n}{B : Set b}{xs : Vec B n}{C : B → Pred I i}
                 ⦃ wₐ : ∀ x → Monotone (C x) ⦄ →
                 Monotone (λ ys → VAll.All (λ x → C x ys) xs)
  wk (vec-all-monotone ⦃ wₐ ⦄) ext v = VAll.map (λ {a} y → wk (wₐ a) ext y) v

  ≤-mono : ∀ {i} → Monotone (_≤_ i)
  wk ≤-mono = flip trans

  open import Data.Maybe as Maybe
  maybe-monotone : ∀ {i}{P : Pred I i} ⦃ mono : Monotone P ⦄ → Monotone (λ W → Maybe (P W))
  wk (maybe-monotone ⦃ mono ⦄) ext mv = Maybe.map (wk mono ext) mv


module 842Isomorphism where

-- Library

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; cong; cong-app; sym) -- added last
open Eq.≡-Reasoning
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Nat.Properties using (+-comm; +-suc; +-identityʳ) -- added last

-- Function composition.

_∘_ : ∀ {A B C : Set} → (B → C) → (A → B) → (A → C)
(g ∘ f) x = g (f x)

_∘′_ : ∀ {A B C : Set} → (B → C) → (A → B) → (A → C)
g ∘′ f = λ x → g (f x)

postulate
  extensionality : ∀ {A B : Set} {f g : A → B}
    → (∀ (x : A) → f x ≡ g x)
      -----------------------
    → f ≡ g

-- Another definition of addition.

_+′_ : ℕ → ℕ → ℕ -- split on n instead, get different code
m +′ zero = m
m +′ suc n = suc (m +′ n)

same-app : ∀ (m n : ℕ) → m +′ n ≡ m + n
same-app m zero = sym (+-identityʳ m)
same-app m (suc n) rewrite +-suc m n | same-app m n = refl

same : _+′_ ≡ _+_  -- this requires extensionality
same = extensionality λ x → extensionality λ x₁ → same-app x x₁

-- Isomorphism.

infix 0 _≃_
record _≃_ (A B : Set) : Set where
  constructor mk-≃  -- This has been added, not in PLFA
  field
    to   : A → B
    from : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x
    to∘from : ∀ (y : B) → to (from y) ≡ y
open _≃_

-- Equivalent to the following:

data _≃′_ (A B : Set): Set where
  mk-≃′ : ∀ (to : A → B) →
          ∀ (from : B → A) →
          ∀ (from∘to : (∀ (x : A) → from (to x) ≡ x)) →
          ∀ (to∘from : (∀ (y : B) → to (from y) ≡ y)) →
          A ≃′ B

to′ : ∀ {A B : Set} → (A ≃′ B) → (A → B)
to′ (mk-≃′ f g g∘f f∘g) = f

from′ : ∀ {A B : Set} → (A ≃′ B) → (B → A)
from′ (mk-≃′ f g g∘f f∘g) = g

from∘to′ : ∀ {A B : Set} → (A≃B : A ≃′ B)
                         → (∀ (x : A)
                         → from′ A≃B (to′ A≃B x) ≡ x)
from∘to′ (mk-≃′ f g g∘f f∘g) = g∘f

to∘from′ : ∀ {A B : Set} → (A≃B : A ≃′ B)
                         → (∀ (y : B)
                         → to′ A≃B (from′ A≃B y) ≡ y)
to∘from′ (mk-≃′ f g g∘f f∘g) = f∘g

-- End of equivalent formulation (records are faster!)

-- Properties of isomorphism.

-- Reflexivity.

≃-refl : ∀ {A : Set}
    -----
  → A ≃ A

-- in empty hole, split on result, get copatterns (not in PLFA)

to ≃-refl x = x
from ≃-refl x = x
from∘to ≃-refl x = refl
to∘from ≃-refl x = refl

-- Symmetry.

≃-sym : ∀ {A B : Set}
  → A ≃ B
    -----
  → B ≃ A

to (≃-sym A≃B) = from A≃B
from (≃-sym A≃B) = to A≃B
from∘to (≃-sym A≃B) = to∘from A≃B
to∘from (≃-sym A≃B) = from∘to A≃B

-- Transitivity.

≃-trans : ∀ {A B C : Set}
  → A ≃ B
  → B ≃ C
    -----
  → A ≃ C

to (≃-trans A≃B B≃C) = to B≃C ∘ to A≃B
from (≃-trans A≃B B≃C) = from A≃B ∘ from B≃C
from∘to (≃-trans A≃B B≃C) x rewrite from∘to B≃C (to A≃B x) = from∘to A≃B x
to∘from (≃-trans A≃B B≃C) x rewrite to∘from A≃B (from B≃C x) = to∘from B≃C x

-- Isomorphism is an equivalence relation.
-- We can create syntax for equational reasoning.

module ≃-Reasoning where

  infix  1 ≃-begin_
  infixr 2 _≃⟨_⟩_
  infix  3 _≃-∎

  ≃-begin_ : ∀ {A B : Set}
    → A ≃ B
      -----
    → A ≃ B
  ≃-begin A≃B = A≃B

  _≃⟨_⟩_ : ∀ (A : Set) {B C : Set}
    → A ≃ B
    → B ≃ C
      -----
    → A ≃ C
  A ≃⟨ A≃B ⟩ B≃C = ≃-trans A≃B B≃C

  _≃-∎ : ∀ (A : Set)
      -----
    → A ≃ A
  A ≃-∎ = ≃-refl

open ≃-Reasoning

-- Embedding (weaker than isomorphism)

infix 0 _≲_
record _≲_ (A B : Set) : Set where
  field
    to      : A → B
    from    : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x
open _≲_

≲-refl : ∀ {A : Set} → A ≲ A
to ≲-refl x = x
from ≲-refl x = x
from∘to ≲-refl x = refl

≲-trans : ∀ {A B C : Set} → A ≲ B → B ≲ C → A ≲ C
to (≲-trans A≲B B≲C) = to B≲C ∘ to A≲B
from (≲-trans A≲B B≲C) = from A≲B ∘ from B≲C
from∘to (≲-trans A≲B B≲C) x rewrite from∘to B≲C (to A≲B x) = from∘to A≲B x

≲-antisym : ∀ {A B : Set}
  → (A≲B : A ≲ B)
  → (B≲A : B ≲ A)
  → (to A≲B ≡ from B≲A)
  → (from A≲B ≡ to B≲A)
    -------------------
  → A ≃ B

to (≲-antisym A≲B B≲A to≡from from≡to) = to A≲B
from (≲-antisym A≲B B≲A to≡from from≡to) = from A≲B
from∘to (≲-antisym A≲B B≲A to≡from from≡to) x = from∘to A≲B x
to∘from (≲-antisym A≲B B≲A to≡from from≡to) y
  rewrite from≡to | to≡from = from∘to B≲A y

-- Tabular reasoning for embedding.

module ≲-Reasoning where

  infix  1 ≲-begin_
  infixr 2 _≲⟨_⟩_
  infix  3 _≲-∎

  ≲-begin_ : ∀ {A B : Set}
    → A ≲ B
      -----
    → A ≲ B
  ≲-begin A≲B = A≲B

  _≲⟨_⟩_ : ∀ (A : Set) {B C : Set}
    → A ≲ B
    → B ≲ C
      -----
    → A ≲ C
  A ≲⟨ A≲B ⟩ B≲C = ≲-trans A≲B B≲C

  _≲-∎ : ∀ (A : Set)
      -----
    → A ≲ A
  A ≲-∎ = ≲-refl

open ≲-Reasoning

-- PLFA exercise: Isomorphism implies embedding.

≃-implies-≲ : ∀ {A B : Set}
  → A ≃ B
    -----
  → A ≲ B

to (≃-implies-≲ a≃b) = to a≃b
from (≃-implies-≲ a≃b) = from a≃b
from∘to (≃-implies-≲ a≃b) = from∘to a≃b

-- PLFA exercise: propositional equivalence (weaker than embedding).

record _⇔_ (A B : Set) : Set where
  field
    to   : A → B
    from : B → A
open _⇔_ -- added

-- This is also an equivalence relation.

⇔-refl : ∀ {A : Set}
    -----
  → A ⇔ A

_⇔_.to ⇔-refl x = x
_⇔_.from ⇔-refl x = x

⇔-sym : ∀ {A B : Set}
  → A ⇔ B
    -----
  → B ⇔ A

_⇔_.to (⇔-sym A⇔B) = from A⇔B
_⇔_.from (⇔-sym A⇔B) = to A⇔B

⇔-trans : ∀ {A B C : Set}
  → A ⇔ B
  → B ⇔ C
    -----
  → A ⇔ C

to (⇔-trans A⇔B B⇔C) = to B⇔C ∘ to A⇔B
from (⇔-trans A⇔B B⇔C) = from A⇔B ∘ from B⇔C

-- 842 extended exercise: Canonical bitstrings.
-- Modified and extended from Bin-predicates exercise in PLFA Relations.

-- Copied from 842Naturals.

data Bin-ℕ : Set where
  bits : Bin-ℕ
  _x0 : Bin-ℕ → Bin-ℕ
  _x1 : Bin-ℕ → Bin-ℕ

dbl : ℕ → ℕ
dbl zero = zero
dbl (suc n) = suc (suc (dbl n))

-- Copy your versions of 'inc', 'tob', and 'fromb' over from earlier files.
-- You may choose to change the definitions here to make proofs easier.
-- But make sure to test them if you do!
-- You may also copy over any theorems that prove useful.

inc : Bin-ℕ → Bin-ℕ
inc n = {!!}

tob : ℕ → Bin-ℕ
tob n = {!!}

dblb : Bin-ℕ → Bin-ℕ
dblb n = {!!}

fromb : Bin-ℕ → ℕ
fromb n = {!!}

-- The reason that we couldn't prove ∀ {n : Bin-ℕ} → tob (fromb n) ≡ n
-- is because of the possibility of leading zeroes in a Bin-ℕ value.
-- 'bits x0 x0 x1' is such a value that gives a counterexample.
-- However, the theorem is true is true for n without leading zeroes.
-- We define a predicate to be able to state this in a theorem.
-- A value of type One n is evidence that n has a leading one.

data One : Bin-ℕ → Set where
  [bitsx1] : One (bits x1)
  _[x0] : ∀ {n : Bin-ℕ} → One n → One (n x0)
  _[x1] : ∀ {n : Bin-ℕ} → One n → One (n x1)

-- Here's a proof that 'bits x1 x0 x0' has a leading one.

_ : One (bits x1 x0 x0)
_ = [bitsx1] [x0] [x0]

-- There is no value of type One (bits x0 x0 x1).
-- But we can't state and prove this yet, because we don't know
-- how to express negation. That comes in the Connectives chapter.

-- A canonical binary representation is either zero or has a leading one.

data Can : Bin-ℕ → Set where
  [zero] : Can bits
  [pos]  : ∀ {n : Bin-ℕ} → One n → Can n

-- Some obvious examples:

_ : Can bits
_ = [zero]

_ : Can (bits x1 x0)
_ = [pos] ([bitsx1] [x0])

-- The Bin-predicates exercise in PLFA Relations gives three properties of canonicity.
-- The first is that the increment of a canonical number is canonical.

-- Most of the work is done in the following lemma.

-- 842 exercise: IncCanOne (2 points)
-- The increment of a canonical number has a leading one.

one-inc : ∀ {n : Bin-ℕ} → Can n → One (inc n)
one-inc cn = {!!}

-- The first canonicity property is now an easy corollary.

-- 842 exercise: OneInc (1 point)

can-inc : ∀ {n : Bin-ℕ} → Can n → Can (inc n)
can-inc cn = {!!}

-- The second canonicity property is that converting a unary number
-- to binary produces a canonical number.

-- 842 exercise: CanToB (1 point)

to-can : ∀ (n : ℕ) → Can (tob n)
to-can n = {!!}

-- The third canonicity property is that converting a canonical number
-- from binary and back to unary produces the same number.

-- This takes more work, and some helper lemmas from 842Induction.
-- You will need to discover which ones.

-- 842 exercise: OneDblbX0 (1 point)

-- This helper function relates binary double to the x0 constructor,
-- for numbers with a leading one.

dblb-x0 : ∀ {n : Bin-ℕ} → One n → dblb n ≡ n x0
dblb-x0 on = {!!}

-- We can now prove the third property for numbers with a leading one.

-- 842 exercise: OneToFrom (3 points)

one-to∘from : ∀ {n : Bin-ℕ} → One n → tob (fromb n) ≡ n
one-to∘from on = {!!}

-- The third property is now an easy corollary.

-- 842 exercise: CanToFrom (1 point)

can-to∘from : ∀ {n : Bin-ℕ} → Can n → tob (fromb n) ≡ n
can-to∘from cn = {!!}

-- 842 exercise: OneUnique (2 points)
-- Proofs of positivity are unique.

one-unique : ∀ {n : Bin-ℕ} → (x y : One n) → x ≡ y
one-unique x y = {!!}

-- 842 exercise: CanUnique (1 point)
-- Proofs of canonicity are unique.

can-unique : ∀ {n : Bin-ℕ} → (x y : Can n) → x ≡ y
can-unique x y = {!!}

-- Do we have an isomorphism between ℕ (unary) and canonical binary representations?
-- Can is not a set, but a family of sets, so it doesn't quite fit
-- into our framework for isomorphism.
-- But we can roll all the values into one set which is isomorphic to ℕ.

-- A CanR value wraps up a Bin-ℕ and proof it has a canonical representation.

data CanR : Set where
  wrap : ∀ (n : Bin-ℕ) → Can n → CanR

-- We can show that there is an isomorphism between ℕ and CanR.

-- 842 exercise: IsoNCanR (3 points)

iso-ℕ-CanR : ℕ ≃ CanR
iso-ℕ-CanR = {!!}

-- Can we get an isomorphism between ℕ and some binary encoding,
-- without the awkwardness of non-canonical values?
-- Yes: we use digits 1 and 2, instead of 0 and 1 (multiplier/base is still 2).
-- This is known as bijective binary numbering.
-- The counting sequence goes <empty>, 1, 2, 11, 12, 21, 22, 111...

data Bij-ℕ : Set where
  bits : Bij-ℕ
  _x1 : Bij-ℕ → Bij-ℕ
  _x2 : Bij-ℕ → Bij-ℕ

-- There is an isomorphism between ℕ and Bij-ℕ.
-- The proof largely follows the outline of what we did above,
-- and is left as an optional exercise.

-- See PLFA for remarks on standard library definitions similar to those here.

-- Unicode introduced in this chapter:

{-

  ∘  U+2218  RING OPERATOR (\o, \circ, \comp)
  λ  U+03BB  GREEK SMALL LETTER LAMBDA (\lambda, \Gl)
  ≃  U+2243  ASYMPTOTICALLY EQUAL TO (\~-)
  ≲  U+2272  LESS-THAN OR EQUIVALENT TO (\<~)
  ⇔  U+21D4  LEFT RIGHT DOUBLE ARROW (\<=>)

-}


module Integer10 where

open import Relation.Binary.PropositionalEquality
  as PropEq using (_≡_; refl)

-- 整数の素朴な定義
--（succ (succ (pred zero))などが有効、という弱点がある）
data ℤ : Set where
  zero : ℤ
  succ : ℤ → ℤ
  pred : ℤ → ℤ

-- 加法
_+_ : ℤ → ℤ → ℤ
zero + y = y
succ x + zero = succ x
succ x + succ y = succ (succ x + y)
succ x + pred y = x + y
pred x + zero = pred x
pred x + succ y = x + y
pred x + pred y = pred (pred x + y)

-- 反数
opposite : ℤ → ℤ
opposite zero = zero
opposite (succ x) = pred (opposite x)
opposite (pred x) = succ (opposite x)

-- 乗法
_*_ : ℤ → ℤ → ℤ
x * zero = zero
x * succ y = (x * y) + x
x * pred y = (x * y) + (opposite x)

infixl 40 _+_
infixl 60 _*_

-- (-1) * (-1) = (+1)
theorem : pred zero * pred zero ≡ succ zero
theorem = refl


------------------------------------------------------------------------------
-- Comparing styles for equational reasoning
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOT.Common.FOL.Relation.Binary.PreorderReasoning.Comparison where

infix  7 _≡_
infixl 9  _+_

data _≡_ {A : Set}(x : A) : A → Set where
  refl : x ≡ x

trans : {A : Set}{x y z : A} → x ≡ y → y ≡ z → x ≡ z
trans refl refl = refl

postulate
  ℕ               : Set
  zero            : ℕ
  _+_             : ℕ → ℕ → ℕ
  +-comm          : (m n : ℕ) → m + n ≡ n + m
  +-rightIdentity : (n : ℕ) → n + zero ≡ n

module Thesis where
  -- From Ulf's thesis (p. 112).

  infix  7 _≃_
  infix  6 chain>_
  infixl 5 _===_by_
  infix  4 _qed

  data _≃_ (x y : ℕ) : Set where
    prf : x ≡ y → x ≃ y

  chain>_ : (x : ℕ) → x ≃ x
  chain> x = prf refl

  _===_by_ : {x y : ℕ} → x ≃ y → (z : ℕ) → y ≡ z → x ≃ z
  prf p === z by q = prf (trans {_} {_} {_} p q)

  _qed : {x y : ℕ} → x ≃ y → x ≡ y
  prf p qed = p

  -- Example.
  +-leftIdentity : (n : ℕ) → zero + n ≡ n
  +-leftIdentity n =
    chain> zero + n
       === n + zero by +-comm zero n
       === n        by +-rightIdentity n
    qed

module SL where
  -- Adapted from the Agda standard library 0.8.1 (see
  -- Relation/Binary/PreorderReasoning.agda).

  infix  7 _≃_
  infix  4 begin_
  infixr 5 _≡⟨_⟩_
  infix  6 _∎

  data _≃_ (x y : ℕ) : Set where
    prf : x ≡ y → x ≃ y

  begin_ : {x y : ℕ} → x ≃ y → x ≡ y
  begin prf x≡y = x≡y

  _≡⟨_⟩_ : (x : ℕ){y z : ℕ} → x ≡ y → y ≃ z → x ≃ z
  _ ≡⟨ x≡y ⟩ prf y≡z = prf (trans x≡y y≡z)

  _∎ : (x : ℕ) → x ≃ x
  _∎ _ = prf refl

  -- Example.
  +-leftIdentity : (n : ℕ) → zero + n ≡ n
  +-leftIdentity n =
    begin
      zero + n ≡⟨ +-comm zero n ⟩
      n + zero ≡⟨ +-rightIdentity n ⟩
      n
    ∎

module NonWrapper where
  -- A set of combinators without request a wrapper data type (Mu,
  -- S.-C., Ko, H.-S. and Jansson, P. (2009)).

  infixr 5 _≡⟨_⟩_
  infix  6 _∎

  _≡⟨_⟩_ : (x : ℕ){y z : ℕ} → x ≡ y → y ≡ z → x ≡ z
  _ ≡⟨ x≡y ⟩ y≡z = trans x≡y y≡z

  _∎ : (x : ℕ) → x ≡ x
  _∎ _ = refl

  -- Example.
  +-leftIdentity : (n : ℕ) → zero + n ≡ n
  +-leftIdentity n = zero + n ≡⟨ +-comm zero n ⟩
                     n + zero ≡⟨ +-rightIdentity n ⟩
                     n        ∎

------------------------------------------------------------------------------
-- References
--
-- Mu, S.-C., Ko, H.-S. and Jansson, P. (2009). Algebra of programming
-- in Agda: Dependent types for relational program derivation. Journal
-- of Functional Programming 19.5, pp. 545–579.


module HasSatisfaction where

open import OscarPrelude
open import Interpretation

record HasSatisfaction (A : Set) : Set₁
 where
  field
    _⊨_ : Interpretation → A → Set

  _⊭_ : Interpretation → A → Set
  _⊭_ I = ¬_ ∘ I ⊨_

open HasSatisfaction ⦃ … ⦄ public

{-# DISPLAY HasSatisfaction._⊨_ _ = _⊨_ #-}
{-# DISPLAY HasSatisfaction._⊭_ _ = _⊭_ #-}

instance HasSatisfactionList : {A : Set} ⦃ _ : HasSatisfaction A ⦄ → HasSatisfaction $ List A
HasSatisfaction._⊨_ HasSatisfactionList I [] = ⊤
HasSatisfaction._⊨_ HasSatisfactionList I (x ∷ xs) = I ⊨ x × I ⊨ xs

module _ {A} ⦃ _ : HasSatisfaction A ⦄
 where

   ⊨_ : A → Set
   ⊨ x = (I : Interpretation) → I ⊨ x

   ⊭_ : A → Set
   ⊭_ = ¬_ ∘ ⊨_

record HasDecidableValidation (A : Set) ⦃ _ : HasSatisfaction A ⦄ : Set₁
 where
  field
    ⊨?_ : (x : A) → Dec $ ⊨ x

open HasDecidableValidation ⦃ … ⦄ public

{-# DISPLAY HasDecidableValidation.⊨?_ _ = ⊨?_ #-}

record HasDecidableSatisfaction (A : Set) ⦃ _ : HasSatisfaction A ⦄ : Set₁
 where
  field
    _⊨?_ : (I : Interpretation) → (x : A) → Dec (I ⊨ x)

open HasDecidableSatisfaction ⦃ … ⦄ public

{-# DISPLAY HasDecidableSatisfaction._⊨?_ _ = _⊨?_ #-}


module sv20.compiler where

open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_)
--open import Data.Nat.DivMod using (_/_)
open import Data.List using (List; _++_; []; _∷_; head)
open import Data.Maybe as DM
open DM using (Maybe; just; nothing; maybe; _>>=_; from-just; From-just)
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; sym; trans; cong; subst)
open import Data.Product using (∃-syntax) renaming (_,_ to ⟨_,_⟩)
open import Function.Base using (_∘_; flip)

pattern [_] z = z ∷ []
pattern [_,_] y z = y ∷ z ∷ []
pattern [_,_,_] x y z = x ∷ y ∷ z ∷ []
pattern [_,_,_,_] w x y z = w ∷ x ∷ y ∷ z ∷ []
pattern [_,_,_,_,_] v w x y z = v ∷ w ∷ x ∷ y ∷ z ∷ []
pattern [_,_,_,_,_,_] u v w x y z = u ∷ v ∷ w ∷ x ∷ y ∷ z ∷ []

data ExOp : Set where
  sum diff prod : ExOp

data Exp : Set where
  const : ℕ → Exp
  exop : ExOp → Exp → Exp → Exp

--exop sum (const 5) (const 2)

prim-ex : ExOp → ℕ → ℕ → ℕ
prim-ex sum  = _+_
prim-ex diff = _∸_
prim-ex prod = _*_

interpreter : Exp → ℕ
interpreter (const ℕ) = ℕ
interpreter (exop op x y) = prim-ex op (interpreter x) (interpreter y)

--interpreter (exop sum (const 5) (const 2))

data BinOp : Set where
  add sub mul : BinOp

data StackCmd : Set where
  push : ℕ → StackCmd
  binop : BinOp → StackCmd

Program = List StackCmd
Stack = List ℕ

prim-op : BinOp → ℕ → ℕ → ℕ
prim-op add = _+_
prim-op sub = _∸_
prim-op mul = _*_

run-vm : Program → Stack → Maybe Stack
run-vm [] st = just st
run-vm (push n ∷ ps) st = run-vm ps (n ∷ st)
run-vm (binop op ∷ ps) (n ∷ m ∷ st) = run-vm ps ((prim-op op n m) ∷ st)
run-vm _ _ = nothing

exop-binop : ExOp → BinOp
exop-binop sum = add
exop-binop diff = sub
exop-binop prod = mul

compile : Exp → Program
compile (const n) = [ push n ]
compile (exop op x y) = compile y ++ compile x ++ [ binop (exop-binop op) ]

-- (3 - 2) + 6  =>  + (- 3 2) 6   =>  "sum (diff 3 2) 6"
_ : compile (exop sum (exop diff (const 3) (const 2)) (const 6))
    ≡ [ push 6 , push 2 , push 3 , binop sub , binop add ]
_ = refl

_ : run-vm (compile (const 3)) [] ≡ just [ interpreter (const 3) ]
_ = refl

--run-vm [ push 2 , push 1 , binop add ] []
--run-vm [ push 1 , binop add ] [ 2 ]
--run-vm [ binop add ] [ 1 , 2 ]
_ :   (run-vm [ push 1 ] [ 2 ] >>= run-vm [ binop add ])
    ≡ run-vm [ push 1 , binop add ] [ 2 ]
_ = refl

lemma₁ : ∀ (a b : Program) (s : Stack) → run-vm (a ++ b) s ≡ (run-vm a s >>= run-vm b)
lemma₁ [] _ _ = refl
lemma₁ (push n ∷ ps) b s rewrite lemma₁ ps b (n ∷ s) = refl
lemma₁ (binop _ ∷ ps) b          []  = refl
lemma₁ (binop _ ∷ _)  _     (_ ∷ []) = refl
lemma₁ (binop op ∷ ps) b (n ∷ m ∷ st) rewrite lemma₁ ps b (prim-op op n m ∷ st) = refl

lemma₂ : ∀ {op : ExOp} → prim-op (exop-binop op) ≡ prim-ex op
lemma₂ {sum} = refl
lemma₂ {diff} = refl
lemma₂ {prod} = refl

compiler-correctness₁ : ∀ {e : Exp} {s : Stack} → run-vm (compile e) s ≡ just (interpreter e ∷ s)
compiler-correctness₁ {const n} = refl
compiler-correctness₁ {exop op x y} {s}
  rewrite
--    run-vm (compile (exop op x y)) s ≡ just (interpreter (exop op x y) ∷ s)
--
--    run-vm (compile y ++ compile x ++ [ binop (exop-binop op) ]) s ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
    lemma₁ (compile y) (compile x ++ [ binop (exop-binop op) ]) s
--    (run-vm (compile y) s >>= run-vm (compile x ++ [ binop (exop-binop op) ])) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
  | compiler-correctness₁ {y} {s}
--    (just (interpreter y ∷ s) >>= run-vm (compile x ++ [ binop (exop-binop op) ])) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
--    run-vm (compile x ++ [ binop (exop-binop op) ]) (interpreter y ∷ s) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
  | lemma₁ (compile x) [ binop (exop-binop op) ] (interpreter y ∷ s)
--    (run-vm (compile x) (interpreter y ∷ s) >>= run-vm [ binop (exop-binop op) ]) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
  | compiler-correctness₁ {x} {interpreter y ∷ s}
--    (just (interpreter x ∷ interpreter y ∷ s) >>= run-vm [ binop (exop-binop op) ]) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
--    just (prim-op (exop-binop op) (interpreter x) (interpreter y) ∷ s) ≡ just (prim-ex op (interpreter x) (interpreter y) ∷ s)
  | lemma₂ {op}
  = refl

compiler-correctness : ∀ (e : Exp) → run-vm (compile e) [] ≡ just [ interpreter e ]
compiler-correctness e = compiler-correctness₁ {e} {[]}

-- Which algorithm is it using when executed "interpreter" or "compile then
-- run-vm"?
-- Well, if I understand correctly. "from-just" will return the value
-- contained in "just" and "subst" will pass the values untouched. Which
-- would mean that there is an associated cost in proving stuff but the code
-- that is being used is "compile then run-vm"
run-vm∘compile : Exp → Maybe ℕ
run-vm∘compile e =
  let
    justln = from-just (run-vm (compile e) [])
    list = subst From-just (compiler-correctness e) justln
  in head list

_ : run-vm∘compile (const 5) ≡ just 5
_ = refl

reduce : {A : Set} → Maybe (List A) → Maybe A
reduce (just (x ∷ _)) = just x
reduce _ = nothing

_ : reduce (just [ 5 ]) ≡ just 5
_ = refl

-- Look, it is possible to execute compile and run-vm and get an unwrapped result
-- For an even prettier proof look at the end of the file
run-vm∘compile` : Exp → ℕ
run-vm∘compile` e =
  let
    computing = reduce (run-vm (compile e) [])
    justn = from-just computing
    redruncomp≡jinter = cong reduce (compiler-correctness e) -- reduce (run-vm (compile e) []) ≡ just (interpreter e)
  in subst From-just redruncomp≡jinter justn

_ : run-vm∘compile` (const 5) ≡ 5
_ = refl

---- So. I wanted to generalise the approach used before, but I'm stuck. Agda
---- seems not able to infer some variable.
---- It's because it is never using 'g', I think
---- Ans: NO. IT WAS BECAUSE x WAS IMPLICIT IN eq
--
--unwrap : {A B : Set}
--       → {g : A → B}
--       → (f : A → Maybe (List B))
--       → ({x : A} → f x ≡ just [ g x ])   -- <- This was the problem! It should be explicit!
--       --------------------------------
--       → A → B  -- Computed using f
--unwrap {A} {B} {g} f eq x =
--  let
--    computing = reduce (f x)  -- type: Maybe B
--    justb = from-just computing    -- type: From-just (f x)
--  in subst From-just new-eq justb -- type: B      -- thanks to the magic of From-just
--
--  where
--    new-eq : {x : A} → reduce (f x) ≡ just (g x)
--    new-eq {x} rewrite eq {x} = refl
--
---- Why doesn't this work!? Ans: See above or below
----run-vm∘compile`` : Exp → ℕ
----run-vm∘compile`` = unwrap {Exp} {ℕ}
----                          {interpreter}
----                          (λ e → run-vm (compile e) [])
----                          compiler-correctness

unwrap : {A B : Set} {g : A → B}
       → (f : A → Maybe (List B))
       → ((x : A) → f x ≡ just [ g x ])
       --------------------------------
       → A → B  -- Computed using f not g (g might still be run to prove the property correct)
unwrap f eq x =
  let
    -- Proof: f x ≡ just [ g x ]
    fx≡mlgx = eq x
    -- New proof: reduce (f x) ≡ just (g x)
    redfx≡gx = cong reduce {x = f x} fx≡mlgx
    compute = reduce (f x)          -- type: Maybe B
    justredfx = from-just compute       -- type: From-just (reduce (f x))  -- lifting to the type level, so we can prove stuff
  in subst From-just redfx≡gx justredfx -- type: From-just (just (g x))    which is the same as  `B`      -- thanks to the magic of From-just

--from-just (just 3)   -- evaluates to: 3
--                     -- with type:  From-just (just 3)
--                                 -- which evaluates to: ℕ
-- ie,
-- from-just (just 3) : From-just (just 3)
-- from-just (just 3) : ℕ
--                  3 : ℕ
--                  3 : From-just (just 3)

_ : from-just (just 3) ≡ 3
_ = refl

_ : From-just (just 3) ≡ ℕ
_ = refl

_ : Set
_ = From-just (just 3)

_ : From-just (just 3)  -- same as ℕ
_ = 3
_ = from-just (just 3)
_ = 2 -- This is an ℕ too

run-vm∘compile`` : Exp → ℕ
run-vm∘compile`` = unwrap ((flip run-vm []) ∘ compile) compiler-correctness
--run-vm∘compile`` = unwrap (λ e → run-vm (compile e) []) compiler-correctness

--LESSON LEARNT:
--Don't use implicit paramaters unless it makes for a clearer proof.
--Always use explicit parameters and make them implicit as you use the
--functions in more places (without breaking anything)

_ : run-vm∘compile`` (const 5) ≡ 5
_ = refl

I-am-a-number : ℕ
I-am-a-number = run-vm∘compile`` (exop sum (exop diff (const 3) (const 2)) (const 20))


open import Agda.Builtin.Nat
open import Agda.Builtin.Equality

record Eq (A : Set) : Set₁ where
  field
    _≈_ : A → A → Set

open Eq {{...}} public

record Setoid : Set₁ where
  field
    ∣_∣    : Set
    {{eq}} : Eq ∣_∣

open Setoid public

instance
  EqNat : Eq Nat
  _≈_ {{EqNat}} = _≡_

NatSetoid : Setoid
∣ NatSetoid ∣ = Nat

thm : (x : ∣ NatSetoid ∣) → x ≈ x
thm x = refl


------------------------------------------------------------------------
-- Commutative semirings with some additional structure ("almost"
-- commutative rings), used by the ring solver
------------------------------------------------------------------------

module Algebra.RingSolver.AlmostCommutativeRing where

open import Relation.Binary
open import Algebra
open import Algebra.Structures
open import Algebra.FunctionProperties
open import Algebra.Morphism
open import Data.Function

------------------------------------------------------------------------
-- Definitions

record IsAlmostCommutativeRing {A} (_≈_ : Rel A)
         (_+_ _*_ : Op₂ A) (-_ : Op₁ A) (0# 1# : A) : Set where
  field
    isCommutativeSemiring : IsCommutativeSemiring _≈_ _+_ _*_ 0# 1#
    -‿pres-≈              : -_ Preserves _≈_ ⟶ _≈_
    -‿*-distribˡ          : ∀ x y → ((- x) * y)     ≈ (- (x * y))
    -‿+-comm              : ∀ x y → ((- x) + (- y)) ≈ (- (x + y))

  open IsCommutativeSemiring isCommutativeSemiring public

record AlmostCommutativeRing : Set₁ where
  infix  8 -_
  infixl 7 _*_
  infixl 6 _+_
  infix  4 _≈_
  field
    carrier                 : Set
    _≈_                     : Rel carrier
    _+_                     : Op₂ carrier
    _*_                     : Op₂ carrier
    -_                      : Op₁ carrier
    0#                      : carrier
    1#                      : carrier
    isAlmostCommutativeRing :
      IsAlmostCommutativeRing _≈_ _+_ _*_ -_ 0# 1#

  open IsAlmostCommutativeRing isAlmostCommutativeRing public

  commutativeSemiring : CommutativeSemiring
  commutativeSemiring =
    record { isCommutativeSemiring = isCommutativeSemiring }

  open CommutativeSemiring commutativeSemiring public
         using ( setoid
               ; +-semigroup; +-monoid; +-commutativeMonoid
               ; *-semigroup; *-monoid; *-commutativeMonoid
               ; semiring
               )

  rawRing : RawRing
  rawRing = record
    { _≈_ = _≈_
    ; _+_ = _+_
    ; _*_ = _*_
    ; -_  = -_
    ; 0#  = 0#
    ; 1#  = 1#
    }

------------------------------------------------------------------------
-- Homomorphisms

_-Raw-AlmostCommutative⟶_ : RawRing → AlmostCommutativeRing → Set
from -Raw-AlmostCommutative⟶ to = from -RawRing⟶ rawRing to
  where open AlmostCommutativeRing

-raw-almostCommutative⟶
  : ∀ r →
    AlmostCommutativeRing.rawRing r -Raw-AlmostCommutative⟶ r
-raw-almostCommutative⟶ r = record
  { ⟦_⟧    = id
  ; +-homo = λ _ _ → refl
  ; *-homo = λ _ _ → refl
  ; -‿homo = λ _ → refl
  ; 0-homo = refl
  ; 1-homo = refl
  }
  where open AlmostCommutativeRing r

------------------------------------------------------------------------
-- Conversions

-- Commutative rings are almost commutative rings.

fromCommutativeRing : CommutativeRing → AlmostCommutativeRing
fromCommutativeRing cr = record
  { isAlmostCommutativeRing = record
      { isCommutativeSemiring = isCommutativeSemiring
      ; -‿pres-≈              = -‿pres-≈
      ; -‿*-distribˡ          = -‿*-distribˡ
      ; -‿+-comm              = -‿∙-comm
      }
  }
  where
  open CommutativeRing cr
  import Algebra.Props.Ring as R; open R ring
  import Algebra.Props.AbelianGroup as AG; open AG +-abelianGroup

-- Commutative semirings can be viewed as almost commutative rings by
-- using identity as the "almost negation".

fromCommutativeSemiring : CommutativeSemiring → AlmostCommutativeRing
fromCommutativeSemiring cs = record
  { -_                      = id
  ; isAlmostCommutativeRing = record
      { isCommutativeSemiring = isCommutativeSemiring
      ; -‿pres-≈              = id
      ; -‿*-distribˡ          = λ _ _ → refl
      ; -‿+-comm              = λ _ _ → refl
      }
  }
  where open CommutativeSemiring cs