open import MJ.Types
import MJ.Classtable.Core as Core
import MJ.Classtable.Code as Code
import MJ.Syntax as Syntax

module MJ.Semantics.Objects.Flat {c}(Ct : Core.Classtable c)(ℂ : Code.Code Ct) where

open import Prelude

open import Level renaming (suc to lsuc; zero to lzero)
open import Data.Vec hiding (_++_; lookup; drop)
open import Data.Star
open import Data.List
open import Data.List.First.Properties
open import Data.List.Membership.Propositional
open import Data.List.Any.Properties
open import Data.List.All as All
open import Data.List.All.Properties.Extra
open import Data.List.Prefix
open import Data.List.Properties.Extra
open import Data.Maybe as Maybe
open import Data.Maybe.Relation.Unary.All as MayAll using ()
open import Data.String using (String)
open import Relation.Binary.PropositionalEquality

open import MJ.Semantics.Values Ct
open import MJ.Semantics.Objects Ct
open import MJ.Syntax Ct
open Code Ct
open Core c
open Classtable Ct
open import MJ.Classtable.Membership Ct

private

  collect : ∀ {cid}(chain : Σ ⊢ cid <: Object) ns → List (String × typing ns)
  collect (super {cid} ◅ ch) ns = Class.decls (Σ (cls cid)) ns ++ collect ch ns
  collect ε ns = Class.decls (Σ Object) ns

  {-
  We define collect members by delegation to collection of members over a particular
  inheritance chain.
  This makes the definition structurally recursive and is sound by the fact that
  inheritance chains are unique.
  -}
  collect-members : ∀ cid ns → List (String × typing ns)
  collect-members cid ns = collect (rooted cid) ns

  {-
  An Object is then simply represented by a list of values for all it's members.
  -}
  Obj : (World c) → (cid : _) → Set
  Obj W cid = All (λ d → Val W (proj₂ d)) (collect-members cid FIELD)

  weaken-obj : ∀ {W W'} cid → W' ⊒ W → Obj W cid → Obj W' cid
  weaken-obj cid ext O = All.map (weaken-val ext) O

  {-
  We can relate the flat Object structure with the hierarchical notion of membership
  through the definition of `collect`.
  This allows us to get and set members on Objects.
  -}
  {-# TERMINATING #-}
  getter : ∀ {W n a} cid → Obj W cid → IsMember cid FIELD n a → Val W a
  getter cid O q with rooted cid
  getter .Object O (.Object , ε , def) | ε = ∈-all (proj₁ (first⟶∈ def)) O
  getter .Object O (_ , () ◅ _ , _) | ε
  getter ._ O (._ , ε , def) | super {cid} ◅ z
    with split-++ (Class.decls (Σ (cls _)) FIELD) _ O
  ... | own , inherited = ∈-all (proj₁ (first⟶∈ def)) own
  getter ._ O (pid , super {cid} ◅ s , def) | super ◅ z
    with split-++ (Class.decls (Σ (cls _)) FIELD) _ O
  ... | own , inherited rewrite <:-unique z (rooted (Class.parent (Σ (cls cid)))) =
    getter _ inherited (pid , s , def)

  {-# TERMINATING #-}
  setter : ∀ {W f a} cid → Obj W cid → IsMember cid FIELD f a → Val W a → Obj W cid
  setter (cls cid) O q v with rooted (cls cid)
  setter (cls cid) O (._ , ε , def) v | super ◅ z with split-++ (Class.decls (Σ (cls _)) FIELD) _ O
  ... | own , inherited =
    (own All.[ proj₁ (first⟶∈ def) ]≔ v) ++-all inherited
  setter (cls cid) O (._ , super ◅ s , def) v | super ◅ z with split-++ (Class.decls (Σ (cls _)) FIELD) _ O
  ... | own , inherited rewrite <:-unique z (rooted (Class.parent (Σ (cls cid)))) =
    own ++-all setter _ inherited (_ , s , def) v
  setter Object O mem v = ⊥-elim (∉Object mem)

  {-
  A default object instance can be created for every class simply by tabulating `default`
  over the types of all fields of a class.
  -}
  defaultObject' : ∀ {W cid} → (ch : Σ ⊢ cid <: Object) → All (λ d → Val W (proj₂ d)) (collect ch FIELD)
  defaultObject' ε rewrite Σ-Object = []
  defaultObject' {cid = Object} (() ◅ _)
  defaultObject' {cid = cls x} (super ◅ z) =
    All.tabulate {xs = Class.decls (Σ (cls x)) FIELD} (λ{ {_ , ty} _ → default ty})
    ++-all
    defaultObject' z

{-
We collect these definitions under the abstract ObjEncoding interface for usage in the semantics
-}
encoding : ObjEncoding
encoding = record {
    Obj = Obj ;
    weaken-obj = λ cid ext O → weaken-obj cid ext O ;
    getter = getter ;
    setter = setter ;
    defaultObject = λ cid → defaultObject' (rooted cid)
  }


------------------------------------------------------------------------
-- A quotient inductive-inductive definition of the partiality monad
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --safe #-}

module Partiality-monad.Inductive where

import Equality.Path as P
open import Equality.Propositional.Cubical
open import Logical-equivalence using (_⇔_)
open import Prelude hiding (⊥)

open import Bijection equality-with-J using (_↔_)
open import Equivalence equality-with-J as Eq using (_≃_)
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J

open import Partiality-algebra as PA hiding (_∘_)
open import Partiality-algebra.Eliminators as PAE hiding (Arguments)
import Partiality-algebra.Properties

private
  variable
    a ℓ p q : Level
    A       : Type a

mutual

  infix 10 _⊥
  infix 4  _⊑_

  -- Originally the monad was postulated. After the release of Cubical
  -- Agda it was turned into a QIIT, but in order to not get any extra
  -- computation rules it was made abstract.

  abstract

    -- The partiality monad.

    data _⊥ (A : Type a) : Type a where
      never        : A ⊥
      now          : A → A ⊥
      ⨆            : Increasing-sequence A → A ⊥
      antisymmetry : x ⊑ y → y ⊑ x → x P.≡ y

      -- We have chosen to explicitly make the type set-truncated.
      -- However, this constructor is mostly unused: it is used to
      -- define partiality-algebra below (and the corresponding record
      -- field is in turn also mostly unused, see its documentation
      -- for details), and it is mentioned in the implementation of
      -- eliminators (also below).
      ⊥-is-set-unused : P.Is-set (A ⊥)

  -- Increasing sequences.

  Increasing-sequence : Type ℓ → Type ℓ
  Increasing-sequence A =
    Σ (ℕ → A ⊥) λ f → ∀ n → f n ⊑ f (suc n)

  -- Upper bounds.

  Is-upper-bound :
    {A : Type a} → Increasing-sequence A → A ⊥ → Type a
  Is-upper-bound s x = ∀ n → (s [ n ]) ⊑ x

  -- A projection function for Increasing-sequence.

  infix 30 _[_]

  _[_] : Increasing-sequence A → ℕ → A ⊥
  _[_] s n = proj₁ {A = _ → _ ⊥} s n

  private
    variable
      x y z : A ⊥

  abstract

    -- An ordering relation.

    data _⊑_ {A : Type a} : A ⊥ → A ⊥ → Type a where
      ⊑-refl            : ∀ x → x ⊑ x
      ⊑-trans           : x ⊑ y → y ⊑ z → x ⊑ z
      never⊑            : ∀ x → never ⊑ x
      upper-bound       : ∀ s → Is-upper-bound s (⨆ s)
      least-upper-bound : ∀ s ub → Is-upper-bound s ub → ⨆ s ⊑ ub
      ⊑-propositional   : P.Is-proposition (x ⊑ y)

-- A partiality algebra for the partiality monad.

partiality-algebra : ∀ {a} (A : Type a) → Partiality-algebra a a A
partiality-algebra A = record
  { T                       = A ⊥
  ; partiality-algebra-with = record
    { _⊑_               = _⊑_
    ; never             = never′
    ; now               = now′
    ; ⨆                 = ⨆′
    ; antisymmetry      = antisymmetry′
    ; T-is-set-unused   = T-is-set-unused′
    ; ⊑-refl            = ⊑-refl′
    ; ⊑-trans           = ⊑-trans′
    ; never⊑            = never⊑′
    ; upper-bound       = upper-bound′
    ; least-upper-bound = least-upper-bound′
    ; ⊑-propositional   = ⊑-propositional′
    }
  }
  where

  abstract

    never′ : A ⊥
    never′ = never

    now′ : A → A ⊥
    now′ = now

    ⨆′ : Increasing-sequence A → A ⊥
    ⨆′ = ⨆

    antisymmetry′ : x ⊑ y → y ⊑ x → x ≡ y
    antisymmetry′ = λ p q → _↔_.from ≡↔≡ (antisymmetry p q)

    T-is-set-unused′ : Is-set (A ⊥)
    T-is-set-unused′ =
                      $⟨ (λ {x y} → ⊥-is-set-unused {x = x} {y = y}) ⟩
      P.Is-set (A ⊥)  ↝⟨ _↔_.from (H-level↔H-level 2) ⟩□
      Is-set (A ⊥)    □

    ⊑-refl′ : (x : A ⊥) → x ⊑ x
    ⊑-refl′ = ⊑-refl

    ⊑-trans′ : x ⊑ y → y ⊑ z → x ⊑ z
    ⊑-trans′ = ⊑-trans

    never⊑′ : ∀ x → never′ ⊑ x
    never⊑′ = never⊑

    upper-bound′ : ∀ s → Is-upper-bound s (⨆′ s)
    upper-bound′ = upper-bound

    least-upper-bound′ : ∀ s ub → Is-upper-bound s ub → ⨆′ s ⊑ ub
    least-upper-bound′ = least-upper-bound

    ⊑-propositional′ : Is-proposition (x ⊑ y)
    ⊑-propositional′ {x = x} {y = y} =
                                $⟨ ⊑-propositional ⟩
      P.Is-proposition (x ⊑ y)  ↝⟨ _↔_.from (H-level↔H-level 1) ⟩□
      Is-proposition (x ⊑ y)    □

abstract

  -- The elimination principle.

  eliminators : Elimination-principle p q (partiality-algebra A)
  eliminators {A = A} args = record
    { ⊥-rec       = ⊥-rec
    ; ⊑-rec       = ⊑-rec
    ; ⊥-rec-never = refl
    ; ⊥-rec-now   = λ _ → refl
    ; ⊥-rec-⨆     = λ _ → refl
    }
    where
    module A = PAE.Arguments args

    mutual

      inc-rec : (s : Increasing-sequence A) → A.Inc A.P A.Q s
      inc-rec (s , inc) = (λ n → ⊥-rec (s   n))
                        , (λ n → ⊑-rec (inc n))

      ⊥-rec : (x : A ⊥) → A.P x
      ⊥-rec never                                     = A.pe
      ⊥-rec (now x)                                   = A.po x
      ⊥-rec (⨆ s)                                     = A.pl s (inc-rec s)
      ⊥-rec (antisymmetry {x = x} {y = y} p q i)      = subst≡→[]≡
                                                          {B = A.P}
                                                          {eq = antisymmetry p q}
                                                          (A.pa p q
                                                             (⊥-rec x) (⊥-rec y)
                                                             (⊑-rec p) (⊑-rec q))
                                                          i
      ⊥-rec (⊥-is-set-unused {x = x} {y = y} p q i j) = lemma i j
        where
        ⊥-is-set-unused′ : P.Is-set (A ⊥)
        ⊥-is-set-unused′ = ⊥-is-set-unused

        lemma :
          P.[ (λ i → P.[ (λ j → A.P (⊥-is-set-unused′ p q i j)) ]
                       ⊥-rec x ≡ ⊥-rec y) ]
            (λ i → ⊥-rec (p i)) ≡ (λ i → ⊥-rec (q i))
        lemma = P.heterogeneous-UIP
                  (λ x → _↔_.to (H-level↔H-level 2) (A.pp {x = x}))
                  (⊥-is-set-unused p q)
                  _ _

      ⊑-rec : (x⊑y : x ⊑ y) → A.Q (⊥-rec x) (⊥-rec y) x⊑y
      ⊑-rec (⊑-refl x)                              = A.qr x (⊥-rec x)
      ⊑-rec (⊑-trans {x = x} {y = y} {z = z} p q)   = A.qt p q
                                                        (⊥-rec x) (⊥-rec y) (⊥-rec z)
                                                        (⊑-rec p) (⊑-rec q)
      ⊑-rec (never⊑ x)                              = A.qe x (⊥-rec x)
      ⊑-rec (upper-bound s n)                       = A.qu s (inc-rec s) n
      ⊑-rec (least-upper-bound s ub is-ub)          = A.ql s ub is-ub
                                                        (inc-rec s) (⊥-rec ub)
                                                        (λ n → ⊑-rec {x = proj₁ s n}
                                                                     (is-ub n))
      ⊑-rec (⊑-propositional {x = x} {y = y} p q i) = lemma i
        where
        ⊑-propositional′ : P.Is-proposition (x ⊑ y)
        ⊑-propositional′ = ⊑-propositional

        lemma : P.[ (λ i → A.Q (⊥-rec x) (⊥-rec y)
                             (⊑-propositional′ p q i)) ]
                  ⊑-rec p ≡ ⊑-rec q
        lemma =
          P.heterogeneous-irrelevance
            (λ p → _↔_.to (H-level↔H-level 1)
                     (A.qp (⊥-rec x) (⊥-rec y) p))

module _ {A : Type a} where

  open Partiality-algebra (partiality-algebra A) public
    hiding (T; _⊑_; _[_]; Increasing-sequence; Is-upper-bound)
    renaming ( T-is-set to ⊥-is-set
             ; equality-characterisation-T to
               equality-characterisation-⊥
             )

  open Partiality-algebra.Properties (partiality-algebra A) public

-- The eliminators' arguments.

Arguments : ∀ {a} p q (A : Type a) → Type (a ⊔ lsuc (p ⊔ q))
Arguments p q A = PAE.Arguments p q (partiality-algebra A)

module _ (args : Arguments p q A) where
  open Eliminators (eliminators args) public

-- Initiality.

initial : Initial p q (partiality-algebra A)
initial = eliminators→initiality _ eliminators


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

open import Data.Empty
open import Data.Product
open import Data.List using ([]; _∷_; _∷ʳ_; _++_)

open import Relation.Nullary
open import Relation.Unary using (_∈_; _⊆_;_∉_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
import Relation.Binary.HeterogeneousEquality as Het

open import Common

module HasTrace {ℙ : Set} (message : Message ℙ)
  where

open import Trace message
open import SessionType message
open import Transitions message

_HasTrace_ : SessionType -> Trace -> Set
_HasTrace_ T φ = ∃[ S ] (Defined S × Transitions T φ S)

after : ∀{T φ} -> T HasTrace φ -> SessionType
after (S , _ , _) = S

-- data _HasTrace_ : SessionType -> Trace -> Set where
--   does : ∀{T φ S} (tr : Transitions T φ S) (def : Defined S) -> T Does φ

_HasTrace?_ : (T : SessionType) (φ : Trace) -> Dec (T HasTrace φ)
nil HasTrace? _ = no λ { (_ , () , refl)
                       ; (_ , _ , step () _) }
inp f HasTrace? [] = yes (_ , inp , refl)
inp f HasTrace? (I x ∷ φ) with x ∈? f
... | no nfx = no λ { (_ , def , step inp tr) → nfx (transitions+defined->defined tr def) }
... | yes fx with f x .force HasTrace? φ
... | yes (_ , def , tr) = yes (_ , def , step inp tr)
... | no ntφ = no λ { (_ , def , step inp tr) → ntφ (_ , def , tr) }
inp f HasTrace? (O x ∷ φ) = no λ { (_ , _ , step () _) }
out f HasTrace? [] = yes (_ , out , refl)
out f HasTrace? (I x ∷ φ) = no λ { (_ , _ , step () _) }
out f HasTrace? (O x ∷ φ) with x ∈? f
... | no nfx = no λ { (_ , _ , step (out fx) _) → nfx fx }
... | yes fx with f x .force HasTrace? φ
... | yes (_ , def , tr) = yes (_ , def , step (out fx) tr)
... | no ntφ = no λ { (_ , def , step (out fx) tr) → ntφ (_ , def , tr) }

has-trace->defined : ∀{T φ} -> T HasTrace φ -> Defined T
has-trace->defined (_ , tdef , tr) = transitions+defined->defined tr tdef

inp-has-trace : ∀{f x φ} -> f x .force HasTrace φ -> inp f HasTrace (I x ∷ φ)
inp-has-trace (_ , def , tr) = _ , def , step inp tr

inp-has-no-trace : ∀{f x φ} -> ¬ f x .force HasTrace φ -> ¬ inp f HasTrace (I x ∷ φ)
inp-has-no-trace nφ (_ , def , step inp tr) = nφ (_ , def , tr)

out-has-trace : ∀{f x φ} -> f x .force HasTrace φ -> out f HasTrace (O x ∷ φ)
out-has-trace (_ , def , tr) = _ , def , step (out (transitions+defined->defined tr def)) tr

out-has-no-trace : ∀{f x φ} -> ¬ f x .force HasTrace φ -> ¬ out f HasTrace (O x ∷ φ)
out-has-no-trace nφ (_ , def , step (out fx) tr) = nφ (_ , def , tr)

out-has-no-trace->undefined : ∀{f x} -> ¬ out f HasTrace (O x ∷ []) -> ¬ Defined (f x .force)
out-has-no-trace->undefined {f} {x} nt with x ∈? f
... | yes fx = ⊥-elim (nt (_ , fx , step (out fx) refl))
... | no nfx = nfx

unprefix-some : ∀{α φ ψ} -> (α ∷ ψ) ⊑ (α ∷ φ) -> ψ ⊑ φ
unprefix-some (some pre) = pre

⊑-has-trace : ∀{T φ ψ} -> ψ ⊑ φ -> T HasTrace φ -> T HasTrace ψ
⊑-has-trace none (_ , tdef , tr) = _ , transitions+defined->defined tr tdef , refl
⊑-has-trace (some pre) (_ , tdef , step t tr) =
  let _ , sdef , sr = ⊑-has-trace pre (_ , tdef , tr) in
  _ , sdef , step t sr

split-trace : ∀{T φ ψ} (tφ : T HasTrace φ) -> T HasTrace (φ ++ ψ) -> after tφ HasTrace ψ
split-trace (_ , tdef , refl) tφψ = tφψ
split-trace (_ , tdef , step inp tr) (_ , sdef , step inp sr) = split-trace (_ , tdef , tr) (_ , sdef , sr)
split-trace (_ , tdef , step (out _) tr) (_ , sdef , step (out _) sr) = split-trace (_ , tdef , tr) (_ , sdef , sr)

join-trace : ∀{T φ ψ} (tφ : T HasTrace φ) -> after tφ HasTrace ψ -> T HasTrace (φ ++ ψ)
join-trace (_ , _ , refl) tφ/ψ = tφ/ψ
join-trace (_ , tdef , step t tr) tφ/ψ =
  let (_ , sdef , sr) = join-trace (_ , tdef , tr) tφ/ψ in
  _ , sdef , step t sr

⊑-has-co-trace : ∀{T φ ψ} -> φ ⊑ ψ -> T HasTrace (co-trace ψ) -> T HasTrace (co-trace φ)
⊑-has-co-trace le tψ = ⊑-has-trace (⊑-co-trace le) tψ

⊑-tran-has-trace : ∀{T φ ψ χ} (pre1 : φ ⊑ ψ) (pre2 : ψ ⊑ χ) ->
  (tχ : T HasTrace χ) ->
  ⊑-has-trace pre1 (⊑-has-trace pre2 tχ) ≡ ⊑-has-trace (⊑-tran pre1 pre2) tχ
⊑-tran-has-trace none none tχ = refl
⊑-tran-has-trace none (some pre2) (fst , fst₁ , step t snd) = refl
⊑-tran-has-trace (some pre1) (some pre2) (_ , def , step _ tr) rewrite ⊑-tran-has-trace pre1 pre2 (_ , def , tr) = refl

⊑-has-trace-after : ∀{T φ} (tφ : T HasTrace φ) -> ⊑-has-trace (⊑-refl φ) tφ ≡ tφ
⊑-has-trace-after (_ , _ , refl) = refl
⊑-has-trace-after (_ , tdef , step inp tr) rewrite ⊑-has-trace-after (_ , tdef , tr) = refl
⊑-has-trace-after (_ , tdef , step (out _) tr) rewrite ⊑-has-trace-after (_ , tdef , tr) = refl

nil-has-no-trace : ∀{φ} -> ¬ nil HasTrace φ
nil-has-no-trace (_ , () , refl)
nil-has-no-trace (_ , _ , step () _)

end-has-empty-trace : ∀{φ T} -> End T -> T HasTrace φ -> φ ≡ []
end-has-empty-trace (inp U) (_ , _ , refl) = refl
end-has-empty-trace (inp U) (_ , def , step inp refl) = ⊥-elim (U _ def)
end-has-empty-trace (inp U) (_ , def , step inp (step t _)) = ⊥-elim (U _ (transition->defined t))
end-has-empty-trace (out U) (_ , _ , refl) = refl
end-has-empty-trace (out U) (_ , _ , step (out !x) _) = ⊥-elim (U _ !x)

has-trace-++ : ∀{T φ ψ} -> T HasTrace (φ ++ ψ) -> T HasTrace φ
has-trace-++ tφψ = ⊑-has-trace ⊑-++ tφψ

trace-coherence : ∀{T φ ψ₁ ψ₂ x y} -> T HasTrace (φ ++ I x ∷ ψ₁) -> T HasTrace (φ ++ O y ∷ ψ₂) -> ⊥
trace-coherence {_} {[]} (_ , _ , step inp _) (_ , _ , step () _)
trace-coherence {_} {I _ ∷ _} (_ , tdef , step inp tr) (_ , sdef , step inp sr) = trace-coherence (_ , tdef , tr) (_ , sdef , sr)
trace-coherence {_} {O _ ∷ _} (_ , tdef , step (out _) tr) (_ , sdef , step (out _) sr) = trace-coherence (_ , tdef , tr) (_ , sdef , sr)

defined->has-empty-trace : ∀{T} -> Defined T -> T HasTrace []
defined->has-empty-trace inp = _ , inp , refl
defined->has-empty-trace out = _ , out , refl

has-trace-double-negation : ∀{T φ} -> ¬ ¬ T HasTrace φ -> T HasTrace φ
has-trace-double-negation {T} {φ} p with T HasTrace? φ
... | yes tφ = tφ
... | no ntφ = ⊥-elim (p ntφ)

{- New -}
not-nil-has-trace : ∀{ϕ} → ¬ (nil HasTrace ϕ)
not-nil-has-trace (.(inp _) , inp , step () _)
not-nil-has-trace (.(out _) , out , step () _)

trace-after-in : ∀{f x ϕ} → (inp f) HasTrace (I x ∷ ϕ) → (f x .force) HasTrace ϕ
trace-after-in (_ , def , step inp red) = _ , def , red

trace-after-out : ∀{f x ϕ} → (out f) HasTrace (O x ∷ ϕ) → (f x .force) HasTrace ϕ
trace-after-out (_ , def , step (out _) red) = _ , def , red

inp-hastrace->defined : ∀{f x tr} → (inp f) HasTrace (I x ∷ tr) → x ∈ dom f
inp-hastrace->defined (_ , def , step inp refl) = def
inp-hastrace->defined (_ , def , step inp (step red _)) = transition->defined red

empty-inp-has-empty-trace : ∀{f ϕ} → EmptyContinuation f → (inp f) HasTrace ϕ → ϕ ≡ []
empty-inp-has-empty-trace e (_ , _ , refl) = refl
empty-inp-has-empty-trace {f} e (_ , _ , step (inp {x = x}) reds) with Defined? (f x .force)
empty-inp-has-empty-trace {f} e (_ , def , step (inp {x = _}) refl) | no ¬def = ⊥-elim (¬def def)
empty-inp-has-empty-trace {f} e (_ , _ , step (inp {x = _}) (step t _)) | no ¬def = ⊥-elim (¬def (transition->defined t))
... | yes def = ⊥-elim (e _ def)

empty-out-has-empty-trace : ∀{f ϕ} → EmptyContinuation f → (out f) HasTrace ϕ → ϕ ≡ []
empty-out-has-empty-trace e (_ , _ , refl) = refl
empty-out-has-empty-trace e (_ , _ , step (out def) _) = ⊥-elim (e _ def)


------------------------------------------------------------------------
-- Aczel's structure identity principle (for 1-categories), more or
-- less as found in "Homotopy Type Theory: Univalent Foundations of
-- Mathematics" (first edition)
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

open import Equality

module Structure-identity-principle
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Bijection eq using (_↔_; Σ-≡,≡↔≡)
open import Category eq
open Derived-definitions-and-properties eq
open import Equality.Decidable-UIP eq
open import Equivalence eq hiding (id; _∘_; inverse; lift-equality)
open import Function-universe eq hiding (id) renaming (_∘_ to _⊚_)
open import H-level eq
open import H-level.Closure eq
open import Logical-equivalence using (_⇔_)
open import Prelude hiding (id)

-- Standard notions of structure.

record Standard-notion-of-structure
         {c₁ c₂} ℓ₁ ℓ₂ (C : Precategory c₁ c₂) :
         Type (c₁ ⊔ c₂ ⊔ lsuc (ℓ₁ ⊔ ℓ₂)) where
  open Precategory C

  field
    P               : Obj → Type ℓ₁
    H               : ∀ {X Y} (p : P X) (q : P Y) → Hom X Y → Type ℓ₂
    H-prop          : ∀ {X Y} {p : P X} {q : P Y}
                      (f : Hom X Y) → Is-proposition (H p q f)
    H-id            : ∀ {X} {p : P X} → H p p id
    H-∘             : ∀ {X Y Z} {p : P X} {q : P Y} {r : P Z} {f g} →
                      H p q f → H q r g → H p r (g ∙ f)
    H-antisymmetric : ∀ {X} (p q : P X) →
                      H p q id → H q p id → p ≡ q

  -- P constructs sets. (The proof was suggested by Michael Shulman in
  -- a mailing list post.)

  P-set : ∀ A → Is-set (P A)
  P-set A = propositional-identity⇒set
    (λ p q → H p q id × H q p id)
    (λ _ _ → ×-closure 1 (H-prop id) (H-prop id))
    (λ _ → H-id , H-id)
    (λ p q → uncurry (H-antisymmetric p q))

  -- Two Str morphisms (see below) of equal type are equal if their
  -- first components are equal.

  lift-equality : {X Y : ∃ P} {f g : ∃ (H (proj₂ X) (proj₂ Y))} →
                  proj₁ f ≡ proj₁ g → f ≡ g
  lift-equality eq = Σ-≡,≡→≡ eq (H-prop _ _ _)

  -- A derived precategory.

  Str : Precategory (c₁ ⊔ ℓ₁) (c₂ ⊔ ℓ₂)
  Str = record { precategory =
    ∃ P ,
    (λ { (X , p) (Y , q) →
         ∃ (H p q) ,
         Σ-closure 2 Hom-is-set (λ f → mono₁ 1 (H-prop f)) }) ,
    (id , H-id) ,
    (λ { (f , hf) (g , hg) → f ∙ g , H-∘ hg hf }) ,
    lift-equality left-identity ,
    lift-equality right-identity ,
    lift-equality assoc }

  module Str = Precategory Str

  -- A rearrangement lemma.

  proj₁-≡→≅-¹ :
    ∀ {X Y} (X≡Y : X ≡ Y) →
    proj₁ (Str.≡→≅ X≡Y Str.¹) ≡
    elim (λ {X Y} _ → Hom X Y) (λ _ → id) (cong proj₁ X≡Y)
  proj₁-≡→≅-¹ {X , p} = elim¹
    (λ X≡Y → proj₁ (Str.≡→≅ X≡Y Str.¹) ≡
             elim (λ {X Y} _ → Hom X Y) (λ _ → id) (cong proj₁ X≡Y))
    (proj₁ (Str.≡→≅ (refl (X , p)) Str.¹)                               ≡⟨ cong (proj₁ ∘ Str._¹) $ elim-refl (λ {X Y} _ → X Str.≅ Y) _ ⟩
     proj₁ (Str.id {X = X , p})                                         ≡⟨⟩
     id {X = X}                                                         ≡⟨ sym $ elim-refl (λ {X Y} _ → Hom X Y) _ ⟩
     elim (λ {X Y} _ → Hom X Y) (λ _ → id) (refl X)                     ≡⟨ cong (elim (λ {X Y} _ → Hom X Y) _) $ sym $ cong-refl proj₁ ⟩∎
     elim (λ {X Y} _ → Hom X Y) (λ _ → id) (cong proj₁ (refl (X , p)))  ∎)

-- The structure identity principle states that the precategory Str is
-- a category (assuming extensionality).
--
-- The proof below is based on (but not quite identical to) the one in
-- "Homotopy Type Theory: Univalent Foundations of Mathematics" (first
-- edition).

abstract

  structure-identity-principle :
    ∀ {c₁ c₂ ℓ₁ ℓ₂} →
    Extensionality (ℓ₁ ⊔ ℓ₂) (ℓ₁ ⊔ ℓ₂) →
    (C : Category c₁ c₂) →
    (S : Standard-notion-of-structure ℓ₁ ℓ₂ (Category.precategory C)) →
    ∀ {X Y} → Is-equivalence
                (Precategory.≡→≅ (Standard-notion-of-structure.Str S)
                                 {X} {Y})
  structure-identity-principle ext C S =
    Str.≡→≅-equivalence-lemma ≡≃≅ ≡≃≅-refl
    where
    open Standard-notion-of-structure S
    module C = Category C

    -- _≡_ is pointwise equivalent to Str._≅_.

    module ≅HH≃≅ where

      to : ∀ {X Y} {p : P X} {q : P Y} →
           (∃ λ (f : X C.≅ Y) → H p q (f C.¹) × H q p (f C.⁻¹)) →
           (X , p) Str.≅ (Y , q)
      to ((f , f⁻¹ , f∙f⁻¹ , f⁻¹∙f) , Hf , Hf⁻¹) =
        (f , Hf) , (f⁻¹ , Hf⁻¹) ,
        lift-equality f∙f⁻¹ ,
        lift-equality f⁻¹∙f

    ≅HH≃≅ : ∀ {X Y} {p : P X} {q : P Y} →
            (∃ λ (f : X C.≅ Y) → H p q (f C.¹) × H q p (f C.⁻¹)) ≃
            ((X , p) Str.≅ (Y , q))
    ≅HH≃≅ {X} {Y} {p} {q} = ↔⇒≃ (record
      { surjection = record
        { logical-equivalence = record
          { to   = ≅HH≃≅.to
          ; from = from
          }
        ; right-inverse-of = to∘from
        }
      ; left-inverse-of = from∘to
      })
      where
      from : (X , p) Str.≅ (Y , q) →
             ∃ λ (f : X C.≅ Y) → H p q (f C.¹) × H q p (f C.⁻¹)
      from ((f , Hf) , (f⁻¹ , Hf⁻¹) , f∙f⁻¹ , f⁻¹∙f) =
        (f , f⁻¹ , cong proj₁ f∙f⁻¹ , cong proj₁ f⁻¹∙f) , Hf , Hf⁻¹

      to∘from : ∀ p → ≅HH≃≅.to (from p) ≡ p
      to∘from ((f , Hf) , (f⁻¹ , Hf⁻¹) , f∙f⁻¹ , f⁻¹∙f) =
        cong₂ (λ f∙f⁻¹ f⁻¹∙f →
                 (f , Hf) , (f⁻¹ , Hf⁻¹) , f∙f⁻¹ , f⁻¹∙f)
              (Str.Hom-is-set _ _)
              (Str.Hom-is-set _ _)

      from∘to : ∀ p → from (≅HH≃≅.to p) ≡ p
      from∘to ((f , f⁻¹ , f∙f⁻¹ , f⁻¹∙f) , Hf , Hf⁻¹) =
        cong₂ (λ f∙f⁻¹ f⁻¹∙f → (f , f⁻¹ , f∙f⁻¹ , f⁻¹∙f) , Hf , Hf⁻¹)
              (C.Hom-is-set _ _)
              (C.Hom-is-set _ _)

    module ≡≡≃≅HH where

      to : ∀ {X Y} {p : P X} {q : P Y} →
           (X≡Y : X ≡ Y) → subst P X≡Y p ≡ q →
           H p q (C.≡→≅ X≡Y C.¹) × H q p (C.≡→≅ X≡Y C.⁻¹)
      to {X} {p = p} X≡Y p≡q = elim¹
        (λ X≡Y → ∀ {q} → subst P X≡Y p ≡ q →
                 H p q (C.≡→≅ X≡Y C.¹) × H q p (C.≡→≅ X≡Y C.⁻¹))
        (elim¹
           (λ {q} _ → H p q (C.≡→≅ (refl X) C.¹) ×
                      H q p (C.≡→≅ (refl X) C.⁻¹))
           ( subst (λ { (q , f) → H p q f })
                   (sym $ cong₂ _,_
                            (subst P (refl X) p  ≡⟨ subst-refl P _ ⟩∎
                             p                   ∎)
                            (C.≡→≅ (refl X) C.¹  ≡⟨ cong C._¹ C.≡→≅-refl ⟩∎
                             C.id                ∎))
                   H-id
           , subst (λ { (q , f) → H q p f })
                   (sym $ cong₂ _,_
                            (subst P (refl X) p  ≡⟨ subst-refl P _ ⟩∎
                             p                   ∎)
                            (C.≡→≅ (refl X) C.⁻¹  ≡⟨ cong C._⁻¹ C.≡→≅-refl ⟩∎
                             C.id                 ∎))
                   H-id
           ))
        X≡Y p≡q

      to-refl : ∀ {X} {p : P X} →
                subst (λ f → H p p (f C.¹) × H p p (f C.⁻¹))
                      C.≡→≅-refl
                      (to (refl X) (subst-refl P p)) ≡
                (H-id , H-id)
      to-refl = cong₂ _,_ (H-prop _ _ _) (H-prop _ _ _)

    ≡≡≃≅HH : ∀ {X Y} {p : P X} {q : P Y} →
             (∃ λ (eq : X ≡ Y) → subst P eq p ≡ q) ≃
             (∃ λ (f : X C.≅ Y) → H p q (f C.¹) × H q p (f C.⁻¹))
    ≡≡≃≅HH {X} {p = p} {q} =
      Σ-preserves C.≡≃≅ λ X≡Y →
        _↔_.to (⇔↔≃ ext (P-set _) (×-closure 1 (H-prop _) (H-prop _)))
               (record { to = ≡≡≃≅HH.to X≡Y ; from = from X≡Y })
      where
      from : ∀ X≡Y → H p q (C.≡→≅ X≡Y C.¹) × H q p (C.≡→≅ X≡Y C.⁻¹) →
             subst P X≡Y p ≡ q
      from X≡Y (H¹ , H⁻¹) = elim¹
        (λ {Y} X≡Y → ∀ {q} →
                     H p q (C.≡→≅ X≡Y C.¹) → H q p (C.≡→≅ X≡Y C.⁻¹) →
                     subst P X≡Y p ≡ q)
        (λ {q} H¹ H⁻¹ →
           subst P (refl X) p  ≡⟨ subst-refl P _ ⟩
           p                   ≡⟨ H-antisymmetric p q
                                    (subst (H p q) (cong C._¹  C.≡→≅-refl) H¹)
                                    (subst (H q p) (cong C._⁻¹ C.≡→≅-refl) H⁻¹) ⟩∎
           q                   ∎)
        X≡Y H¹ H⁻¹

    ≡≃≅ : ∀ {X Y} {p : P X} {q : P Y} →
          _≡_ {A = ∃ P} (X , p) (Y , q) ≃ ((X , p) Str.≅ (Y , q))
    ≡≃≅ = ≅HH≃≅ ⊚ ≡≡≃≅HH ⊚ ↔⇒≃ (inverse Σ-≡,≡↔≡)

    -- …and the proof maps reflexivity to the identity morphism.

    ≡≃≅-refl : ∀ {Xp} → _≃_.to ≡≃≅ (refl Xp) Str.¹ ≡ Str.id
    ≡≃≅-refl {X , p} = cong Str._¹ (
      ≅HH≃≅.to (_≃_.to ≡≡≃≅HH (Σ-≡,≡←≡ (refl (_,_ {B = P} X p))))      ≡⟨ cong (≅HH≃≅.to ∘ _≃_.to ≡≡≃≅HH) $ Σ-≡,≡←≡-refl {B = P} ⟩
      ≅HH≃≅.to (_≃_.to ≡≡≃≅HH (refl X , subst-refl P p))               ≡⟨⟩
      ≅HH≃≅.to (C.≡→≅ (refl X) , ≡≡≃≅HH.to (refl X) (subst-refl P p))  ≡⟨ cong ≅HH≃≅.to $ Σ-≡,≡→≡ C.≡→≅-refl ≡≡≃≅HH.to-refl ⟩
      ≅HH≃≅.to (C.id≅ , H-id , H-id)                                   ≡⟨ refl _ ⟩∎
      Str.id≅                                                          ∎)


open import Categories
open import Functors
open import RMonads

module RMonads.CatofRAdj.InitRAdj {a b c d}{C : Cat {a}{b}}{D : Cat {c}{d}}
                                  {J : Fun C D}(M : RMonad J) where

open import Library
open import Naturals hiding (Iso; module Iso)
open import RAdjunctions
open import RMonads.CatofRAdj M
open import Categories.Initial
open import RMonads.RKleisli M
open import RMonads.RKleisli.Functors M
open import RMonads.RKleisli.Adjunction M
open import RAdjunctions.RAdj2RMon

open Cat
open Fun
open RMonad M
open NatT
open RAdj

lemX : R KlAdj ○ L KlAdj ≅ TFun M
lemX = FunctorEq _ _ refl refl

KlObj : Obj CatofAdj
KlObj = record {
  E   = Kl;
  adj = KlAdj;
  law = lemX;
  ηlaw = refl;
  bindlaw = λ{X}{Y}{f} →
    cong bind (stripsubst (Hom D (OMap J X))
                          f
                          (fcong Y (cong OMap (sym lemX))))}
  where
        open Cat

open ObjAdj
open import Isomorphism
open Iso

lemZ : {F G G' : Fun C D} → G ≅ G' → {α : NatT F G}{β : NatT F G'} →
       α ≅ β →
       ∀{X} → cmp α {X} ≅ cmp β {X}
lemZ refl refl = refl

lemZ' : ∀{a b c d}{C : Cat {a}{b}}{D : Cat {c}{d}}{F F' G G' : Fun C D} →
        F ≅ F' → G ≅ G' →
        {α : NatT F G}{β : NatT F' G'} → α ≅ β →
        ∀{X} → cmp α {X} ≅ cmp β {X}
lemZ' refl refl refl = refl

lemfid : ∀{e f}{E : Cat {e}{f}}(L : Fun C E)(R : Fun E D)
  (T : Fun C D)
  (p : T ≅ R ○ L)
  (η : ∀ {X} → Hom D (OMap J X) (OMap T X))
  (right : {X : Obj C}{Y : Obj E} →
           Hom D (OMap J X) (OMap R Y) → Hom E (OMap L X) Y) →
  (left : {X : Obj C}{Y : Obj E} →
          Hom E (OMap L X) Y → Hom D (OMap J X) (OMap R Y)) →
  (q : {X : Obj C}{Y : Obj E}(f : Hom E (OMap L X) Y) → right (left f) ≅ f) →
  (r : {X : Obj C} → left (iden E {OMap L X}) ≅ η {X}) →
  {X : Obj C} →
  right (subst (Hom D (OMap J X)) (fcong X (cong OMap p)) η)
  ≅
  iden E {OMap L X}
lemfid {E = E} L R .(R ○ L) refl η right left q r {X} =
  trans (cong right (sym r)) (q (iden E))

lemfcomp : ∀{e f}{E : Cat {e}{f}}(L : Fun C E)(R : Fun E D)(T : Fun C D)
  (p : T ≅ R ○ L)
  (bind : ∀{X Y} → Hom D (OMap J X) (OMap T Y) → Hom D (OMap T X) (OMap T Y)) →
  (right : {X : Obj C} {Y : Obj E} →
           Hom D (OMap J X) (OMap R Y) → Hom E (OMap L X) Y) →
  (q : {X Y : Obj C} {f : Hom D (OMap J X) (OMap T Y)} →
       HMap R (right (subst (Hom D (OMap J X)) (fcong Y (cong OMap p)) f))
       ≅
       bind f) →
  (r : {X X' : Obj C}{Y Y' : Obj E}
       (f : Hom C X' X)(g : Hom E Y Y')
       (h : Hom D (OMap J X) (OMap R Y)) →
       right (comp D (HMap R g) (comp D h (HMap J f)))
       ≅
       comp E g (comp E (right h) (HMap L f))) →
  ∀{X Y Z : Obj C}
  {f : Hom D (OMap J Y) (OMap T Z)}
  {g : Hom D (OMap J X) (OMap T Y)} →
  right (subst (Hom D (OMap J X)) (fcong Z (cong OMap p)) (comp D (bind f) g))
  ≅
  comp E (right (subst (Hom D (OMap J Y)) (fcong Z (cong OMap p)) f))
         (right (subst (Hom D (OMap J X)) (fcong Y (cong OMap p)) g))
lemfcomp {E = E} L R .(R ○ L) refl bind right q r {X}{Y}{Z}{f}{g} =
  trans (cong₂ (λ f g → right (comp D f g)) (sym (q {f = f})) (sym (idr D)))
        (trans (cong (λ h → right (comp D (HMap R (right f)) (comp D g h)))
                     (sym (fid J)))
               (trans (r (iden C) (right f) g)
                      (cong (comp E (right f))
                            (trans (cong (comp E (right g)) (fid L))
                                   (idr E)))))

lemLlaw : ∀{e f}{E : Cat {e}{f}}(J : Fun C D)(L : Fun C E)(R : Fun E D)(T : Fun C D)
          (p : T ≅ R ○ L)
          (η : ∀ {X} → Hom D (OMap J X) (OMap T X)) →
          (right : {X : Obj C} {Y : Obj E} →
                   Hom D (OMap J X) (OMap R Y) → Hom E (OMap L X) Y) →
          (left : {X : Obj C} {Y : Obj E} →
                  Hom E (OMap L X) Y → Hom D (OMap J X)  (OMap R Y)) →
          (q : {X : Obj C} {Y : Obj E} (f : Hom E (OMap L X) Y) →
               right (left f) ≅ f) →
          (r : {X : Obj C} → left (iden E {OMap L X}) ≅ η {X}) →
          (t : {X X' : Obj C}{Y Y' : Obj E}
                   (f : Hom C X' X)(g : Hom E Y Y')
                   (h : Hom D (OMap J X) (OMap R Y)) →
                   right (comp D (HMap R g) (comp D h (HMap J f)))
                   ≅
                   comp E g (comp E (right h) (HMap L f)) ) →
          {X Y : Obj C} (f : Hom C X Y) →
          right (subst (Hom D (OMap J X))
                       (fcong Y (cong OMap p))
                       (comp D η (HMap J f)))
          ≅
          HMap L f
lemLlaw {E = E} J L R .(R ○ L) refl η right left q r t {X}{Y} f =
  trans (cong (λ g → right (comp D g (HMap J f))) (sym r))
        (trans (cong right
                     (trans (sym (idl D))
                            (cong (λ g → comp D g (comp D (left (iden E))
                                                          (HMap J f)))
                                  (sym (fid R)))))
               (trans (trans (trans (t f (iden E) (left (iden E)))
                                    (idl E))
                             (cong (λ g → comp E g (HMap L f)) (q (iden E))))
                      (idl E)))

K' : ∀{e}{f}(A : Obj (CatofAdj {e}{f})) → Fun (E KlObj) (E A)
K' A = record {
    OMap  = OMap (L (adj A));
    HMap  = λ{X}{Y} f →
      right (adj A) (subst (Hom D (OMap J X))
                           (fcong Y (cong OMap (sym (ObjAdj.law A))))
                           f);
    fid   = lemfid
                   (L (adj A))
                   (R (adj A))
                   (TFun M)
                   (sym (law A))
                   η
                   (right (adj A))
                   (left (adj A))
                   (lawa (adj A))
                   (ObjAdj.ηlaw A);
    fcomp = lemfcomp
                     (L (adj A))
                     (R (adj A))
                     (TFun M)
                     (sym (law A))
                     bind
                     (right (adj A))
                     (bindlaw A)
                     (natright (adj A))}

Llaw' : ∀{e}{f}(A : Obj (CatofAdj {e}{f})) → K' A ○ L (adj KlObj) ≅ L (adj A)
Llaw' A = FunctorEq
   _
   _
   refl
   (iext λ _ → iext λ _ → ext $ lemLlaw J
     (L (adj A))
     (R (adj A))
     (TFun M)
     (sym (law A))
     η
     (right (adj A))
     (left (adj A))
     (lawa (adj A))
     (ηlaw A)
     (natright (adj A)))

Rlaw' : ∀{e}{f}(A : Obj (CatofAdj {e}{f})) → R (adj KlObj) ≅ R (adj A) ○ K' A
Rlaw' A = FunctorEq _ _ (cong OMap (sym (law A)))
                        (iext λ _ → iext λ _ → ext λ _ → sym (bindlaw A))

rightlaw' : ∀{e f}(A : Obj (CatofAdj {e}{f})) →
            {X : Obj C} {Y : Obj (E KlObj)}
            {f : Hom D (OMap J X) (OMap (R (adj KlObj)) Y)} →
            HMap (K' A) (right (adj KlObj) f) ≅
            right (adj A) (subst (Hom D (OMap J X)) (fcong Y (cong OMap (Rlaw' A))) f)
rightlaw' A {X}{Y}{f} = cong (right (adj A)) (trans (stripsubst (Hom D (OMap J X)) f (fcong Y (cong OMap (sym (law A))))) (sym (stripsubst (Hom D (OMap J X)) f (fcong Y (cong OMap (Rlaw' A))))) )

KlHom : {A : Obj CatofAdj} → Hom CatofAdj KlObj A
KlHom {A = A} = record {
  K        = K' A;
  Llaw     = Llaw' A;
  Rlaw     = Rlaw' A;
  rightlaw = rightlaw' A}

KlIsInit : Init CatofAdj KlObj
KlIsInit = record {
  i   = KlHom;
  law = λ {X} {V} → HomAdjEq
    _
    _
    ((FunctorEq _ _ (cong OMap (sym (HomAdj.Llaw V)))
             (iext λ A → iext λ B → ext λ f →
                trans
                (cong₂ (λ B₁ → right (adj X) {A} {B₁})
                 (sym (fcong B (cong OMap (HomAdj.Llaw V))))
                 (trans
                    (stripsubst (Hom D (OMap J A)) f
                     (fcong B (cong OMap (sym (law X)))))
                    (sym
                     (stripsubst (Hom D (OMap J A)) f
                      (fcong B (cong OMap (HomAdj.Rlaw V)))))))
                (sym (HomAdj.rightlaw V)))))}
-- -}


{-# OPTIONS --allow-unsolved-metas #-}
open import Data.Nat -- using (ℕ; suc; zero; s≤s ; z≤n )
module FLComm (n : ℕ) where

open import Level renaming ( suc to Suc ; zero to Zero ) hiding (lift)
open import Data.Fin hiding ( _<_  ; _≤_ ; _-_ ; _+_ ; _≟_)
open import Data.Fin.Properties hiding ( <-trans ; ≤-refl ; ≤-trans ; ≤-irrelevant ; _≟_ ) renaming ( <-cmp to <-fcmp )
open import Data.Fin.Permutation
open import Data.Nat.Properties
open import Relation.Binary.PropositionalEquality hiding ( [_] ) renaming ( sym to ≡-sym )
open import Data.List using (List; []; _∷_ ; length ; _++_ ; tail ) renaming (reverse to rev )
open import Data.Product hiding (_,_ )
open import Relation.Nullary
open import Data.Unit hiding (_≤_)
open import Data.Empty
open import  Relation.Binary.Core
open import  Relation.Binary.Definitions hiding (Symmetric )
open import logic
open import nat

open import FLutil
open import Putil
import Solvable
open import Symmetric

-- infixr  100 _::_

open import Relation.Nary using (⌊_⌋)
open import Data.List.Fresh hiding ([_])
open import Data.List.Fresh.Relation.Unary.Any

open import Algebra
open Group (Symmetric n) hiding (refl)
open Solvable (Symmetric n)
open _∧_
-- open import Relation.Nary using (⌊_⌋)
open import Relation.Nullary.Decidable hiding (⌊_⌋)

open import fin

-- all cobmbination in P and Q (could be more general)
record AnyComm {n m l : ℕ}  (P : FList n) (Q : FList m) (fpq : (p : FL n) (q : FL m) → FL l) : Set where
   field
     commList : FList l
     commAny : (p : FL n) (q : FL m)
         → Any ( p ≡_ ) P →  Any ( q ≡_ ) Q
         → Any (fpq p q ≡_) commList

-------------
--    (p,q)   (p,qn) ....           (p,q0)
--    pn,q
--     :        AnyComm FL0 FL0 P  Q
--    p0,q

open AnyComm
anyComm : {n m l : ℕ } → (P : FList n) (Q : FList m) → (fpq : (p : FL n) (q : FL m) → FL l)  → AnyComm P Q fpq
anyComm [] [] _ = record { commList = [] ; commAny = λ _ _ () }
anyComm [] (cons q Q qr) _ = record { commList = [] ; commAny = λ _ _ () }
anyComm (cons p P pr) [] _ = record { commList = [] ; commAny = λ _ _ _ () }
anyComm {n} {m} {l} (cons p P pr) (cons q Q qr) fpq = record { commList = FLinsert (fpq p q) (commListQ Q)  ; commAny = anyc0n } where
  commListP : (P1 : FList n) → FList l
  commListP [] = commList (anyComm P Q fpq)
  commListP (cons p₁ P1 x) =  FLinsert (fpq p₁ q) (commListP P1)
  commListQ : (Q1 : FList m) → FList l
  commListQ [] = commListP P
  commListQ (cons q₁ Q1 qr₁) = FLinsert (fpq p q₁) (commListQ Q1)
  anyc0n : (p₁ : FL n) (q₁ : FL m) → Any (_≡_ p₁) (cons p P pr) → Any (_≡_ q₁) (cons q Q qr)
    → Any (_≡_ (fpq p₁ q₁)) (FLinsert (fpq p q) (commListQ Q))
  anyc0n p₁ q₁ (here refl) (here refl) = x∈FLins _ (commListQ Q )
  anyc0n p₁ q₁ (here refl) (there anyq) = insAny (commListQ Q) (anyc01 Q anyq) where
     anyc01 : (Q1 : FList m) → Any (_≡_ q₁) Q1 → Any (_≡_ (fpq p₁ q₁)) (commListQ Q1)
     anyc01 (cons q Q1 qr₂) (here refl) = x∈FLins _ _
     anyc01 (cons q₂ Q1 qr₂) (there any) = insAny _ (anyc01 Q1 any)
  anyc0n p₁ q₁ (there anyp) (here refl) = insAny _ (anyc02 Q) where
     anyc03 : (P1 : FList n) → Any (_≡_ p₁) P1  → Any (_≡_ (fpq p₁ q₁)) (commListP P1)
     anyc03 (cons a P1 x) (here refl) = x∈FLins _ _
     anyc03 (cons a P1 x) (there any) = insAny _ ( anyc03 P1 any)
     anyc02 : (Q1 : FList m) → Any (_≡_ (fpq p₁ q₁)) (commListQ Q1)
     anyc02 [] = anyc03 P anyp
     anyc02 (cons a Q1 x) = insAny _ (anyc02 Q1)
  anyc0n p₁ q₁ (there anyp) (there anyq) = insAny (commListQ Q) (anyc04 Q) where
     anyc05 : (P1 : FList n) → Any (_≡_ (fpq p₁ q₁)) (commListP P1)
     anyc05 [] = commAny (anyComm P Q fpq) p₁ q₁ anyp anyq
     anyc05 (cons a P1 x) = insAny _ (anyc05 P1)
     anyc04 : (Q1 : FList m) → Any (_≡_ (fpq p₁ q₁)) (commListQ Q1)
     anyc04 [] = anyc05 P
     anyc04 (cons a Q1 x) = insAny _ (anyc04 Q1)

-------------
--    # 0 :: # 0 :: # 0 : # 0 :: f0
--    # 0 :: # 0 :: # 1 : # 0 :: f0
--    # 0 :: # 1 :: # 0 : # 0 :: f0
--    # 0 :: # 1 :: # 1 : # 0 :: f0
--    # 0 :: # 2 :: # 0 : # 0 :: f0
--       ...
--    # 3 :: # 2 :: # 0 : # 0 :: f0
--    # 3 :: # 2 :: # 1 : # 0 :: f0

-- all FL
record AnyFL (n : ℕ) : Set where
   field
     allFL : FList n
     anyP : (x : FL n) → Any (x ≡_ ) allFL

open AnyFL

--   all FL as all combination
--      anyComm ( #0 :: FL0 ... # n :: FL0 ) (all n) (λ p q → FLpos p :: q ) = all (suc n)

anyFL01 :  (n : ℕ) → AnyFL (suc n)
anyFL01 zero    = record { allFL = (zero :: f0) ∷# [] ; anyP = λ x → anyFL2 x ((zero :: f0) ∷# []) refl }  where
     anyFL2 : (x : FL 1) → (y : FList 1) → y ≡ ((zero :: f0) ∷# []) → Any (_≡_ x) y
     anyFL2 (zero :: f0) .(cons (zero :: f0) [] (Level.lift tt)) refl = here refl
anyFL01 (suc n) = record { allFL = commList anyC ;  anyP =  anyFL02 } where
     anyFL05 : {n i : ℕ} → (i < suc n) → FList (suc n)
     anyFL05 {_} {0} (s≤s z≤n) = zero :: FL0 ∷# []
     anyFL05 {n} {suc i} (s≤s i<n) = FLinsert (fromℕ< (s≤s i<n) :: FL0) (anyFL05 {n} {i} (<-trans i<n a<sa))
     anyFL08 : {n i : ℕ} {x : Fin (suc n)} {i<n : suc i < suc n}  → toℕ x ≡ suc i → x ≡ suc (fromℕ< (≤-pred i<n))
     anyFL08 {n} {i} {x} {i<n} eq = toℕ-injective ( begin
                toℕ x                               ≡⟨ eq ⟩
                suc i                               ≡⟨ cong suc (≡-sym (toℕ-fromℕ< _ )) ⟩
                suc (toℕ (fromℕ< (≤-pred i<n)) )
          ∎ ) where open ≡-Reasoning
     anyFL06 : {n i : ℕ} → (i<n : i < suc n) → (x : Fin (suc n)) → toℕ x < suc i → Any (_≡_ (x :: FL0)) (anyFL05 i<n)
     anyFL06 (s≤s z≤n) zero (s≤s lt) = here refl
     anyFL06 {n} {suc i} (s≤s (s≤s i<n)) x (s≤s lt) with <-cmp (toℕ x) (suc i)
     ... | tri< a ¬b ¬c = insAny _ (anyFL06 (<-trans (s≤s i<n) a<sa) x a)
     ... | tri≈ ¬a b ¬c = subst (λ k →  Any (_≡_ (x :: FL0)) (FLinsert (k :: FL0) (anyFL05 {n} {i} (<-trans (s≤s i<n) a<sa))))
                  (anyFL08 {n} {i} {x} {s≤s (s≤s i<n)} b) (x∈FLins (x :: FL0)  (anyFL05 {n} {i} (<-trans (s≤s i<n) a<sa)))
     ... | tri> ¬a ¬b c = ⊥-elim ( nat-≤> c (s≤s lt) )
     anyC = anyComm (anyFL05 a<sa) (allFL (anyFL01 n)) (λ p q → FLpos p :: q )
     anyFL02 : (x : FL (suc (suc n))) → Any (_≡_ x) (commList anyC)
     anyFL02 (x :: y) = commAny anyC (x :: FL0) y
                       (subst (λ k → Any (_≡_ (k :: FL0) ) _) (fromℕ<-toℕ _ _) (anyFL06 a<sa (fromℕ< x≤n) fin<n) ) (anyP (anyFL01 n) y) where
         x≤n : suc (toℕ x)  ≤ suc (suc n)
         x≤n = toℕ<n x

anyFL :  (n : ℕ) → AnyFL n
anyFL zero = record { allFL = f0 ∷# [] ; anyP = anyFL4 } where
    anyFL4 : (x : FL zero) → Any (_≡_ x) ( f0 ∷# [] )
    anyFL4 f0 = here refl
anyFL (suc n) = anyFL01 n

at1 = proj₁ (toList (allFL (anyFL 1)))
at2 = proj₁ (toList (allFL (anyFL 2)))
at3 = proj₁ (toList (allFL (anyFL 3)))
at4 = proj₁ (toList (allFL (anyFL 4)))

CommFListN  : ℕ →  FList n
CommFListN  zero = allFL (anyFL n)
CommFListN (suc i ) = commList (anyComm ( CommFListN i ) ( CommFListN i ) (λ p q →  perm→FL [ FL→perm p , FL→perm q ] ))

CommStage→ : (i : ℕ) → (x : Permutation n n ) → deriving i x → Any (perm→FL x ≡_) (CommFListN i)
CommStage→ zero x (Level.lift tt) = anyP (anyFL n) (perm→FL x)
CommStage→ (suc i) .( [ g , h ] ) (comm {g} {h} p q) = comm2 where
  G = perm→FL g
  H = perm→FL h
  comm3 :  perm→FL [ FL→perm G , FL→perm H ] ≡ perm→FL [ g , h ]
  comm3 = begin
              perm→FL [ FL→perm G , FL→perm H ]
           ≡⟨ pcong-pF (comm-resp (FL←iso _) (FL←iso _)) ⟩
              perm→FL [ g , h ]
          ∎  where open ≡-Reasoning
  comm2 : Any (_≡_ (perm→FL [ g , h ])) (CommFListN (suc i))
  comm2 = subst (λ k → Any (_≡_ k) (CommFListN (suc i)) ) comm3
     ( commAny ( anyComm (CommFListN i) (CommFListN i) (λ p q →  perm→FL [ FL→perm p , FL→perm q ] )) G H (CommStage→ i g p) (CommStage→ i h q) )
CommStage→ (suc i) x (ccong {f} {x} eq p) =
      subst (λ k → Any (k ≡_) (commList (anyComm ( CommFListN i ) ( CommFListN i ) (λ p q →  perm→FL [ FL→perm p , FL→perm q ] ))))
          (comm4 eq) (CommStage→ (suc i) f p ) where
   comm4 : f =p= x →  perm→FL f ≡ perm→FL x
   comm4 = pcong-pF

CommSolved : (x : Permutation n n) → (y : FList n) → y ≡ FL0 ∷# [] → (FL→perm (FL0 {n}) =p= pid ) → Any (perm→FL x ≡_) y → x =p= pid
CommSolved x .(cons FL0 [] (Level.lift tt)) refl eq0 (here eq) = FLpid _ eq eq0


-- Andreas, 2016-08-08, issue #2132 reported by effectfully
-- Pattern synonyms in lhss of display form definitions

-- {-# OPTIONS -v scope:50 -v tc.decl:10 #-}

open import Common.Equality

data D : Set where
  C c : D
  g : D → D

pattern C′ = C

{-# DISPLAY C′ = C′ #-}
{-# DISPLAY g C′ = c #-}

-- Since pattern synonyms are now expanded on lhs of DISPLAY,
-- this behaves as
-- {-# DISPLAY C = C′ #-}
-- {-# DISPLAY g C = c #-}

test : C ≡ g C
test = refl

-- Expected error:
-- C′ != c of type D
-- when checking that the expression refl has type C′ ≡ c


open import Data.Bool using (Bool; true; false)
open import Data.Float using (Float)
open import Avionics.Maybe using (Maybe; just; nothing; _>>=_; map)
--open import Data.Maybe using (Maybe; just; nothing; _>>=_; map)
open import Data.Nat using (ℕ)
open import Function using (_∘_)
open import Level using (0ℓ; _⊔_) renaming (suc to lsuc)
open import Relation.Binary using (Decidable)
open import Relation.Binary.Definitions using (Transitive; Trans)
open import Relation.Binary.PropositionalEquality using (_≡_)
open import Relation.Nullary using (Dec; yes; no)
open import Relation.Nullary.Decidable using (False; ⌊_⌋; True)
open import Relation.Unary using (Pred; _∈_)

-- stack exec -- agda -c --ghc-dont-call-ghc --no-main code.agda

infix  4 _≤_ _≤ᵇ_ _≤?_

postulate
  ℝ : Set

  fromFloat : Float → Maybe ℝ
  toFloat : ℝ → Float

  0ℝ : ℝ

  _≤_ : ℝ → ℝ → Set
  _≤?_ : (m n : ℝ) → Dec (m ≤ n)

_≤ᵇ_ : ℝ → ℝ → Bool
p ≤ᵇ q = ⌊ p ≤? q ⌋

postulate
  √_ : (x : ℝ) → .{0≤x : True (0ℝ ≤? x)} → ℝ

--Subset : Set → Set _
--Subset A = Pred A 0ℓ
--
--postulate
--  [0,∞⟩ : Subset ℝ
--
--  √_ : (x : ℝ) → .{0≤x : x ∈ [0,∞⟩} → ℝ

{-# COMPILE GHC ℝ = type Double #-}

-- `fromFloat` should check whether it is a valid floating point
-- number
{-# COMPILE GHC fromFloat = \x -> Just x #-}
{-# COMPILE GHC toFloat = \x -> x #-}

{-# COMPILE GHC 0ℝ = 0 #-}

{-# FOREIGN GHC type UnitErase a b = () #-}
{-# COMPILE GHC _≤_ = type UnitErase #-}
--{-# COMPILE GHC _≤_ = \ _ _ -> () #-}
{-# COMPILE GHC _≤ᵇ_ = (<=) #-}

--{-# FOREIGN GHC type UnitErase erase = () #-}
--{-# COMPILE GHC [0,∞⟩ = type UnitErase #-}

-- TODO: Find out how to perform the `sqrt` of a value!!
--       Here lies the main problem of compilation failure!
{-# COMPILE GHC √_  = \x _ -> sqrt x #-}
-- Agda is actually doing the right thing not accepting dubious
-- translations of code into Haskell

export√ : Float → Maybe Float
export√ x = fromFloat x >>= sqrt >>= just ∘ toFloat
  where
    sqrt : ℝ → Maybe ℝ
    sqrt y with 0ℝ ≤ᵇ y
    ... | false = nothing
    ... | true = just ((√ y) {y≥0})
      where postulate y≥0 : True (0ℝ ≤? y)
      --where postulate y≥0 : y ∈ [0,∞⟩
{-# COMPILE GHC export√ as exportSqrt #-}


module UpTo where

open import Level
open import Relation.Binary using (Rel; IsEquivalence)
open import Data.Product
open import Categories.Support.Equivalence
open import Categories.Category
open import Categories.2-Category
open import Categories.Functor
open import Categories.NaturalTransformation
  renaming (id to natId; _≡_ to _≡N_; setoid to natSetoid)
  hiding (_∘ˡ_; _∘ʳ_)
open import Categories.Support.EqReasoning

open import NaturalTransFacts

Cat₀ = Category zero zero zero

EndoFunctor : Cat₀ → Set zero
EndoFunctor C = Functor C C

record Endo⇒ (C₁ : Cat₀) (F₁ : EndoFunctor C₁)
             (C₂ : Cat₀) (F₂ : EndoFunctor C₂)
             : Set zero where
  field
    T : Functor C₁ C₂
    ρ : NaturalTransformation (T ∘ F₁) (F₂ ∘ T)

record UpTo⇒ {C₁ : Cat₀} {F : EndoFunctor C₁}
             {C₂ : Cat₀} {G : EndoFunctor C₂}
             (S₁ S₂ : Endo⇒ C₁ F C₂ G)
             : Set zero where
  module S₁ = Endo⇒ S₁
  module S₂ = Endo⇒ S₂
  field
    γ : NaturalTransformation S₁.T S₂.T
    -- The following diagram must commute
    -- T₁F - ρ₁ -> GT₁
    --  |            |
    --  γF         Gγ
    --  |            |
    --  v            v
    -- T₂G - ρ₂ -> GT₂
    .square : S₂.ρ ∘₁ (γ ∘ʳ F) ≡N (G ∘ˡ γ) ∘₁ S₁.ρ

record _≡U_ {C₁ : Cat₀} {C₂ : Cat₀}
            {F : EndoFunctor C₁} {G : EndoFunctor C₂}
            {T₁ T₂ : Endo⇒ C₁ F C₂ G}
            (A : UpTo⇒ T₁ T₂) (B : UpTo⇒ T₁ T₂) : Set where
  field
    ≡U-proof : UpTo⇒.γ A ≡N UpTo⇒.γ B

open _≡U_
infix 4 _≡U_

.≡U-equiv : {C₁ : Cat₀} {C₂ : Cat₀}
            {F : EndoFunctor C₁} {G : EndoFunctor C₂} →
            {A B : Endo⇒ C₁ F C₂ G} →
            IsEquivalence {A = UpTo⇒ A B} (_≡U_ {C₁} {C₂} {F} {G})
≡U-equiv =
  record
  { refl = λ {A} → record { ≡U-proof = Setoid.refl natSetoid {UpTo⇒.γ A} }
  ; sym = λ {A} {B} p → record {
        ≡U-proof = Setoid.sym natSetoid {UpTo⇒.γ A} {UpTo⇒.γ B} (≡U-proof p) }
  ; trans = λ {A} {B} {C} p₁ p₂ → record {
          ≡U-proof = Setoid.trans natSetoid {UpTo⇒.γ A} {UpTo⇒.γ B} {UpTo⇒.γ C}
                     (≡U-proof p₁) (≡U-proof p₂) }
  }

id-UpTo⇒ : {C₁ : Cat₀} {F : EndoFunctor C₁}
           {C₂ : Cat₀} {G : EndoFunctor C₂}
           {A : Endo⇒ C₁ F C₂ G} → UpTo⇒ A A
id-UpTo⇒ {C₁} {F} {C₂} {G} {A} =
  record
      { γ = natId
      ; square =
        begin
          Endo⇒.ρ A ∘₁ (natId {F = Endo⇒.T A} ∘ʳ F)
        ↓⟨ ∘₁-resp-≡
           {f = Endo⇒.ρ A} {h = Endo⇒.ρ A}
           {g = natId {F = Endo⇒.T A} ∘ʳ F}
           {i = natId {F = Endo⇒.T A ∘ F}}
           (Setoid.refl natSetoid {Endo⇒.ρ A})
           (identityNatʳ {F = Endo⇒.T A} F)
         ⟩
          Endo⇒.ρ A ∘₁ (natId {F = Endo⇒.T A ∘ F})
        ↓⟨ identity₁ʳ {X = Endo⇒.ρ A} ⟩
          Endo⇒.ρ A
        ↑⟨ identity₁ˡ {X = Endo⇒.ρ A} ⟩
          natId {F = G ∘ Endo⇒.T A} ∘₁ Endo⇒.ρ A
        ↑⟨ ∘₁-resp-≡
           {f = G ∘ˡ natId {F = Endo⇒.T A}}
           {h = natId {F = G ∘ Endo⇒.T A}}
           {g = Endo⇒.ρ A} {i = Endo⇒.ρ A}
           (identityNatˡ {F = Endo⇒.T A} G)
           (Setoid.refl natSetoid {Endo⇒.ρ A})
         ⟩
          (G ∘ˡ natId {F = Endo⇒.T A}) ∘₁ Endo⇒.ρ A
        ∎
      }
        where
          open SetoidReasoning (natSetoid {F = Endo⇒.T A ∘ F} {G ∘ Endo⇒.T A})

_•_ : {C₁ : Cat₀} {F : EndoFunctor C₁}
             {C₂ : Cat₀} {G : EndoFunctor C₂}
             {A B C : Endo⇒ C₁ F C₂ G} →
             UpTo⇒ B C → UpTo⇒ A B → UpTo⇒ A C
_•_ {F = F} {G = G} {A = A} {B} {C} g f =
  record
  { γ = γ ∘₁ φ
  ; square =
    --     AF - A.ρ -> GA
    --     |           |
    --    φF          Gφ
    --     |           |
    --     v           v
    --    BF - B.ρ -> GB
    --     |           |
    --     γF         Gγ
    --     |           |
    --     v           v
    --    CF - C.ρ -> GC
    begin
      C.ρ ∘₁ ((γ ∘₁ φ) ∘ʳ F)
    ↓⟨ ∘₁-resp-≡ʳ
       {f = C.ρ} {(γ ∘₁ φ) ∘ʳ F} {(γ ∘ʳ F) ∘₁ (φ ∘ʳ F)}
       (∘ʳ-distr-∘₁ γ φ F)
     ⟩
      C.ρ ∘₁ ((γ ∘ʳ F) ∘₁ (φ ∘ʳ F))
    ↑⟨ assoc₁ {X = (φ ∘ʳ F)} {(γ ∘ʳ F)} {C.ρ} ⟩
      (C.ρ ∘₁ (γ ∘ʳ F)) ∘₁ (φ ∘ʳ F)
    ↓⟨ ∘₁-resp-≡ˡ
       {f = C.ρ ∘₁ (γ ∘ʳ F)} {G ∘ˡ γ ∘₁ B.ρ} {φ ∘ʳ F}
       (UpTo⇒.square g)
     ⟩
      (G ∘ˡ γ ∘₁ B.ρ) ∘₁ (φ ∘ʳ F)
    ↓⟨ assoc₁ {X = (φ ∘ʳ F)} {B.ρ} {G ∘ˡ γ} ⟩
      (G ∘ˡ γ) ∘₁ (B.ρ ∘₁ (φ ∘ʳ F))
    ↓⟨ ∘₁-resp-≡ʳ
       {f = G ∘ˡ γ} {B.ρ ∘₁ (φ ∘ʳ F)} {(G ∘ˡ φ) ∘₁ A.ρ}
       (UpTo⇒.square f)
     ⟩
      (G ∘ˡ γ) ∘₁ ((G ∘ˡ φ) ∘₁ A.ρ)
    ↑⟨ assoc₁ {X = A.ρ} {G ∘ˡ φ} {G ∘ˡ γ} ⟩
      ((G ∘ˡ γ) ∘₁ (G ∘ˡ φ)) ∘₁ A.ρ
    ↑⟨ ∘₁-resp-≡ˡ
       {f = G ∘ˡ (γ ∘₁ φ)} {(G ∘ˡ γ) ∘₁ (G ∘ˡ φ)}  {A.ρ}
       (∘ˡ-distr-∘₁ G γ φ)
     ⟩
      (G ∘ˡ (γ ∘₁ φ)) ∘₁ A.ρ
    ∎
  }
  where
    module A = Endo⇒ A
    module B = Endo⇒ B
    module C = Endo⇒ C
    open SetoidReasoning (natSetoid {F = A.T ∘ F} {G ∘ C.T})
    φ : A.T ⇒ B.T
    φ = UpTo⇒.γ f
    γ : B.T ⇒ C.T
    γ = UpTo⇒.γ g

-- | Category of morphisms between endofunctors, where the morphisms
-- are certain natural transformations (see UpTo⇒).
-- This category will be the the setting in which we can talk about
-- properties of up-to techniques.
EndoMor : Σ Cat₀ (λ C → Functor C C) →
          Σ Cat₀ (λ C → Functor C C) →
          Cat₀
EndoMor (C₁ , F) (C₂ , G) =
  record
  { Obj = Endo⇒ C₁ F C₂ G
  ; _⇒_ = UpTo⇒
  ; _≡_ = _≡U_
  ; id = id-UpTo⇒
  ; _∘_ = _•_
  ; assoc = λ {_} {_} {_} {_} {f} {g} {h} →
          record { ≡U-proof = assoc₁ {X = UpTo⇒.γ f} {UpTo⇒.γ g} {UpTo⇒.γ h} }
  ; identityˡ = λ {_} {_} {f} →
              record { ≡U-proof = identity₁ˡ {X = UpTo⇒.γ f} }
  ; identityʳ = λ {_} {_} {f} →
              record { ≡U-proof = identity₁ʳ {X = UpTo⇒.γ f} }
  ; equiv = ≡U-equiv
  ; ∘-resp-≡ = λ {_} {_} {_} {f} {h} {g} {i} f≡h g≡i → record {
             ≡U-proof = ∘₁-resp-≡ {f = UpTo⇒.γ f} {UpTo⇒.γ h}
                                  {UpTo⇒.γ g} {UpTo⇒.γ i}
                                  (≡U-proof f≡h) (≡U-proof g≡i) }
  }

-- | The 2-category of endofunctors, their morphisms and UpTo⇒ as 2-cells.
-- This is the 2-category of endofunctors defined by Lenisa, Power and Watanabe.
{-
Endo : 2-Category (suc zero) zero zero zero
Endo = record
         { Obj = Σ Cat₀ (λ C → Functor C C)
         ; _⇒_ = EndoMor
         ; id = record
                { F₀ = λ _ → record { T = id ; ρ = natId }
                ; F₁ = λ _ → id-UpTo⇒
                ; identity = IsEquivalence.refl ≡U-equiv
                ; homomorphism = λ {_} {_} {_} {_} {F} → {!!}
                ; F-resp-≡ = {!!}
                }
         ; —∘— = {!!}
         ; assoc = {!!}
         ; identityˡ = {!!}
         ; identityʳ = {!!}
         }
-}


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Connecting Parametric.Change.Term and Parametric.Change.Value.
------------------------------------------------------------------------

import Parametric.Syntax.Type as Type
import Parametric.Syntax.Term as Term
import Parametric.Denotation.Value as Value
import Parametric.Denotation.Evaluation as Evaluation
import Parametric.Change.Type as ChangeType
import Parametric.Change.Term as ChangeTerm
import Parametric.Change.Value as ChangeValue

module Parametric.Change.Evaluation
    {Base : Type.Structure}
    {Const : Term.Structure Base}
    (⟦_⟧Base : Value.Structure Base)
    (⟦_⟧Const : Evaluation.Structure Const ⟦_⟧Base)
    (ΔBase : ChangeType.Structure Base)
    (apply-base : ChangeTerm.ApplyStructure Const ΔBase)
    (diff-base : ChangeTerm.DiffStructure Const ΔBase)
    (nil-base : ChangeTerm.NilStructure Const ΔBase)
    (⟦apply-base⟧ : ChangeValue.ApplyStructure Const ⟦_⟧Base ΔBase)
    (⟦diff-base⟧ : ChangeValue.DiffStructure Const ⟦_⟧Base ΔBase)
    (⟦nil-base⟧ : ChangeValue.NilStructure Const ⟦_⟧Base ΔBase)
  where

open Type.Structure Base
open Term.Structure Base Const
open Value.Structure Base ⟦_⟧Base
open Evaluation.Structure Const ⟦_⟧Base ⟦_⟧Const
open ChangeType.Structure Base ΔBase
open ChangeTerm.Structure Const ΔBase apply-base diff-base nil-base
open ChangeValue.Structure Const ⟦_⟧Base ΔBase ⟦apply-base⟧ ⟦diff-base⟧ ⟦nil-base⟧

open import Relation.Binary.PropositionalEquality
open import Base.Denotation.Notation
open import Postulate.Extensionality

-- Extension point 1: Relating ⊕ and its value on base types
ApplyStructure : Set
ApplyStructure = ∀ ι {Γ} →
  {t : Term Γ (base ι)} {Δt : Term Γ (ΔType (base ι))} {ρ : ⟦ Γ ⟧} →
  ⟦ t ⟧ ρ ⟦⊕₍ base ι ₎⟧ ⟦ Δt ⟧ ρ ≡ ⟦ t ⊕₍ base ι ₎ Δt ⟧ ρ

-- Extension point 2: Relating ⊝ and its value on base types
DiffStructure : Set
DiffStructure = ∀ ι {Γ} →
  {s : Term Γ (base ι)} {t : Term Γ (base ι)} {ρ : ⟦ Γ ⟧} →
  ⟦ s ⟧ ρ ⟦⊝₍ base ι ₎⟧ ⟦ t ⟧ ρ ≡ ⟦ s ⊝₍ base ι ₎ t ⟧ ρ

-- Extension point 3: Relating nil-term and its value on base types
NilStructure : Set
NilStructure = ∀ ι {Γ} →
  {t : Term Γ (base ι)} {ρ : ⟦ Γ ⟧} →
  ⟦nil₍ base ι ₎⟧ (⟦ t ⟧ ρ) ≡ ⟦ onil₍ base ι ₎ t ⟧ ρ

module Structure
    (meaning-⊕-base    : ApplyStructure)
    (meaning-⊝-base    : DiffStructure)
    (meaning-onil-base : NilStructure)
  where

  -- unique names with unambiguous types
  -- to help type inference figure things out
  private
    module Disambiguation where
      infixr 9 _⋆_
      _⋆_ : Type → Context → Context
      _⋆_ = _•_

  -- We provide: Relating ⊕ and ⊝ and their values on arbitrary types.
  meaning-⊕ : ∀ {τ Γ}
    {t : Term Γ τ} {Δt : Term Γ (ΔType τ)} {ρ : ⟦ Γ ⟧} →
    ⟦ t ⟧ ρ ⟦⊕₍ τ ₎⟧ ⟦ Δt ⟧ ρ ≡ ⟦ t ⊕₍ τ ₎ Δt ⟧ ρ

  meaning-⊝ : ∀ {τ Γ}
    {s : Term Γ τ} {t : Term Γ τ} {ρ : ⟦ Γ ⟧} →
    ⟦ s ⟧ ρ ⟦⊝₍ τ ₎⟧ ⟦ t ⟧ ρ ≡ ⟦ s ⊝₍ τ ₎ t ⟧ ρ

  meaning-onil : ∀ {τ Γ}
    {t : Term Γ τ} {ρ : ⟦ Γ ⟧} →
    ⟦nil₍ τ ₎⟧ (⟦ t ⟧ ρ) ≡ ⟦ onil₍ τ ₎ t ⟧ ρ

  meaning-⊕ {base ι} {Γ} {τ} {Δt} {ρ} = meaning-⊕-base ι {Γ} {τ} {Δt} {ρ}
  meaning-⊕ {σ ⇒ τ} {Γ} {t} {Δt} {ρ} = ext (λ v →
    let
      Γ′ = σ ⋆ (σ ⇒ τ) ⋆ ΔType (σ ⇒ τ) ⋆ Γ
      ρ′ : ⟦ Γ′ ⟧
      ρ′ = v • (⟦ t ⟧ ρ) • (⟦ Δt ⟧ ρ) • ρ
      x  : Term Γ′ σ
      x  = var this
      f  : Term Γ′ (σ ⇒ τ)
      f  = var (that this)
      Δf : Term Γ′ (ΔType (σ ⇒ τ))
      Δf = var (that (that this))
      y  = app f x
      Δy = app (app Δf x) (onil x)
    in
      begin
        ⟦ t ⟧ ρ v ⟦⊕₍ τ ₎⟧ ⟦ Δt ⟧ ρ v (⟦nil₍ σ ₎⟧ v)
      ≡⟨ cong (λ hole → ⟦ t ⟧ ρ v ⟦⊕₍ τ ₎⟧ ⟦ Δt ⟧ ρ v hole)
           (meaning-onil {t = x} {ρ′}) ⟩
        ⟦ t ⟧ ρ v ⟦⊕₍ τ ₎⟧ ⟦ Δt ⟧ ρ v (⟦ onil x ⟧ ρ′)
      ≡⟨ meaning-⊕ {t = y} {Δt = Δy} {ρ′} ⟩
        ⟦ y ⊕₍ τ ₎ Δy ⟧ ρ′
      ∎)
    where
      open ≡-Reasoning
      open Disambiguation

  meaning-⊝ {base ι} {Γ} {s} {t} {ρ} = meaning-⊝-base ι {Γ} {s} {t} {ρ}
  meaning-⊝ {σ ⇒ τ} {Γ} {s} {t} {ρ} =
    ext (λ v → ext (λ Δv →
    let
      Γ′ = ΔType σ ⋆ σ ⋆ (σ ⇒ τ) ⋆ (σ ⇒ τ) ⋆ Γ
      ρ′ : ⟦ Γ′ ⟧Context
      ρ′ = Δv • v • ⟦ t ⟧Term ρ • ⟦ s ⟧Term ρ • ρ
      Δx : Term Γ′ (ΔType σ)
      Δx = var this
      x  : Term Γ′ σ
      x  = var (that this)
      f  : Term Γ′ (σ ⇒ τ)
      f  = var (that (that this))
      g  : Term Γ′ (σ ⇒ τ)
      g  = var (that (that (that this)))
      y  = app f x
      y′ = app g (x ⊕₍ σ ₎ Δx)
    in
      begin
        ⟦ s ⟧ ρ (v ⟦⊕₍ σ ₎⟧ Δv) ⟦⊝₍ τ ₎⟧ ⟦ t ⟧ ρ v
      ≡⟨ cong (λ hole → ⟦ s ⟧ ρ hole ⟦⊝₍ τ ₎⟧ ⟦ t ⟧ ρ v)
           (meaning-⊕ {t = x} {Δt = Δx} {ρ′}) ⟩
        ⟦ s ⟧ ρ (⟦ x ⊕₍ σ ₎ Δx ⟧ ρ′) ⟦⊝₍ τ ₎⟧ ⟦ t ⟧ ρ v
      ≡⟨ meaning-⊝ {s = y′} {y} {ρ′} ⟩
        ⟦ y′ ⊝ y ⟧ ρ′
      ∎))
    where
      open ≡-Reasoning
      open Disambiguation

  meaning-onil {base ι} {Γ} {t} {ρ} = meaning-onil-base ι {Γ} {t} {ρ}
  meaning-onil {σ ⇒ τ} {Γ} {t} {ρ} = meaning-⊝ {σ ⇒ τ} {Γ} {t} {t} {ρ}
  -- Ideally, this proof should simply be:
  -- meaning-⊝ {σ ⇒ τ} {Γ} {t} {t} {ρ}
  --
  -- However, the types of the results don't match because using onil constructs
  -- different environments.


open import Oscar.Prelude

module Oscar.Class.Injectivity where

module _ where

  module _ -- Arity=1
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} (𝔒₂ : 𝔒₁ → Ø 𝔬₂)
    where
    𝓲njection₁ = (x : 𝔒₁) → 𝔒₂ x
    record 𝓘njection₁ : Ø 𝔬₁ ∙̂ 𝔬₂ where field injection₁ : 𝓲njection₁
    open 𝓘njection₁ ⦃ … ⦄ public
    module _
      {ℓ₂} (_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂)
      {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
      where
      record [𝓘njectivity₁] : Ø₀ where
        no-eta-equality
        constructor ∁
      module _
        ⦃ _ : 𝓘njection₁ ⦄
        where
        𝓲njectivity₁ = ∀ {x₁ x₂} → injection₁ x₁ ∼₂ injection₁ x₂ → x₁ ∼₁ x₂
        record 𝓘njectivity₁ ⦃ _ : [𝓘njectivity₁] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ ℓ₁ ∙̂ ℓ₂ where field injectivity₁ : 𝓲njectivity₁
        open 𝓘njectivity₁ ⦃ … ⦄ public

  injectivity₁[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {ℓ₂} {_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂}
    {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
    ⦃ _ : 𝓘njection₁ (λ (x : 𝔒₁) → 𝔒₂ x) ⦄
    ⦃ _ : [𝓘njectivity₁] (λ (x : 𝔒₁) → 𝔒₂ x) _∼₂_ _∼₁_ ⦄
    ⦃ _ : 𝓘njectivity₁ (λ (x : 𝔒₁) → 𝔒₂ x) _∼₂_ _∼₁_ ⦄
    → 𝓲njectivity₁ (λ (x : 𝔒₁) → 𝔒₂ x) _∼₂_ _∼₁_
  injectivity₁[ _ ] = injectivity₁

  module _ -- Arity=2
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} (𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃)
    where
    𝓲njection₂ = ∀ x₁ → (x₂ : 𝔒₂ x₁) → 𝔒₃ _ x₂
    record 𝓘njection₂ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ where
      constructor ∁
      field injection₂ : 𝓲njection₂
    open 𝓘njection₂ ⦃ … ⦄ public
    module _ -- Fixed=0
      {ℓ₃} (_∼₃_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} → 𝔒₃ _ y₁ → 𝔒₃ _ y₂ → Ø ℓ₃)
      where
      module _ -- Var=1
        {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
        where
        record [𝓘njectivity₂,₀,₁] : Ø₀ where
          no-eta-equality
          constructor ∁
        module _
          ⦃ _ : 𝓘njection₂ ⦄
          where
          𝓲njectivity₂,₀,₁ = ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} → injection₂ _ y₁ ∼₃ injection₂ _ y₂ → x₁ ∼₁ x₂
          record 𝓘njectivity₂,₀,₁ ⦃ _ : [𝓘njectivity₂,₀,₁] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ ℓ₁ ∙̂ ℓ₃ where field injectivity₂,₀,₁ : 𝓲njectivity₂,₀,₁
          open 𝓘njectivity₂,₀,₁ ⦃ … ⦄ public
      module _ -- Var=2
        {ℓ₂} (_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂)
        where
        record [𝓘njectivity₂,₀,₂] : Ø₀ where
          no-eta-equality
          constructor ∁
        module _
          ⦃ _ : 𝓘njection₂ ⦄
          where
          𝓲njectivity₂,₀,₂ = ∀ {x₁ x₂ : 𝔒₁} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} → injection₂ _ y₁ ∼₃ injection₂ _ y₂ → y₁ ∼₂ y₂
          record 𝓘njectivity₂,₀,₂ ⦃ _ : [𝓘njectivity₂,₀,₂] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ ℓ₂ ∙̂ ℓ₃ where field injectivity₂,₀,₂ : 𝓲njectivity₂,₀,₂
          open 𝓘njectivity₂,₀,₂ ⦃ … ⦄ public
    module _ -- Fixed=1
      {ℓ₃} (_∼₃_ : ∀ {x} {y₁ y₂ : 𝔒₂ x} → 𝔒₃ _ y₁ → 𝔒₃ _ y₂ → Ø ℓ₃)
      {ℓ₂} (_∼₂_ : ∀ {x} → 𝔒₂ x → 𝔒₂ x → Ø ℓ₂)
      where
      record [𝓘njectivity₂,₁] : Ø₀ where
        no-eta-equality
        constructor ∁
      module _
        ⦃ _ : 𝓘njection₂ ⦄
        where
        𝓲njectivity₂,₁ = ∀ (x : 𝔒₁) {y₁ y₂ : 𝔒₂ x} → injection₂ _ y₁ ∼₃ injection₂ _ y₂ → y₁ ∼₂ y₂
        record 𝓘njectivity₂,₁ ⦃ _ : [𝓘njectivity₂,₁] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ ℓ₂ ∙̂ ℓ₃ where field injectivity₂,₁ : 𝓲njectivity₂,₁
        open 𝓘njectivity₂,₁ ⦃ … ⦄ public

  injectivity₂,₀,₁[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {ℓ₃} {_∼₃_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} → 𝔒₃ _ y₁ → 𝔒₃ _ y₂ → Ø ℓ₃}
    {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
    ⦃ _ : 𝓘njection₂ 𝔒₃ ⦄
    ⦃ _ : [𝓘njectivity₂,₀,₁] 𝔒₃ _∼₃_ _∼₁_ ⦄
    ⦃ _ : 𝓘njectivity₂,₀,₁ 𝔒₃ _∼₃_ _∼₁_ ⦄
    → 𝓲njectivity₂,₀,₁ 𝔒₃ _∼₃_ _∼₁_
  injectivity₂,₀,₁[ _ ] = injectivity₂,₀,₁

  injectivity₂,₀,₂[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {ℓ₃} {_∼₃_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} → 𝔒₃ _ y₁ → 𝔒₃ _ y₂ → Ø ℓ₃}
    {ℓ₂} (_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂)
    ⦃ _ : 𝓘njection₂ 𝔒₃ ⦄
    ⦃ _ : [𝓘njectivity₂,₀,₂] 𝔒₃ _∼₃_ _∼₂_ ⦄
    ⦃ _ : 𝓘njectivity₂,₀,₂ 𝔒₃ _∼₃_ _∼₂_ ⦄
    → 𝓲njectivity₂,₀,₂ 𝔒₃ _∼₃_ _∼₂_
  injectivity₂,₀,₂[ _ ] = injectivity₂,₀,₂

  injectivity₂,₁[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {ℓ₃} {_∼₃_ : ∀ {x} {y₁ y₂ : 𝔒₂ x} → 𝔒₃ _ y₁ → 𝔒₃ _ y₂ → Ø ℓ₃}
    {ℓ₂} (_∼₂_ : ∀ {x} → 𝔒₂ x → 𝔒₂ x → Ø ℓ₂)
    ⦃ _ : [𝓘njectivity₂,₁] 𝔒₃ _∼₃_ _∼₂_ ⦄
    ⦃ _ : 𝓘njection₂ 𝔒₃ ⦄
    ⦃ _ : 𝓘njectivity₂,₁ 𝔒₃ _∼₃_ _∼₂_ ⦄
    → 𝓲njectivity₂,₁ 𝔒₃ _∼₃_ _∼₂_
  injectivity₂,₁[ _ ] = injectivity₂,₁

  module _ -- Arity=3
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {𝔬₄} (𝔒₄ : ∀ x₁ → (x₂ : 𝔒₂ x₁) → 𝔒₃ _ x₂ → Ø 𝔬₄)
    where
    𝓲njection₃ = ∀ x₁ → (x₂ : 𝔒₂ x₁) → (x₃ : 𝔒₃ _ x₂) → 𝔒₄ _ _ x₃
    record 𝓘njection₃ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ 𝔬₄ where
      constructor ∁
      field injection₃ : 𝓲njection₃
    open 𝓘njection₃ ⦃ … ⦄ public
    module _ -- Fixed=0
      {ℓ₄} (_∼₄_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} {z₁ : 𝔒₃ _ y₁} {z₂ : 𝔒₃ _ y₂} → 𝔒₄ _ _ z₁ → 𝔒₄ _ _ z₂ → Ø ℓ₄)
      where
      module _ -- Var=1
        {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
        where
        record [𝓘njectivity₃,₀,₁] : Ø₀ where
          no-eta-equality
          constructor ∁
        module _
          ⦃ _ : 𝓘njection₃ ⦄
          where
          𝓲njectivity₃,₀,₁ = ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} {z₁ : 𝔒₃ _ y₁} {z₂ : 𝔒₃ _ y₂} → injection₃ _ _ z₁ ∼₄ injection₃ _ _ z₂ → x₁ ∼₁ x₂
          record 𝓘njectivity₃,₀,₁ ⦃ _ : [𝓘njectivity₃,₀,₁] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ 𝔬₄ ∙̂ ℓ₁ ∙̂ ℓ₄ where field injectivity₃,₀,₁ : 𝓲njectivity₃,₀,₁
          open 𝓘njectivity₃,₀,₁ ⦃ … ⦄ public
      module _ -- Var=2
        {ℓ₂} (_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂)
        where
        record [𝓘njectivity₃,₀,₂] : Ø₀ where
          no-eta-equality
          constructor ∁
        module _
          ⦃ _ : 𝓘njection₃ ⦄
          where
          𝓲njectivity₃,₀,₂ = ∀ {x₁ x₂ : 𝔒₁} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} {z₁ : 𝔒₃ _ y₁} {z₂ : 𝔒₃ _ y₂} → injection₃ _ _ z₁ ∼₄ injection₃ _ _ z₂ → y₁ ∼₂ y₂
          record 𝓘njectivity₃,₀,₂ ⦃ _ : [𝓘njectivity₃,₀,₂] ⦄ : Ø 𝔬₁ ∙̂ 𝔬₂ ∙̂ 𝔬₃ ∙̂ ℓ₂ ∙̂ ℓ₄ where field injectivity₃,₀,₂ : 𝓲njectivity₃,₀,₂
          open 𝓘njectivity₃,₀,₂ ⦃ … ⦄ public

  injectivity₃,₀,₁[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {𝔬₄} {𝔒₄ : ∀ x₁ → (x₂ : 𝔒₂ x₁) → 𝔒₃ _ x₂ → Ø 𝔬₄}
    {ℓ₄} {_∼₄_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} {z₁ : 𝔒₃ _ y₁} {z₂ : 𝔒₃ _ y₂} → 𝔒₄ _ _ z₁ → 𝔒₄ _ _ z₂ → Ø ℓ₄}
    {ℓ₁} (_∼₁_ : 𝔒₁ → 𝔒₁ → Ø ℓ₁)
    ⦃ _ : [𝓘njectivity₃,₀,₁] 𝔒₄ _∼₄_ _∼₁_ ⦄
    ⦃ _ : 𝓘njection₃ 𝔒₄ ⦄
    ⦃ _ : 𝓘njectivity₃,₀,₁ 𝔒₄ _∼₄_ _∼₁_ ⦄
    → 𝓲njectivity₃,₀,₁ 𝔒₄ _∼₄_ _∼₁_
  injectivity₃,₀,₁[ _ ] = injectivity₃,₀,₁

  injectivity₃,₀,₂[_] : ∀
    {𝔬₁} {𝔒₁ : Ø 𝔬₁}
    {𝔬₂} {𝔒₂ : 𝔒₁ → Ø 𝔬₂}
    {𝔬₃} {𝔒₃ : ∀ x₁ → 𝔒₂ x₁ → Ø 𝔬₃}
    {𝔬₄} {𝔒₄ : ∀ x₁ → (x₂ : 𝔒₂ x₁) → 𝔒₃ _ x₂ → Ø 𝔬₄}
    {ℓ₄} {_∼₄_ : ∀ {x₁ x₂} {y₁ : 𝔒₂ x₁} {y₂ : 𝔒₂ x₂} {z₁ : 𝔒₃ _ y₁} {z₂ : 𝔒₃ _ y₂} → 𝔒₄ _ _ z₁ → 𝔒₄ _ _ z₂ → Ø ℓ₄}
    {ℓ₂} (_∼₂_ : ∀ {x₁ x₂} → 𝔒₂ x₁ → 𝔒₂ x₂ → Ø ℓ₂)
    ⦃ _ : [𝓘njectivity₃,₀,₂] 𝔒₄ _∼₄_ _∼₂_ ⦄
    ⦃ _ : 𝓘njection₃ 𝔒₄ ⦄
    ⦃ _ : 𝓘njectivity₃,₀,₂ 𝔒₄ _∼₄_ _∼₂_ ⦄
    → 𝓲njectivity₃,₀,₂ 𝔒₄ _∼₄_ _∼₂_
  injectivity₃,₀,₂[ _ ] = injectivity₃,₀,₂


data ⊥ : Set where

data Maybe : Set where
  just : ⊥ → Maybe
  nothing : Maybe

test : Set → Set
test x with nothing
test x | just ()
test x | nothing = test x


open import Oscar.Prelude
open import Oscar.Class
open import Oscar.Class.SimilaritySingleton
open import Oscar.Class.SimilarityM

module Oscar.Class.Similarity where

module Similarity
  {𝔞} {𝔄 : Ø 𝔞}
  {𝔟} {𝔅 : Ø 𝔟}
  {𝔣} {𝔉 : Ø 𝔣}
  {𝔞̇ 𝔟̇}
  (_∼₁_ : 𝔄 → 𝔄 → Ø 𝔞̇)
  (_∼₂_ : 𝔅 → 𝔅 → Ø 𝔟̇) (let _∼₂_ = _∼₂_; infix 4 _∼₂_)
  (_◃_ : 𝔉 → 𝔄 → 𝔅) (let _◃_ = _◃_; infix 16 _◃_)
  = ℭLASS (_◃_ , _∼₂_) (∀ {x y} f → x ∼₁ y → f ◃ x ∼₂ f ◃ y)

module _
  {𝔞} {𝔄 : Ø 𝔞}
  {𝔟} {𝔅 : Ø 𝔟}
  {𝔣} {𝔉 : Ø 𝔣}
  {𝔞̇ 𝔟̇}
  {_∼₁_ : 𝔄 → 𝔄 → Ø 𝔞̇}
  {_∼₂_ : 𝔅 → 𝔅 → Ø 𝔟̇}
  {_◃_ : 𝔉 → 𝔄 → 𝔅}
  where
  similarity = Similarity.method _∼₁_ _∼₂_ _◃_
  module _ ⦃ _ : Similarity.class _∼₁_ _∼₂_ _◃_ ⦄ where
    instance
      toSimilaritySingleton : ∀ {x y f} → SimilaritySingleton.class (x ∼₁ y) _∼₂_ _◃_ x y f
      toSimilaritySingleton .⋆ = similarity _
      toSimilarityM : ∀ {x y} → SimilarityM.class _∼₁_ _∼₂_ _◃_ x y
      toSimilarityM .⋆ = similarity

open import Oscar.Class.Smap
open import Oscar.Class.Surjection

module Similarity,cosmaparrow
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  (surjection : Surjection.type 𝔛₁ 𝔛₂)
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭₁} (𝔓₁ : 𝔛₂ → Ø 𝔭₁)
  {𝔭₂} (𝔓₂ : 𝔛₂ → Ø 𝔭₂)
  (smaparrow : Smaparrow.type ℜ 𝔓₁ 𝔓₂ surjection surjection)
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇₂} (𝔓̇₂ : ∀ {x} → 𝔓₂ x → 𝔓₂ x → Ø 𝔭̇₂)
  where
  class = ∀ {m n} → Similarity.class (ℜ̇ {m} {n}) (𝔓̇₂ {surjection n}) (flip smaparrow)
  type = ∀ {m n} → Similarity.type (ℜ̇ {m} {n}) (𝔓̇₂ {surjection n}) (flip smaparrow)

module Similarity,cosmaparrow!
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭₁} (𝔓₁ : 𝔛₂ → Ø 𝔭₁)
  {𝔭₂} (𝔓₂ : 𝔛₂ → Ø 𝔭₂)
  ⦃ _ : Smaparrow!.class ℜ 𝔓₁ 𝔓₂ ⦄
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇₂} (𝔓̇₂ : ∀ {x} → 𝔓₂ x → 𝔓₂ x → Ø 𝔭̇₂)
  = Similarity,cosmaparrow surjection ℜ 𝔓₁ 𝔓₂ smaparrow ℜ̇ 𝔓̇₂

module Similarity,cosmaphomarrow
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  (surjection : Surjection.type 𝔛₁ 𝔛₂)
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭} (𝔓 : 𝔛₂ → Ø 𝔭)
  (smaparrow : Smaphomarrow.type ℜ 𝔓 surjection)
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇} (𝔓̇ : ∀ {x} → 𝔓 x → 𝔓 x → Ø 𝔭̇)
  = Similarity,cosmaparrow surjection ℜ 𝔓 𝔓 smaparrow ℜ̇ 𝔓̇

module Similarity,cosmaphomarrow!
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭} (𝔓 : 𝔛₂ → Ø 𝔭)
  ⦃ _ : Smaphomarrow!.class ℜ 𝔓 ⦄
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇} (𝔓̇ : ∀ {x} → 𝔓 x → 𝔓 x → Ø 𝔭̇)
  = Similarity,cosmaphomarrow surjection ℜ 𝔓 smaparrow ℜ̇ 𝔓̇

module Similarity,smaparrow
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  (surjection : Surjection.type 𝔛₁ 𝔛₂)
  {𝔯} (ℜ : π̂² 𝔯 𝔛₁)
  {𝔭₁} (𝔓₁ : π̂ 𝔭₁ 𝔛₂)
  {𝔭₂} (𝔓₂ : π̂ 𝔭₂ 𝔛₂)
  (smaparrow : Smaparrow.type ℜ 𝔓₁ 𝔓₂ surjection surjection)
  {𝔭̇₁} (𝔓̇₁ : ∀̇ π̂² 𝔭̇₁ (𝔓₁ ∘ surjection))
  {𝔭̇₂} (𝔓̇₂ : ∀̇ π̂² 𝔭̇₂ (𝔓₂ ∘ surjection))
  where
  class = ∀ {m n} → Similarity.class (𝔓̇₁ {m}) (𝔓̇₂ {n}) smaparrow
  type = ∀ {m n} → Similarity.type (𝔓̇₁ {m}) (𝔓̇₂ {n}) smaparrow

module Similarity,smaparrow!
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  {𝔯} (ℜ : π̂² 𝔯 𝔛₁)
  {𝔭₁} (𝔓₁ : π̂ 𝔭₁ 𝔛₂)
  {𝔭₂} (𝔓₂ : π̂ 𝔭₂ 𝔛₂)
  ⦃ _ : Smaparrow!.class ℜ 𝔓₁ 𝔓₂ ⦄
  {𝔭̇₁} (𝔓̇₁ : ∀̇ π̂² 𝔭̇₁ (𝔓₁ ∘ surjection))
  {𝔭̇₂} (𝔓̇₂ : ∀̇ π̂² 𝔭̇₂ (𝔓₂ ∘ surjection))
  = Similarity,smaparrow surjection ℜ 𝔓₁ 𝔓₂ smaparrow 𝔓̇₁ 𝔓̇₂

module Similarity,smaphomarrow
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  (surjection : Surjection.type 𝔛₁ 𝔛₂)
  {𝔯} (ℜ : π̂² 𝔯 𝔛₁)
  {𝔭} (𝔓 : π̂ 𝔭 𝔛₂)
  (smaparrow : Smaphomarrow.type ℜ 𝔓 surjection)
  {𝔭̇} (𝔓̇ : ∀̇ π̂² 𝔭̇ (𝔓 ∘ surjection))
  = Similarity,smaparrow surjection ℜ 𝔓 𝔓 smaparrow 𝔓̇ 𝔓̇

module Similarity,smaphomarrow!
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  {𝔯} (ℜ : π̂² 𝔯 𝔛₁)
  {𝔭} (𝔓 : π̂ 𝔭 𝔛₂)
  ⦃ _ : Smaphomarrow!.class ℜ 𝔓 ⦄
  {𝔭̇} (𝔓̇ : ∀̇ π̂² 𝔭̇ (𝔓 ∘ surjection))
  = Similarity,smaphomarrow surjection ℜ 𝔓 smaparrow 𝔓̇


------------------------------------------------------------------------------
-- Streams properties
------------------------------------------------------------------------------

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

module FOTC.Data.Stream.PropertiesI where

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Base.List.PropertiesI
open import FOTC.Data.Conat
open import FOTC.Data.Conat.Equality.Type
open import FOTC.Data.Colist
open import FOTC.Data.Colist.PropertiesI
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesI
open import FOTC.Data.Stream

-----------------------------------------------------------------------------
-- Because a greatest post-fixed point is a fixed-point, then the
-- Stream predicate is also a pre-fixed point of the functional
-- StreamF, i.e.
--
-- StreamF Stream ≤ Stream (see FOTC.Data.Stream.Type).
Stream-in : ∀ {xs} →
            ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ Stream xs' →
            Stream xs
Stream-in h = Stream-coind A h' h
  where
  A : D → Set
  A xs = ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ Stream xs'

  h' : ∀ {xs} → A xs → ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs'
  h' (x' , xs' , prf , Sxs') = x' , xs' , prf , Stream-out Sxs'

zeros-Stream : Stream zeros
zeros-Stream = Stream-coind A h refl
  where
  A : D → Set
  A xs = xs ≡ zeros

  h : ∀ {xs} → A xs → ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs'
  h Axs = zero , zeros , trans Axs zeros-eq , refl

ones-Stream : Stream ones
ones-Stream = Stream-coind A h refl
  where
  A : D → Set
  A xs = xs ≡ ones

  h : ∀ {xs} → A xs → ∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs'
  h Axs = succ₁ zero , ones , trans Axs ones-eq , refl

∷-Stream : ∀ {x xs} → Stream (x ∷ xs) → Stream xs
∷-Stream {x} {xs} h = ∷-Stream-helper (Stream-out h)
  where
  ∷-Stream-helper : ∃[ x' ] ∃[ xs' ] x ∷ xs ≡ x' ∷ xs' ∧ Stream xs' →
                    Stream xs
  ∷-Stream-helper (x' , xs' , prf , Sxs') =
    subst Stream (sym (∧-proj₂ (∷-injective prf))) Sxs'

-- Version using Agda with constructor.
∷-Stream' : ∀ {x xs} → Stream (x ∷ xs) → Stream xs
∷-Stream' h with Stream-out h
... | x' , xs' , prf , Sxs' =
  subst Stream (sym (∧-proj₂ (∷-injective prf))) Sxs'

Stream→Colist : ∀ {xs} → Stream xs → Colist xs
Stream→Colist {xs} Sxs = Colist-coind A h₁ h₂
  where
  A : D → Set
  A ys = Stream ys

  h₁ : ∀ {xs} → A xs → xs ≡ [] ∨ (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ A xs')
  h₁ Axs with Stream-out Axs
  ... | x' , xs' , prf , Sxs' = inj₂ (x' , xs' , prf , Sxs')

  h₂ : A xs
  h₂ = Sxs

-- Adapted from (Sander 1992, p. 59).
streamLength : ∀ {xs} → Stream xs → length xs ≈ ∞
streamLength {xs} Sxs = ≈-coind R h₁ h₂
  where
  R : D → D → Set
  R m n = ∃[ xs ] Stream xs ∧ m ≡ length xs ∧ n ≡ ∞

  h₁ : ∀ {m n} → R m n →
       m ≡ zero ∧ n ≡ zero
         ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ R m' n')
  h₁ {m} {n} (xs , Sxs , m=lxs , n≡∞) = helper₁ (Stream-out Sxs)
    where
    helper₁ : (∃[ x' ] ∃[ xs' ] xs ≡ x' ∷ xs' ∧ Stream xs') →
              m ≡ zero ∧ n ≡ zero
                ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ R m' n')
    helper₁ (x' , xs' , xs≡x'∷xs' , Sxs') =
      inj₂ (length xs'
           , ∞
           , helper₂
           , trans n≡∞ ∞-eq
           , (xs' , Sxs' , refl , refl))
        where
        helper₂ : m ≡ succ₁ (length xs')
        helper₂ = trans m=lxs (trans (lengthCong xs≡x'∷xs') (length-∷ x' xs'))

  h₂ : R (length xs) ∞
  h₂ = xs , Sxs , refl , refl

-- Adapted from (Sander 1992, p. 59). Version using Agda with
-- constructor.
streamLength' : ∀ {xs} → Stream xs → length xs ≈ ∞
streamLength' {xs} Sxs = ≈-coind R h₁ h₂
  where
  R : D → D → Set
  R m n = ∃[ xs ] Stream xs ∧ m ≡ length xs ∧ n ≡ ∞

  h₁ : ∀ {m n} → R m n →
       m ≡ zero ∧ n ≡ zero
         ∨ (∃[ m' ] ∃[ n' ] m ≡ succ₁ m' ∧ n ≡ succ₁ n' ∧ R m' n')
  h₁ {m} (xs , Sxs , m=lxs , n≡∞) with Stream-out Sxs
  ... | x' , xs' , xs≡x'∷xs' , Sxs' =
    inj₂ (length xs' , ∞ , helper , trans n≡∞ ∞-eq , (xs' , Sxs' , refl , refl))
    where
    helper : m ≡ succ₁ (length xs')
    helper = trans m=lxs (trans (lengthCong xs≡x'∷xs') (length-∷ x' xs'))

  h₂ : R (length xs) ∞
  h₂ = xs , Sxs , refl , refl

------------------------------------------------------------------------------
-- References
--
-- Sander, Herbert P. (1992). A Logic of Functional Programs with an
-- Application to Concurrency. PhD thesis. Department of Computer
-- Sciences: Chalmers University of Technology and University of
-- Gothenburg.


-- Interpretation of partial weakenings as partial finite maps.

module Dimension.PartialWeakening.Model (E : Set) where

import Level

open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Fin using (Fin; zero; suc)
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

open import Axiom.FunctionExtensionality

open import Control.Category
open import Control.Category.Functor

open import Control.Kleisli
open import Control.Monad using (IsMonad; module IsMonad)
import Control.Monad.Error as Err
open module Error = Err E using (Error; fail; return; errorIsMonad)

-- open import Dimension.PartialWeakening E  -- horrible names
import Dimension.PartialWeakening as PW
open module PWeak = PW E

open IsMonad errorIsMonad hiding (return)
module Kl = IsCategory (kleisliIsCategory errorIsMonad)

-- Semantics given by application to a name in Fin n

apply : ∀ {n m} (f : PWeak n m) (i : Fin n) → Error (Fin m)
apply []       ()
apply (e ∷ f)  zero    = fail e
apply (e ∷ f)  (suc i) = apply f i
apply (lift f) zero    = return zero
apply (lift f) (suc i) = suc <$> apply f i
apply (weak f) i       = suc <$> apply f i

-- Soundness of id

abstract
  apply-id : ∀ n (i : Fin n) → apply id i ≡ return i
  apply-id 0       ()
  apply-id (suc n) zero    = refl
  apply-id (suc n) (suc i) with (apply id i) | (apply-id n i)
  apply-id (suc n) (suc i) | .(return i) | refl = refl

-- Lemma: one inductive step

abstract
  lift-lift-suc : ∀ {n m l} (f : PWeak n m) {g : PWeak m l} {i : Fin n} →

    (ih : apply (comp f g) i ≡ apply f i >>= apply g) →

    apply (comp (lift f) (lift g)) (suc i)  ≡
    apply (lift f) (suc i) >>= apply (lift g)

  lift-lift-suc f {g = g} {i = i} ih = begin

      apply (comp (lift f) (lift g)) (suc i)

    ≡⟨⟩                                      -- definition of comp

      apply (lift (comp f g)) (suc i)

    ≡⟨⟩                                      -- definition of apply

      suc <$> apply (comp f g) i

    ≡⟨ cong (_<$>_ suc) ih ⟩                 -- induction hypothesis

      suc <$> (apply f i >>= apply g)

    ≡⟨ map-after-bind (apply f i) ⟩          -- map commutes with bind I

      (apply f i >>= λ j → suc <$> apply g j)

    ≡⟨⟩                                      -- definition of apply

      (apply f i >>= λ j → apply (lift g) (suc j))

    ≡⟨ sym (bind-after-map (apply f i)) ⟩    -- map commutes with bind II

      (suc <$> apply f i) >>= apply (lift g)

    ≡⟨⟩                                      -- definition of apply

      apply (lift f) (suc i) >>= apply (lift g)
    ∎

-- Soundness of comp

abstract
  apply-comp : ∀ {n m l} (f : PWeak n m) (g : PWeak m l) (i : Fin n) →

   apply (comp f g) i ≡ apply f i >>= apply g

  apply-comp [] g ()
  apply-comp (e ∷ f) g zero = refl
  apply-comp (e ∷ f) g (suc i) = apply-comp f g i
  apply-comp (lift f) (e ∷ g) zero = refl
  apply-comp (lift f) (e ∷ g) (suc i) rewrite apply-comp f g i with apply f i
  ... | fail e′  = refl
  ... | return j = refl
  apply-comp (lift f) (lift g) zero = refl
  apply-comp (lift f) (lift g) (suc i) = lift-lift-suc f (apply-comp f g i)
  apply-comp (lift f) (weak g) i = begin

      suc <$> apply (comp (lift f) g) i

    ≡⟨ cong (_<$>_ suc) (apply-comp (lift f) g i) ⟩  -- ind. hyp.

      suc <$> (apply (lift f) i >>= apply g)

    ≡⟨ map-after-bind (apply (lift f) i) ⟩           -- move map

      apply (lift f) i >>= (λ j → suc <$> apply g j)
    ∎

  apply-comp (weak f) (e ∷ g) i rewrite apply-comp f g i = sym (bind-after-map (apply f i))

  apply-comp (weak f) (lift g) i rewrite apply-comp f g i = begin

      suc <$> (apply f i >>= apply g)

    ≡⟨ map-after-bind (apply f i) ⟩

      apply f i >>= (λ j → suc <$> apply g j)

    ≡⟨⟩

      apply f i >>= (λ j → apply (lift g) (suc j))

    ≡⟨ sym (bind-after-map (apply f i)) ⟩

      (suc <$> apply f i) >>= apply (lift g)
    ∎

  apply-comp (weak f) (weak g) i rewrite apply-comp (weak f) g i =
    map-after-bind (apply (weak f) i)

-- The Kleisli category of partial finite maps  Fin n → Error (Fin m)

PFin : (n m : ℕ) → Set
PFin n m = Fin n → Error (Fin m)

SPFin = λ n m → setoid (PFin n m)

pFinIsCategory : IsCategory SPFin
pFinIsCategory = record
  { ops = record
    { id  = return
    ; _⟫_ = λ f g a → f a >>= g
    }
  ; laws = record
    { id-first = fun-ext (bind-β _)
    ; id-last  = fun-ext (λ a → bind-η _)
    ; ∘-assoc  = λ f → fun-ext (λ a → bind-assoc (f a))
    ; ∘-cong   = λ{ refl refl → refl }
    }
  }

PFIN : Category _ _ _
PFIN = record { Hom = SPFin; isCategory = pFinIsCategory }

-- The evaluation functor

-- ⟦_⟧ : ∀ {n m} → PWeak n m → PFin n m
-- ⟦ f ⟧ = apply f

applyIsFunctor : IsFunctor {C = PWEAK} {D = PFIN} (λ n → n)
applyIsFunctor = record
  { ops  = record
    { map = apply
    }
  ; laws = record
    { map-id = fun-ext (apply-id _)
    ; map-∘  = λ f → fun-ext (apply-comp f _)
    }
  }


{-# OPTIONS --without-K #-}

module Pitch where

open import Data.Bool       using (Bool; false; true)
open import Data.Integer    using (ℤ; +_; -[1+_])
open import Data.Fin        using (Fin; toℕ; #_; _≟_) renaming (zero to fz; suc to fs)
open import Data.Maybe      using (Maybe; just; nothing) renaming (map to mmap)
open import Data.Nat        using (ℕ; suc; _+_; _*_; _∸_; _≡ᵇ_)
open import Data.Nat.DivMod using (_mod_; _div_)
open import Data.Product    using (_×_; _,_; proj₁)
open import Data.Vec        using (Vec; []; _∷_; map; lookup; replicate; _[_]%=_; toList)
open import Function        using (_∘_)

open import Relation.Nullary using (yes; no)

open import BitVec          using (BitVec; insert)
open import Lemmas          using (revMod; -_mod_; -_div_)
open import Util            using (findIndex)

-- Position of a pitch on an absolute scale
-- 0 is C(-1) on the international scale (where C4 is middle C)
-- or C0 on the Midi scale (where C5 is middle C)
-- Pitch is intentially set to match Midi pitch.
-- However it is fine to let 0 represent some other note and
-- translate appropriately at the end.
data Pitch : Set where
  pitch : ℕ → Pitch

unpitch : Pitch → ℕ
unpitch (pitch p) = p

-- Number of notes in the chromatic scale.
chromaticScaleSize : ℕ
chromaticScaleSize = 12

-- Number of notes in the diatonic scale.
diatonicScaleSize : ℕ
diatonicScaleSize = 7

-- Position of a pitch within an octave, in the range [0..chromaticScaleSize-1].
data PitchClass : Set where
  pitchClass : Fin chromaticScaleSize → PitchClass

unPitchClass : PitchClass → Fin chromaticScaleSize
unPitchClass (pitchClass p) = p

Scale : ℕ → Set
Scale = Vec PitchClass

-- Which octave one is in.
data Octave : Set where
  octave : ℕ → Octave

unoctave : Octave → ℕ
unoctave (octave n) = n

PitchOctave : Set
PitchOctave = PitchClass × Octave

relativeToAbsolute : PitchOctave → Pitch
relativeToAbsolute (pitchClass n , octave o) =
  pitch (o * chromaticScaleSize + (toℕ n))

absoluteToRelative : Pitch → PitchOctave
absoluteToRelative (pitch  n) =
  (pitchClass (n mod chromaticScaleSize) , octave (n div chromaticScaleSize))

pitchToClass : Pitch → PitchClass
pitchToClass = proj₁ ∘ absoluteToRelative

majorScale harmonicMinorScale : Scale diatonicScaleSize
majorScale         = map pitchClass (# 0 ∷ # 2 ∷ # 4 ∷ # 5 ∷ # 7 ∷ # 9 ∷ # 11 ∷ [])
harmonicMinorScale = map pitchClass (# 0 ∷ # 2 ∷ # 3 ∷ # 5 ∷ # 7 ∷ # 8 ∷ # 11 ∷ [])

indexInScale : {n : ℕ} → Vec PitchClass n → Fin chromaticScaleSize → Maybe (Fin n)
indexInScale []         p = nothing
indexInScale (pc ∷ pcs) p with (unPitchClass pc ≟ p)
... | yes _ = just fz
... | no  _ = mmap fs (indexInScale pcs p)

data DiatonicDegree : Set where
  diatonicDegree : Fin diatonicScaleSize → DiatonicDegree

undd : DiatonicDegree → Fin diatonicScaleSize
undd (diatonicDegree d) = d

infix 4 _≡ᵈ_
_≡ᵈ_ : DiatonicDegree → DiatonicDegree → Bool
diatonicDegree d ≡ᵈ diatonicDegree e = toℕ d ≡ᵇ toℕ e

-- round down
pitchClassToDegreeMajor : PitchClass → DiatonicDegree
pitchClassToDegreeMajor (pitchClass fz)                                                        = diatonicDegree (# 0)
pitchClassToDegreeMajor (pitchClass (fs fz))                                                   = diatonicDegree (# 0)
pitchClassToDegreeMajor (pitchClass (fs (fs fz)))                                              = diatonicDegree (# 1)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs fz))))                                         = diatonicDegree (# 1)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs fz)))))                                    = diatonicDegree (# 2)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs fz))))))                               = diatonicDegree (# 3)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs fz)))))))                          = diatonicDegree (# 3)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs (fs fz))))))))                     = diatonicDegree (# 4)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs (fs (fs fz)))))))))                = diatonicDegree (# 4)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs (fs (fs (fs fz))))))))))           = diatonicDegree (# 5)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs fz)))))))))))      = diatonicDegree (# 5)
pitchClassToDegreeMajor (pitchClass (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs fz)))))))))))) = diatonicDegree (# 6)

degreeToPitchClassMajor : DiatonicDegree → PitchClass
degreeToPitchClassMajor (diatonicDegree fz)                               = pitchClass (# 0)
degreeToPitchClassMajor (diatonicDegree (fs fz))                          = pitchClass (# 2)
degreeToPitchClassMajor (diatonicDegree (fs (fs fz)))                     = pitchClass (# 4)
degreeToPitchClassMajor (diatonicDegree (fs (fs (fs fz))))                = pitchClass (# 5)
degreeToPitchClassMajor (diatonicDegree (fs (fs (fs (fs fz)))))           = pitchClass (# 7)
degreeToPitchClassMajor (diatonicDegree (fs (fs (fs (fs (fs fz))))))      = pitchClass (# 9)
degreeToPitchClassMajor (diatonicDegree (fs (fs (fs (fs (fs (fs fz))))))) = pitchClass (# 11)

pitchToDegreeCMajor : Pitch → DiatonicDegree
pitchToDegreeCMajor = pitchClassToDegreeMajor ∘ pitchToClass

d1 d2 d3 d4 d5 d6 d7 : DiatonicDegree
d1 = diatonicDegree (# 0)
d2 = diatonicDegree (# 1)
d3 = diatonicDegree (# 2)
d4 = diatonicDegree (# 3)
d5 = diatonicDegree (# 4)
d6 = diatonicDegree (# 5)
d7 = diatonicDegree (# 6)

thirdUp : DiatonicDegree → DiatonicDegree
thirdUp (diatonicDegree d) = diatonicDegree ((toℕ d + 2) mod diatonicScaleSize)

scaleSize : {n : ℕ} → Scale n → ℕ
scaleSize {n} _ = n

transposePitch : ℤ → Pitch → Pitch
transposePitch (+_     k) (pitch n) = pitch (n + k)
transposePitch (-[1+_] k) (pitch n) = pitch (n ∸ suc k)

-- Set of pitch classes represented as a bit vector.
PitchClassSet : Set
PitchClassSet = BitVec chromaticScaleSize

addToPitchClassSet : PitchClass → PitchClassSet → PitchClassSet
addToPitchClassSet (pitchClass p) ps = insert p ps

-- Standard Midi pitches

-- first argument is relative pitch within octave
-- second argument is octave (C5 = middle C for Midi)
standardMidiPitch : Fin chromaticScaleSize → ℕ → Pitch
standardMidiPitch p o = relativeToAbsolute (pitchClass p , octave o)

c c♯ d d♯ e f f♯ g g♯ a b♭ b : ℕ → Pitch
c  = standardMidiPitch (# 0)
c♯ = standardMidiPitch (# 1)
d  = standardMidiPitch (# 2)
d♯ = standardMidiPitch (# 3)
e  = standardMidiPitch (# 4)
f  = standardMidiPitch (# 5)
f♯ = standardMidiPitch (# 6)
g  = standardMidiPitch (# 7)
g♯ = standardMidiPitch (# 8)
a  = standardMidiPitch (# 9)
b♭ = standardMidiPitch (# 10)
b  = standardMidiPitch (# 11)


module Everything where

open import Oscar.Prelude public

-- meta-class
open import Oscar.Class public

-- classes
open import Oscar.Class.Amgu                                        public
open import Oscar.Class.Apply                                       public
open import Oscar.Class.Bind                                        public
open import Oscar.Class.Category                                    public
open import Oscar.Class.Congruity                                   public
open import Oscar.Class.Fmap                                        public
open import Oscar.Class.Functor                                     public
open import Oscar.Class.HasEquivalence                              public
open import Oscar.Class.Hmap                                        public
open import Oscar.Class.Injectivity                                 public
open import Oscar.Class.IsCategory                                  public
open import Oscar.Class.IsDecidable                                 public
open import Oscar.Class.IsEquivalence                               public
open import Oscar.Class.IsFunctor                                   public
open import Oscar.Class.IsPrecategory                               public
open import Oscar.Class.IsPrefunctor                                public
open import Oscar.Class.Leftstar                                    public
open import Oscar.Class.Leftunit                                    public
open import Oscar.Class.Map                                         public
open import Oscar.Class.Precategory                                 public
open import Oscar.Class.Prefunctor                                  public
open import Oscar.Class.Properthing                                 public
open import Oscar.Class.Pure                                        public
open import Oscar.Class.Quadricity                                  public
open import Oscar.Class.Reflexivity                                 public
open import Oscar.Class.Setoid                                      public
open import Oscar.Class.Similarity                                  public
open import Oscar.Class.SimilarityM                                 public
open import Oscar.Class.SimilaritySingleton                         public
open import Oscar.Class.Smap                                        public
open import Oscar.Class.Smapoid                                     public
open import Oscar.Class.Successor₀                                  public
open import Oscar.Class.Successor₁                                  public
open import Oscar.Class.Surjection                                  public
open import Oscar.Class.Surjextensionality                          public
open import Oscar.Class.Surjidentity                                public
open import Oscar.Class.Surjtranscommutativity                      public
open import Oscar.Class.Symmetrical                                 public
open import Oscar.Class.Symmetry                                    public
open import Oscar.Class.Thickandthin                                public
open import Oscar.Class.Transassociativity                          public
open import Oscar.Class.Transextensionality                         public
open import Oscar.Class.Transitivity                                public
open import Oscar.Class.Transleftidentity                           public
open import Oscar.Class.Transrightidentity                          public
open import Oscar.Class.Unit                                        public
open import Oscar.Class.[ExtensibleType]                            public
open import Oscar.Class.[IsExtensionB]                              public

-- individual instances
open import Oscar.Class.Amgu.Term∃SubstitistMaybe                   public
open import Oscar.Class.Congruity.Proposequality                    public
open import Oscar.Class.Congruity.Proposextensequality              public
open import Oscar.Class.HasEquivalence.ExtensionṖroperty            public
open import Oscar.Class.HasEquivalence.Ṗroperty                     public
open import Oscar.Class.HasEquivalence.Substitunction               public
open import Oscar.Class.Hmap.Transleftidentity                      public
open import Oscar.Class.Injectivity.Vec                             public
open import Oscar.Class.IsDecidable.Fin                             public
open import Oscar.Class.IsDecidable.¶                               public
open import Oscar.Class.Properthing.ExtensionṖroperty               public
open import Oscar.Class.Properthing.Ṗroperty                        public
open import Oscar.Class.Reflexivity.Function                        public
open import Oscar.Class.Smap.ExtensionFinExtensionTerm              public
open import Oscar.Class.Smap.ExtensionṖroperty                      public
open import Oscar.Class.Smap.TransitiveExtensionLeftṖroperty        public
open import Oscar.Class.Surjection.⋆                                public
open import Oscar.Class.Symmetrical.ExtensionalUnifies              public
open import Oscar.Class.Symmetrical.Symmetry                        public
open import Oscar.Class.Symmetrical.Unifies                         public
open import Oscar.Class.Transextensionality.Proposequality          public
open import Oscar.Class.Transitivity.Function                       public
open import Oscar.Class.[ExtensibleType].Proposequality             public

-- instance bundles
open import Oscar.Property.Category.AListProposequality                                         public
open import Oscar.Property.Category.ExtensionProposextensequality                               public
open import Oscar.Property.Category.Function                                                    public
open import Oscar.Property.Functor.SubstitistProposequalitySubstitunctionProposextensequality   public
open import Oscar.Property.Functor.SubstitunctionExtensionTerm                                  public
open import Oscar.Property.Monad.Maybe                                                          public
open import Oscar.Property.Propergroup.Substitunction                                           public
open import Oscar.Property.Setoid.ProductIndexEquivalence                                       public
open import Oscar.Property.Setoid.Proposequality                                                public
open import Oscar.Property.Setoid.Proposextensequality                                          public
open import Oscar.Property.Setoid.ṖropertyEquivalence                                           public
open import Oscar.Property.Thickandthin.FinFinProposequalityMaybeProposequality                 public
open import Oscar.Property.Thickandthin.FinTermProposequalityMaybeProposequality                public

-- data
open import Oscar.Data.Constraint                        public
open import Oscar.Data.Decidable                         public
open import Oscar.Data.Descender                         public
open import Oscar.Data.ExtensionṖroperty                 public
open import Oscar.Data.Fin                               public
open import Oscar.Data.List                              public
open import Oscar.Data.Maybe                             public
open import Oscar.Data.ProductIndexEquivalence           public
open import Oscar.Data.ProperlyExtensionNothing          public
open import Oscar.Data.Proposequality                    public
open import Oscar.Data.ṖropertyEquivalence               public
open import Oscar.Data.Substitist                        public
open import Oscar.Data.Substitunction                    public
open import Oscar.Data.Surjcollation                     public
open import Oscar.Data.Surjextenscollation               public
open import Oscar.Data.Term                              public
open import Oscar.Data.Vec                               public
open import Oscar.Data.¶                                 public
open import Oscar.Data.𝟘                                 public
open import Oscar.Data.𝟙                                 public
open import Oscar.Data.𝟚                                 public

-- class derivations
open import Oscar.Class.Leftunit.ToUnit                  public
open import Oscar.Class.Symmetry.ToSym                   public
open import Oscar.Class.Transleftidentity.ToLeftunit     public