module InstanceArgs where

data UnitMonoid : Set where
  u : UnitMonoid
  p : UnitMonoid → UnitMonoid → UnitMonoid

record Plus (A : Set) : Set where
  infixl 6 _+_
  field
    _+_ : A → A → A

open Plus {{...}}

instance
  plus-unitMonoid : Plus UnitMonoid
  plus-unitMonoid = record { _+_ = p }

bigValue : UnitMonoid
bigValue =
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u +
  u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u + u


{-
  This is the readme of the Agda mechanization accompanying our
  POPL 2018 paper:

    "Intrinsically-Typed Interpreters for Imperative Languages"

  The repository is hosted here:
  - https://github.com/metaborg/mj.agda

  A rendered and linked version of this readme can be found here:
  - https://metaborg.github.io/mj.agda/

  This development has been tested against Agda 2.6.1.1 (see CHANGELOG for previous versions).
  If you have this installed, you should simply be able to run
  `make` in the project root. This will checkout the two dependencies in
  `./lib/` first and then build this `./Readme.agda` which serves as the main
  entrypoint to the development.

  Alternatively you can run `make docs` to build the html version of
  the development which is useful if you want to navigate the code
  (starting e.g. in `docs/index.html`) without having an editor setup
  for it. The html docs are syntax-highlighted and you can click
  references to navigate to their definitions.

  There are some minor differences between the Agda code used in the
  paper and this mechanization.  One general (but minor) discrepancy
  is that the definitions in the paper are typed in a manner that is
  not universe polymorphic.  However, the development makes extensive
  use of universe polymorphism, by explicitly quantifying over
  universe levels (e.g., `i` in `{i} → Set i`).

  Other discrepancies are summarized below in this readme.
-}

module Readme where

{-
  * Section 2 *

  We develop a monadic, well-typed interpreter for STLC and interpret
  a few example programs.

  Unlike the interpreter summarized in the paper, the STLC semantics
  in the development makes use of integers and integer operations.
-}
open import STLC.Semantics
open import STLC.Examples

{-
  * Section 3.1 - 3.3 *

  We demonstrate how naively extending the approach to cover
  imperative state is possible, but requires explicit weakening of
  bound values in the interpreter.
-}

open import STLCRef.SemanticsLB

{-
  * Section 3.4 : dependent passing style *

  We can improve the semantics with a form of monadic strength to get
  rid of explicit weakening.
-}
open import STLCRef.Semantics
open import STLCRef.Examples

{-
  * Section 4 until 4.3 *

  We implement the scopes and frames approach in the following small
  Agda library.
-}

open import ScopesFrames.ScopesFrames

{-
  * Section 4.4 *

  We demonstrate the basic usage of this library on an interpreter for
  STLC using scopes and frames.
-}
open import STLCSF.Semantics

{-
  * Section 5 *

  We show how our techniques scale by defining an intrinsically-typed
  interpreter for Middleweight Java (MJ), a language with:

  - Imperative objects
  - Sub-typing
  - Mutable, block-scoped environments
  - Early returns

  The only discrepancy between the code in this development and the
  code shown in the paper is the following:

  - Pattern matching lambdas are not useful for pattern matching
    against.  Instead of using `All` types with pattern matching
    lambdas (Section 5.3 and 5.4), we use tagging predicates in
    `MJSF.Syntax`.

  Then there are some notable differences between the original
  presentation of MJ and our development:

  - Original MJ distinguishes promotable expressions (method
    invocation and object creation) and all other expressions. We
    admit arbitrary expressions to be promoted. This does not change
    the semantics in any significant way. The expressions that we
    allow to be promoted are side-effect free.

  - returns are implemented by modeling non-void methods as having an
    expression as its last statement (technically, this is enforced by
    the type rules of Original MJ; in our development it is
    syntactically enforced).

  - MJ only has equality comparison expressions that can be used as
    conditional expressions.  We allow arbitrary expressions, and use
    if-zero (`ifz`) for conditionals.  This does not correspond to MJ
    or Java, but it would be straightforward to add Booleans and use a
    more conventional `if` statement instead.

  - If-zero statements have ordinary statements as their
    sub-statements.  These can either be block statements or any other
    statement which does not allocate a new frame.  In Original MJ, if
    statements must be blocks.

  - We include integers and integer operations, which are not in
    Original MJ.

  - Our MJ syntax admits fields typed by `void`, which Original MJ
    does not.

  - Our MJ syntax incorporates a dedicated `this` expression for
    referencing the "self" of an object.

-}

open import MJSF.Syntax
open import MJSF.Values
open import MJSF.Monad
open import MJSF.Semantics

{-
  We demonstrate that our interpreter is executable:
-}
open import MJSF.Examples.Integer
open import MJSF.Examples.DynamicDispatch

{-
  * Appendix A *

  The following code artifacts *are not* described in the paper, but
  are used as a comparison point to evaluate the impact on the
  interpreter of using the scopes and frames model of binding.

  This is an intrinsically-typed interpreter for MJ without the use of
  scope-and-frames. Instead it describes a language-*dependent*
  classtable construction to deal with object dot-access binding and
  typing contexts and environments to deal with lexical binding
  respectively.
-}
open import MJ.Syntax.Typed

-- lexical contexts
open import MJ.LexicalScope

-- class table
open import MJ.Classtable.Core
open import MJ.Classtable.Membership
open import MJ.Classtable.Code

-- semantics
open import MJ.Semantics.Values
open import MJ.Semantics.Environments
open import MJ.Semantics.Objects
open import MJ.Semantics.Objects.Flat
open import MJ.Semantics.Monadic

-- examples
open import MJ.Examples.Integer
open import MJ.Examples.Exceptions
open import MJ.Examples.While
open import MJ.Examples.DynDispatch

{-
  * Appendix B *

  Additionally we demonstrate briefly how Agda's typeclass mechanism
  is not sufficiently strong to infer store extension facts for
  weakening. (Notably Idris rejects an equivalent program as well
  because the two instances are overlapping)
-}
open import Experiments.Infer

{-
  * Appendix C *

  Our interpreters make use of the operator `_^_` operator, defined
  as:

  (1) `_^_ : ∀ {Σ Γ}{p q : List Type → Set} ⦃ w : Weakenable q ⦄ →
             M Γ p Σ → q Σ → M Γ (p ⊗ q) Σ`

  This operator is strikingly similar to the strength operator that is
  characteristic of strong monads:

  (2)  `_^_ : ∀ {p q} → M p → q → M (p ⊗ q)`

  Here, `p` and `q` are objects in a category ℂ, and M is a monad for
  ℂ.

  In the following development we show how to define a monad that is
  morally equivalent to ours.  The monad in the development below is
  defined over the category of monotone predicates.  In this category,
  the store-passing monad is a strong monad, with the usual notion of
  monadic strength, i.e., (2) above.

  We also show how, in this category, we can write an interpreter
  without explicit weakening, by writing the interpreter in a
  point-free style.
-}

open import Experiments.Category
open import Experiments.StrongMonad
open import Experiments.STLCRefPointfree

{-
  We briefly outline how these experiments relate to our paper.

  The interpreters in our paper are defined in terms of ordinary Agda
  functions and indexed types.  Agda functions and indexed types are
  not guaranteed to be weakenable, and Agda does not have built-in
  support for automatically weakening types across monadic binds.  In
  our paper, we address the weakening problem by making explicit use
  in our interpreters of the `_^_` operator, which is morally
  equivalent to the monadic strength operator for monotone predicates
  over store types, defined in the categorical development above.  Our
  `_^_` operator explicitly requires `q` to be weakenable, which is a
  fairly minimal requirement for convincing Agda's type checker that
  carrying types over monadic binds is safe.

  The categorical model enjoys a cleaner treatment of weakening, but
  it is more cumbersome to write interpreters in Agda using this
  model, because of the additional level of encoding imposed by
  constructing and working with objects and morphisms in a category,
  as encoded in Agda.  However, we imagine that the categorical
  development is a good target model for a future specification
  language for dynamic semantics.
-}


open import Data.Nat hiding (_^_ ; _+_)
open import Data.List as List hiding (null)
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.Any
open import Data.List.Relation.Unary.All as All
open import Data.List.All.Properties.Extra
open import Data.Integer
open import Data.Unit
open import Data.Empty
open import Data.Product hiding (map)
open import Relation.Binary.PropositionalEquality hiding ([_])
open ≡-Reasoning
open import Function
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Star hiding (return ; _>>=_ ; map)

module MJSF.Semantics (k : ℕ) where

open import MJSF.Syntax k
open import MJSF.Values k
open import MJSF.Monad k
open import ScopesFrames.ScopesFrames k Ty
open import Common.Weakening

-- This file contains the definitional interpreter for MJ using scopes
-- and frames, described in Section 5 of the paper.

module Semantics (g : Graph) where

  open SyntaxG g
  open ValuesG g
  open MonadG g
  open UsesVal Valᵗ valᵗ-weaken renaming (getFrame to getFrame') public

  -----------------
  -- AUXILIARIES --
  -----------------

  -- The interpreter relies on a number of auxiliary function.  We
  -- briefly summarize each.

  -- In Java (and MJ), each field member of an object is initialized
  -- to have a default value.  The `default` function defines a
  -- default value for each value type in MJ.

  default : {Σ : List Scope} → (t : VTy) → Val t Σ
  default int     = num (+ 0)
  default (ref s) = null
  default void    = void

  -- An alias for mapping `default` over a list of well-typed values:

  defaults : ∀ {fs : List Ty}{Σ} → All (#v (λ _ → ⊤)) fs → Slots fs Σ
  defaults fs = All.map (λ{ (#v' {t} _) → vᵗ (default t) }) fs

  -- `override` overrides methods in parent objects by overwriting
  -- method slots in super class frames by overriding child methods.
  --
  -- This notion of overriding does not straightforwardly scale to
  -- deal with `super`, but `super` is not a part of MJ.  See the
  -- paper or the MJ semantics in the appendix of this artifact for
  -- discussion and inspiration for how to represent class methods
  -- separately from object representations.

  override : ∀ {s Σ oms} →
             All (#m (λ ts t → (s ↦ (mᵗ ts t)) × Meth s ts t)) oms →
             M s (λ _ → ⊤) Σ
  override [] =
    return tt
  override (#m' (p , m) ∷ oms) =
    getFrame >>= λ f →
    setv p (mᵗ f m) >>= λ _ →
    override oms

  -- `init-obj` takes as input a class definition and a witness that
  -- the class has a finite inheritance chain.  The function is
  -- structurally recursive over the inheritance chain finity witness.
  --
  -- Each object is initialized by using the `initι` function, which
  -- allows us to refer to the "self" frame and store it in method
  -- definitions (i.e., the `fc` passed to the method value
  -- constructor `mᵗ` in `map-all` below).
  --
  -- Method slots are initialized using the method members in the
  -- class definition.  Field slots are initialized using `defaults`
  -- defined above.
  --
  -- After frame initialization, overrides are applied by using
  -- `override` defined above.
  --
  -- `init-obj` is assumed to be invoked in the context of the root
  -- frame, which all objects are linked to (because all scopes have
  -- an edge to the root scope).

  init-obj : ∀ {sʳ s s' Σ} → Class sʳ s → Inherits s s' → M sʳ (Frame s) Σ
  init-obj (class0 ms fs oms) (obj _)
    = getFrame >>= λ f →
      initι _ (λ fc → (All.map (λ{ (#m' m) → mᵗ fc m }) ms) ++-all (defaults fs)) (f ∷ []) >>= λ f' →
      (usingFrame f' (override oms) ^ f') >>= λ{ (_ , f') →
      return f' }
  init-obj (class0 ⦃ shape ⦄ _ _ _) (super ⦃ shape' ⦄ _)
    with (trans (sym shape) shape')
  ...  | () -- absurd case: the scope shapes of `class0` and `super` do not match
  init-obj (class1 p ⦃ shape ⦄ ms fs oms) (super ⦃ shape' ⦄ x)
    with (trans (sym shape) shape')
  ...  | refl =
    getv p >>= λ{ (cᵗ class' ic f') →
    (usingFrame f' (init-obj class' x) ^ f') >>= λ{ (f , f') →
    initι _ ⦃ shape ⦄ (λ fc → (All.map (λ{ (#m' m) → mᵗ fc m }) ms) ++-all (defaults fs)) (f' ∷ f ∷ []) >>= λ f'' →
    (usingFrame f'' (override oms) ^ f'') >>= λ{ (_ , f'') →
    return f'' }}}
  init-obj (class1 _ ⦃ shape ⦄ _ _ _) (obj _ ⦃ shape' ⦄)
    with (trans (sym shape) shape')
  ...  | () -- absurd case: the scope shapes of `class1` and `obj` do not match

  -------------------------
  -- EARLY RETURNS MONAD --
  -------------------------

  -- In order to deal with early returns, we define a specialized bind
  -- which stops evaluation and returns the yielded value.

  _>>=ᶜ_     :  ∀ {s s' s'' r Σ} →
               M s (λ Σ → Val r Σ ⊎ Frame s' Σ) Σ →
               (∀ {Σ'} → Frame s' Σ' → M s (λ Σ → Val r Σ ⊎ Frame s'' Σ) Σ') →
               M s (λ Σ → Val r Σ ⊎ Frame s'' Σ) Σ
  (m >>=ᶜ f) = m >>= λ{
      (inj₁ v) → return (inj₁ v) ;
      (inj₂ fr) → f fr
    }

  -- Continue is used to indicate that evaluation should continue.

  continue : ∀ {s r Σ} → M s (λ Σ → Val r Σ ⊎ Frame s Σ) Σ
  continue = fmap inj₂ getFrame

  mutual

    ---------------------------
    -- EXPRESSION EVALUATION --
    ---------------------------

    eval    :  ℕ → ∀ {t s Σ} → Expr s t → M s (Val t) Σ
    eval zero _ =
      timeoutᴹ
    eval (suc k) (upcast σ e) =
      eval k e >>= λ v →
      return (upcastRef σ v)
    eval (suc k) (num x) =
      return (num x)
    eval (suc k) (iop x l r) =
      eval k l >>= λ{ (num il) →
      eval k r >>= λ{ (num ir) →
      return (num (il + ir)) }}
    eval (suc k) null =
      return null
    eval (suc k) (var x) =
      getv x >>= λ{ (vᵗ v) →
      return v }
    eval (suc k) (new x) =
      getv x >>= λ{ (cᵗ class ic f) →
      usingFrame f (init-obj class ic) >>= λ{ f' →
      return (ref [] f') }}
    eval (suc k) (get e p) =
      eval k e >>= λ{
        null → raise ;
        (ref p' f) →
          usingFrame f (getv (prepend p' p)) >>= λ{ (vᵗ v) →
          return v }}
    eval (suc k) (call e p args) =
      eval k e >>= λ {
        null → raise ;
        (ref p' f) →
          usingFrame f (getv (prepend p' p)) >>= λ{ (mᵗ f' (meth s b)) →  -- f' is the "self"
          (eval-args k args ^ f') >>= λ{ (slots , f') →
          init s slots (f' ∷ []) >>= λ f'' →  -- f' is the static link of the method call frame
          usingFrame f'' (eval-body k b) }}}
    eval (suc k) (this p e) =
      getf p >>= λ f →
      usingFrame f (getl e) >>= λ f' →
      return (ref [] f')

    eval-args : ℕ → ∀ {s ts Σ} → All (Expr s) ts → M s (Slots (List.map vᵗ ts)) Σ
    eval-args zero _ = timeoutᴹ
    eval-args (suc k) [] = return []
    eval-args (suc k) (e ∷ es) =
      eval k e >>= λ v →
      (eval-args k es ^ v) >>= λ{ (slots , v) →
      return (vᵗ v ∷ slots) }

    --------------------------
    -- STATEMENT EVALUATION --
    --------------------------

    -- The following functions use the early returns monadic bind and
    -- `continue` operation defined above.

    eval-stmt : ℕ → ∀ {s s' r Σ} → Stmt s r s' → M s (λ Σ → Val r Σ ⊎ Frame s' Σ) Σ
    eval-stmt zero _ = timeoutᴹ
    eval-stmt (suc k) (run e) =
      eval k e >>= λ _ →
      continue
    eval-stmt (suc k) (ifz c t e) =
      eval k c >>= λ{
        (num (+ zero)) → eval-stmt k t
      ; (num i) → eval-stmt k e }
    eval-stmt (suc k) (set e p e') =
      eval k e >>= λ{
        null → raise ;
        (ref p' f) →
          (eval k e' ^ f) >>= λ{ (v , f) →
          usingFrame f (setv (prepend p' p) (vᵗ v)) >>= λ _ → continue }}
    eval-stmt (suc k) (loc s t) =
      getFrame >>= λ f →
      fmap inj₂ (init s (vᵗ (default t) ∷ []) (f ∷ [])) -- initializes a new local variable frame
    eval-stmt (suc k) (asgn x e) =
      eval k e >>= λ{ v →
      setv x (vᵗ v) >>= λ _ →
      continue }
    eval-stmt (suc k) (block stmts) =
      eval-stmts k stmts >>= λ{ _ →
      continue }
    eval-stmt (suc k) (ret e) =
      eval k e >>= λ{ v →
      return (inj₁ v) }

    eval-stmts : ℕ → ∀ {s r s' Σ} → Stmts s r s' → M s (λ Σ → Val r Σ ⊎ Frame s' Σ) Σ
    eval-stmts zero _ =
      timeoutᴹ
    eval-stmts (suc k) ε =
      continue
    eval-stmts (suc k) (stmt ◅ stmts) =
      eval-stmt k stmt >>=ᶜ λ f' →
      usingFrame f' (eval-stmts k stmts)

    ---------------------
    -- BODY EVALUATION --
    ---------------------

    -- Defined in terms of statement evaluation; handles early returns:

    eval-body : ℕ → ∀ {s t Σ} → Body s t → M s (Val t) Σ
    eval-body zero _ =
      timeoutᴹ
    eval-body (suc k) (body stmts e) =
      eval-stmts k stmts >>= λ{
        (inj₁ v)  → return v
      ; (inj₂ fr) → usingFrame fr (eval k e) }
    eval-body (suc k) (body-void stmts) =
      eval-stmts k stmts >>= λ _ →
      return void

  ------------------------
  -- PROGRAM EVALUATION --
  ------------------------

  -- Program evaluation first initializes the root frame to store all
  -- class definitions, and subsequently evaluates the program main
  -- function in the context of this root scope.
  --
  -- We use `initι` to initialize the root frame since class
  -- definition values (`cᵗ`) store a pointer to the root frame, i.e.,
  -- slots in the root frame are self-referential.

  eval-program : ℕ → ∀ {sʳ t} → Program sʳ t → Res (∃ λ Σ' → (Heap Σ' × Val t Σ'))
  eval-program k (program _ cs b) =
    let (f₀ , h₀) = initFrameι _ (λ f → All.map (λ{ (#c' (c , _ , ic)) → cᵗ c ic f }) cs) [] []
    in case (eval-body k b f₀ h₀) of λ{
         (ok (Σ' , h' , v , _)) → ok (Σ' , h' , v)
       ; timeout → timeout
       ; nullpointer → nullpointer }

  -- For the purpose of running the interpreter on test programs, we
  -- introduce some short-hand notation:

  -- The program will terminate succesfully in a state where p holds
  _⇓⟨_⟩_ : ∀ {sʳ a} → Program sʳ a → ℕ → (p : ∀ {Σ} → Val a Σ → Set) → Set
  p ⇓⟨ k ⟩ P with eval-program k p
  ... | nullpointer = ⊥
  ... | timeout = ⊥
  ... | ok (_ , _ , v) = P v

  -- The program will raise an exception in a state where p holds
  _⇓⟨_⟩!_ : ∀ {sʳ a} → Program sʳ a → ℕ → (p : ∀ {Σ} → Val a Σ → Set) → Set
  p ⇓⟨ k ⟩! P with eval-program k p
  ... | nullpointer = ⊤
  ... | timeout = ⊥
  ... | ok (_ , _ , v) = ⊥


open import Agda.Builtin.Reflection
open import Agda.Builtin.Unit
open import Agda.Builtin.String
open import Agda.Builtin.Nat
open import Agda.Builtin.List

infixl 5 _>>=_
_>>=_ = bindTC
pure = returnTC

defToTerm : Name → Definition → List (Arg Term) → Term
defToTerm _ (function cs) as = pat-lam cs as
defToTerm _ (data-cons d) as = con d as
defToTerm _ _ _ = unknown

derefImmediate : Term → TC Term
derefImmediate (def f args) = getDefinition f >>= λ f' → pure (defToTerm f f' args)
derefImmediate x = pure x

macro
  reflect : ∀ {ℓ} {t : Set ℓ} → t → Type → TC ⊤
  reflect {_} {t} x ty = quoteTC x >>= derefImmediate >>= λ x' → quoteTC t >>= checkType x' >>= quoteTC >>= unify ty
  -- reflect {_} {t} x ty = quoteTC x >>= derefImmediate >>= quoteTC >>= unify ty

data Maybe {ℓ} (t : Set ℓ) : Set ℓ where
  just : t → Maybe t
  nothing : Maybe t

listToMaybe : ∀ {ℓ} {t : Set ℓ} → List t → Maybe t
listToMaybe [] = nothing
listToMaybe (x ∷ _) = just x

record Show {ℓ} (t : Set ℓ) : Set ℓ where
  field
     show : t → String

open Show {{...}}

instance
  showN : Show Nat
  showN = record { show = λ _ → "nat" }

  mShow : ∀ {ℓ} {t : Set ℓ} → {{_ : Show t}} → Show (Maybe t)
  mShow = record {
      show = λ { (just x) → show x ;
                 _ → "nothing" }
    }

prog : String
prog =
  let
    x : List Nat
    x = 3 ∷ []
  in
  show (listToMaybe x)

example : Term
example = reflect prog


-------------------------------------------
-- This file contains basic definitions. --
-------------------------------------------
module Basics where

  open import Level

  data ⊥-poly {l : Level} : Set l where

  ⊥-poly-elim : ∀ {l w} {Whatever : Set w} → ⊥-poly {l} → Whatever
  ⊥-poly-elim ()


------------------------------------------------------------------------
-- An example
------------------------------------------------------------------------

module Mixfix.Acyclic.Example where

open import Codata.Musical.Notation
open import Data.Vec using ([]; _∷_; [_])
open import Data.List as List
  using (List; []; _∷_) renaming ([_] to L[_])
open import Data.List.Membership.Propositional using (_∈_)
open import Data.List.Relation.Unary.Any using (here; there)
import Codata.Musical.Colist as Colist
open import Data.Product using (∃₂; -,_)
open import Data.Unit.Polymorphic using (⊤)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Nat using (zero; suc)
open import Data.Fin using (#_)
import Data.List.Relation.Binary.Pointwise as ListEq
import Data.String as String
open String using (String; _++_)
import Data.String.Properties as String
open import Relation.Binary
open DecSetoid (ListEq.decSetoid String.≡-decSetoid) using (_≟_)
open import Function using (_∘_; _$_)
open import Data.Bool using (Bool; if_then_else_)
import Data.Bool.Show as Bool
open import Relation.Nullary.Decidable using (⌊_⌋)
import Relation.Binary.PropositionalEquality as P
open import IO
import Level

open import Mixfix.Fixity hiding (_≟_)
open import Mixfix.Operator
open import Mixfix.Expr
open import Mixfix.Acyclic.PrecedenceGraph
import Mixfix.Acyclic.Grammar as Grammar
import Mixfix.Acyclic.Show as Show
import StructurallyRecursiveDescentParsing.Simplified as Simplified
open Simplified using (Parser)
import StructurallyRecursiveDescentParsing.DepthFirst as DepthFirst
import TotalParserCombinators.BreadthFirst as BreadthFirst

------------------------------------------------------------------------
-- Operators

atom : Operator closed 0
atom = record { nameParts = "•" ∷ [] }

plus : Operator (infx left) 0
plus = record { nameParts = "+" ∷ [] }

ifThen : Operator prefx 1
ifThen = record { nameParts = "i" ∷ "t" ∷ [] }

ifThenElse : Operator prefx 2
ifThenElse = record { nameParts = "i" ∷ "t" ∷ "e" ∷ [] }

comma : Operator (infx left) 0
comma = record { nameParts = "," ∷ [] }

wellTyped : Operator postfx 1
wellTyped = record { nameParts = "⊢" ∷ "∶" ∷ [] }

------------------------------------------------------------------------
-- Precedence graph

prec : List (∃₂ Operator) → List Precedence → Precedence
prec ops = precedence (λ fix → List.mapMaybe (hasFixity fix) ops)

mutual

  a  = prec ((-, -, atom) ∷ [])                        []
  pl = prec ((-, -, plus) ∷ [])                        (a ∷ [])
  ii = prec ((-, -, ifThen) ∷ (-, -, ifThenElse) ∷ []) (pl ∷ a ∷ [])
  c  = prec ((-, -, comma) ∷ [])                       (ii ∷ pl ∷ a ∷ [])
  wt = prec ((-, -, wellTyped) ∷ [])                   (c ∷ a ∷ [])

g : PrecedenceGraph
g = wt ∷ c ∷ ii ∷ pl ∷ a ∷ []

------------------------------------------------------------------------
-- Expressions

open PrecedenceCorrect acyclic g

• : ExprIn a non
• = ⟪ here P.refl ∙ [] ⟫

_+_ : Outer pl left → Expr (a ∷ []) → ExprIn pl left
e₁ + e₂ = e₁ ⟨ here P.refl ∙ [] ⟩ˡ e₂

i_t_ : Expr g → Outer ii right → ExprIn ii right
i e₁ t e₂ = ⟪ here P.refl ∙ e₁ ∷ [] ⟩ e₂

i_t_e_ : Expr g → Expr g → Outer ii right → ExprIn ii right
i e₁ t e₂ e e₃ = ⟪ there (here P.refl) ∙ e₁ ∷ e₂ ∷ [] ⟩ e₃

_,_ : Outer c left → Expr (ii ∷ pl ∷ a ∷ []) → ExprIn c left
e₁ , e₂ = e₁ ⟨ here P.refl ∙ [] ⟩ˡ e₂

_⊢_∶ : Outer wt left → Expr g → Expr g
e₁ ⊢ e₂ ∶ = here P.refl ∙ (e₁ ⟨ here P.refl ∙ [ e₂ ] ⟫)

------------------------------------------------------------------------
-- Some tests

open Show g

fromNameParts : List NamePart → String
fromNameParts = List.foldr _++_ ""

toNameParts : String → List NamePart
toNameParts = List.map (String.fromList ∘ L[_]) ∘ String.toList

data Backend : Set where
  depthFirst   : Backend
  breadthFirst : Backend

parse : ∀ {Tok e R} → Backend → Parser Tok e R → List Tok → List R
parse depthFirst   p = DepthFirst.parseComplete p
parse breadthFirst p = BreadthFirst.parse (Simplified.⟦_⟧ p)

parseExpr : Backend → String → List String
parseExpr backend = List.map (fromNameParts ∘ show) ∘
                    parse backend (Grammar.expression g) ∘
                    toNameParts

-- The breadth-first backend is considerably slower than the
-- depth-first one on these examples.
backend = depthFirst

runTest : String → List String → IO (⊤ {ℓ = Level.zero})
runTest s₁ s₂ = do
  putStrLn ("Testing: " ++ s₁)
  Colist.mapM′ putStrLn (Colist.fromList p₁)
  putStrLn (if ⌊ p₁ ≟ s₂ ⌋ then "Passed" else "Failed")
  where p₁ = parseExpr backend s₁

main = run do
  runTest "•+•⊢•∶"      []
  runTest "•,•⊢∶"       []
  runTest "•⊢•∶"        L[ "•⊢•∶" ]
  runTest "•,i•t•+•⊢•∶" L[ "•,i•t•+•⊢•∶" ]
  runTest "i•ti•t•e•"   ("i•ti•t•e•" ∷ "i•ti•t•e•" ∷ [])


-- 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.Unary using (_∈_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
import Relation.Binary.HeterogeneousEquality as Het

open import Common

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

open import SessionType message
open import Trace message

data Transition : SessionType -> Action -> SessionType -> Set where
  inp : ∀{f x}                 -> Transition (inp f) (I x) (f x .force)
  out : ∀{f x} (!x : x ∈ dom f) -> Transition (out f) (O x) (f x .force)

data Transitions : SessionType -> Trace -> SessionType -> Set where
  refl : ∀{T} -> Transitions T [] T
  step : ∀{T α T' φ T''} (t : Transition T α T') (tr : Transitions T' φ T'') -> Transitions T (α ∷ φ) T''

transition->defined : ∀{T a S} -> Transition T a S -> Defined T
transition->defined inp     = inp
transition->defined (out _) = out

output-transition->defined : ∀{T x S} -> Transition T (O x) S -> Defined S
output-transition->defined (out !x) = !x

output-transitions->defined : ∀{T φ x S} -> Transitions T (φ ∷ʳ O x) S -> Defined S
output-transitions->defined {_} {[]} (step (out fx) refl) = fx
output-transitions->defined {_} {_ ∷ _} (step _ tr) = output-transitions->defined tr

unsnoc-transitions : ∀{T φ α S} -> Transitions T (φ ∷ʳ α) S -> ∃[ R ] (Transitions T φ R × Transition R α S)
unsnoc-transitions {_} {[]} (step t refl) = _ , refl , t
unsnoc-transitions {_} {x ∷ φ} (step t tr) with unsnoc-transitions tr
... | _ , sr , s = _ , step t sr , s

transitions+defined->defined : ∀{T as S} -> Transitions T as S -> Defined S -> Defined T
transitions+defined->defined refl def = def
transitions+defined->defined (step t _) _ = transition->defined t

transitions-eq : ∀{T T' T'' φ} (tr : Transitions T φ T') (sr : Transitions T φ T'') ->
  T' ≡ T'' × tr Het.≅ sr
transitions-eq refl refl = refl , Het.refl
transitions-eq (step inp tr) (step inp sr) with transitions-eq tr sr
... | refl , Het.refl = refl , Het.refl
transitions-eq (step (out fx) tr) (step (out gx) sr) with Defined-eq fx gx | transitions-eq tr sr
... | refl | refl , Het.refl = refl , Het.refl

win-reduces-⊥ : ∀{S S' α} → Win S → Transition S α S' → ⊥
win-reduces-⊥ (out e) (out !x) = e _ !x


------------------------------------------------------------------------
-- The Agda standard library
--
-- Pointwise lifting of relations to lists
------------------------------------------------------------------------

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

module Data.List.Relation.Binary.Pointwise where

open import Function
open import Function.Inverse using (Inverse)
open import Data.Product hiding (map)
open import Data.List.Base as List hiding (map; head; tail)
open import Data.List.Properties using (≡-dec)
open import Data.Fin using (Fin) renaming (zero to fzero; suc to fsuc)
open import Data.Nat using (ℕ; zero; suc)
open import Level
open import Relation.Nullary
open import Relation.Nullary.Negation using (contradiction)
import Relation.Nullary.Decidable as Dec using (map′)
open import Relation.Unary as U using (Pred)
open import Relation.Binary renaming (Rel to Rel₂)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

infixr 5 _∷_

data Pointwise {a b ℓ} {A : Set a} {B : Set b}
         (_∼_ : REL A B ℓ) : List A → List B → Set (a ⊔ b ⊔ ℓ) where
  []  : Pointwise _∼_ [] []
  _∷_ : ∀ {x xs y ys} (x∼y : x ∼ y) (xs∼ys : Pointwise _∼_ xs ys) →
        Pointwise _∼_ (x ∷ xs) (y ∷ ys)

------------------------------------------------------------------------
-- Operations

module _ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} where

  head : ∀ {x y xs ys} → Pointwise _∼_ (x ∷ xs) (y ∷ ys) → x ∼ y
  head (x∼y ∷ xs∼ys) = x∼y

  tail : ∀ {x y xs ys} → Pointwise _∼_ (x ∷ xs) (y ∷ ys) →
         Pointwise _∼_ xs ys
  tail (x∼y ∷ xs∼ys) = xs∼ys

  rec : ∀ {c} (P : ∀ {xs ys} → Pointwise _∼_ xs ys → Set c) →
        (∀ {x y xs ys} {xs∼ys : Pointwise _∼_ xs ys} →
          (x∼y : x ∼ y) → P xs∼ys → P (x∼y ∷ xs∼ys)) →
        P [] →
        ∀ {xs ys} (xs∼ys : Pointwise _∼_ xs ys) → P xs∼ys
  rec P c n []            = n
  rec P c n (x∼y ∷ xs∼ys) = c x∼y (rec P c n xs∼ys)

  map : ∀ {ℓ₂} {_≈_ : REL A B ℓ₂} →
        _≈_ ⇒ _∼_ → Pointwise _≈_ ⇒ Pointwise _∼_
  map ≈⇒∼ []            = []
  map ≈⇒∼ (x≈y ∷ xs≈ys) = ≈⇒∼ x≈y ∷ map ≈⇒∼ xs≈ys

------------------------------------------------------------------------
-- Relational properties

reflexive : ∀ {a b ℓ₁ ℓ₂} {A : Set a} {B : Set b}
            {_≈_ : REL A B ℓ₁} {_∼_ : REL A B ℓ₂} →
            _≈_ ⇒ _∼_ → Pointwise _≈_ ⇒ Pointwise _∼_
reflexive ≈⇒∼ []            = []
reflexive ≈⇒∼ (x≈y ∷ xs≈ys) = ≈⇒∼ x≈y ∷ reflexive ≈⇒∼ xs≈ys

refl : ∀ {a ℓ} {A : Set a} {_∼_ : Rel₂ A ℓ} →
       Reflexive _∼_ → Reflexive (Pointwise _∼_)
refl rfl {[]}     = []
refl rfl {x ∷ xs} = rfl ∷ refl rfl

symmetric : ∀ {a b ℓ₁ ℓ₂} {A : Set a} {B : Set b}
            {_≈_ : REL A B ℓ₁} {_∼_ : REL B A ℓ₂} →
            Sym _≈_ _∼_ → Sym (Pointwise _≈_) (Pointwise _∼_)
symmetric sym []            = []
symmetric sym (x∼y ∷ xs∼ys) = sym x∼y ∷ symmetric sym xs∼ys

transitive : ∀ {a b c ℓ₁ ℓ₂ ℓ₃}
             {A : Set a} {B : Set b} {C : Set c}
             {_≋_ : REL A B ℓ₁} {_≈_ : REL B C ℓ₂} {_∼_ : REL A C ℓ₃} →
             Trans _≋_ _≈_ _∼_ →
             Trans (Pointwise _≋_) (Pointwise _≈_) (Pointwise _∼_)
transitive trans []            []            = []
transitive trans (x∼y ∷ xs∼ys) (y∼z ∷ ys∼zs) =
  trans x∼y y∼z ∷ transitive trans xs∼ys ys∼zs

antisymmetric : ∀ {a b ℓ₁ ℓ₂ ℓ₃} {A : Set a} {B : Set b}
                {_≤_ : REL A B ℓ₁} {_≤′_ : REL B A ℓ₂} {_≈_ : REL A B ℓ₃} →
                Antisym _≤_ _≤′_ _≈_ →
                Antisym (Pointwise _≤_) (Pointwise _≤′_) (Pointwise _≈_)
antisymmetric antisym []            []            = []
antisymmetric antisym (x∼y ∷ xs∼ys) (y∼x ∷ ys∼xs) =
  antisym x∼y y∼x ∷ antisymmetric antisym xs∼ys ys∼xs

respects₂ : ∀ {a ℓ₁ ℓ₂} {A : Set a}
            {_≈_ : Rel₂ A ℓ₁} {_∼_ : Rel₂ A ℓ₂} →
            _∼_ Respects₂ _≈_ →
            (Pointwise _∼_) Respects₂ (Pointwise _≈_)
respects₂ {_≈_ = _≈_} {_∼_} resp = resp¹ , resp²
  where
  resp¹ : ∀ {xs} → (Pointwise _∼_ xs) Respects (Pointwise _≈_)
  resp¹ []            []            = []
  resp¹ (x≈y ∷ xs≈ys) (z∼x ∷ zs∼xs) =
    proj₁ resp x≈y z∼x ∷ resp¹ xs≈ys zs∼xs

  resp² : ∀ {ys} → (flip (Pointwise _∼_) ys) Respects (Pointwise _≈_)
  resp² []            []            = []
  resp² (x≈y ∷ xs≈ys) (x∼z ∷ xs∼zs) =
    proj₂ resp x≈y x∼z ∷ resp² xs≈ys xs∼zs

decidable : ∀ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} →
            Decidable _∼_ → Decidable (Pointwise _∼_)
decidable dec []       []       = yes []
decidable dec []       (y ∷ ys) = no (λ ())
decidable dec (x ∷ xs) []       = no (λ ())
decidable dec (x ∷ xs) (y ∷ ys) with dec x y
... | no ¬x∼y = no (¬x∼y ∘ head)
... | yes x∼y with decidable dec xs ys
...   | no ¬xs∼ys = no (¬xs∼ys ∘ tail)
...   | yes xs∼ys = yes (x∼y ∷ xs∼ys)

isEquivalence : ∀ {a ℓ} {A : Set a} {_≈_ : Rel₂ A ℓ} →
                IsEquivalence _≈_ → IsEquivalence (Pointwise _≈_)
isEquivalence eq = record
  { refl  = refl       Eq.refl
  ; sym   = symmetric  Eq.sym
  ; trans = transitive Eq.trans
  } where module Eq = IsEquivalence eq

isPreorder : ∀ {a ℓ₁ ℓ₂} {A : Set a}
             {_≈_ : Rel₂ A ℓ₁} {_∼_ : Rel₂ A ℓ₂} →
             IsPreorder _≈_ _∼_ → IsPreorder (Pointwise _≈_) (Pointwise _∼_)
isPreorder pre = record
  { isEquivalence = isEquivalence Pre.isEquivalence
  ; reflexive     = reflexive     Pre.reflexive
  ; trans         = transitive    Pre.trans
  } where module Pre = IsPreorder pre

isPartialOrder : ∀ {a ℓ₁ ℓ₂} {A : Set a}
                 {_≈_ : Rel₂ A ℓ₁} {_≤_ : Rel₂ A ℓ₂} →
                 IsPartialOrder _≈_ _≤_ →
                 IsPartialOrder (Pointwise _≈_) (Pointwise _≤_)
isPartialOrder po = record
  { isPreorder = isPreorder    PO.isPreorder
  ; antisym    = antisymmetric PO.antisym
  } where module PO = IsPartialOrder po

isDecEquivalence : ∀ {a ℓ} {A : Set a} {_≈_ : Rel₂ A ℓ} →
                   IsDecEquivalence _≈_ →
                   IsDecEquivalence (Pointwise _≈_)
isDecEquivalence eq = record
  { isEquivalence = isEquivalence DE.isEquivalence
  ; _≟_           = decidable     DE._≟_
  } where module DE = IsDecEquivalence eq

preorder : ∀ {p₁ p₂ p₃} → Preorder p₁ p₂ p₃ → Preorder _ _ _
preorder p = record
  { isPreorder = isPreorder (Preorder.isPreorder p)
  }

poset : ∀ {c ℓ₁ ℓ₂} → Poset c ℓ₁ ℓ₂ → Poset _ _ _
poset p = record
  { isPartialOrder = isPartialOrder (Poset.isPartialOrder p)
  }

setoid : ∀ {c ℓ} → Setoid c ℓ → Setoid _ _
setoid s = record
  { isEquivalence = isEquivalence (Setoid.isEquivalence s)
  }

decSetoid : ∀ {c ℓ} → DecSetoid c ℓ → DecSetoid _ _
decSetoid d = record
  { isDecEquivalence = isDecEquivalence (DecSetoid.isDecEquivalence d)
  }

------------------------------------------------------------------------
-- tabulate

module _ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} where

  tabulate⁺ : ∀ {n} {f : Fin n → A} {g : Fin n → B} →
              (∀ i → f i ∼ g i) → Pointwise _∼_ (tabulate f) (tabulate g)
  tabulate⁺ {zero}  f∼g = []
  tabulate⁺ {suc n} f∼g = f∼g fzero ∷ tabulate⁺ (f∼g ∘ fsuc)

  tabulate⁻ : ∀ {n} {f : Fin n → A} {g : Fin n → B} →
              Pointwise _∼_ (tabulate f) (tabulate g) → (∀ i → f i ∼ g i)
  tabulate⁻ {zero}  []            ()
  tabulate⁻ {suc n} (x∼y ∷ xs∼ys) fzero    = x∼y
  tabulate⁻ {suc n} (x∼y ∷ xs∼ys) (fsuc i) = tabulate⁻ xs∼ys i

------------------------------------------------------------------------
-- _++_

module _ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} where

  ++⁺ : ∀ {ws xs ys zs} → Pointwise _∼_ ws xs → Pointwise _∼_ ys zs →
        Pointwise _∼_ (ws ++ ys) (xs ++ zs)
  ++⁺ []            ys∼zs = ys∼zs
  ++⁺ (w∼x ∷ ws∼xs) ys∼zs = w∼x ∷ ++⁺ ws∼xs ys∼zs

------------------------------------------------------------------------
-- concat

module _ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} where

  concat⁺ : ∀ {xss yss} → Pointwise (Pointwise _∼_) xss yss →
            Pointwise _∼_ (concat xss) (concat yss)
  concat⁺ []                = []
  concat⁺ (xs∼ys ∷ xss∼yss) = ++⁺ xs∼ys (concat⁺ xss∼yss)

------------------------------------------------------------------------
-- length

module _ {a b ℓ} {A : Set a} {B : Set b} {_∼_ : REL A B ℓ} where

  Pointwise-length : ∀ {xs ys} → Pointwise _∼_ xs ys →
                     length xs ≡ length ys
  Pointwise-length []            = P.refl
  Pointwise-length (x∼y ∷ xs∼ys) = P.cong ℕ.suc (Pointwise-length xs∼ys)

------------------------------------------------------------------------
-- reverse

module _ {a b r} {A : Set a} {B : Set b} {R : REL A B r} where

  reverseAcc⁺ : ∀ {as bs as′ bs′} → Pointwise R as′ bs′ → Pointwise R as bs →
                Pointwise R (reverseAcc as′ as) (reverseAcc bs′ bs)
  reverseAcc⁺ rs′ []       = rs′
  reverseAcc⁺ rs′ (r ∷ rs) = reverseAcc⁺ (r ∷ rs′) rs

  reverse⁺ : ∀ {as bs} → Pointwise R as bs → Pointwise R (reverse as) (reverse bs)
  reverse⁺ = reverseAcc⁺ []

------------------------------------------------------------------------
-- map

module _ {a b c d r} {A : Set a} {B : Set b} {C : Set c} {D : Set d}
         {R : REL C D r} where

  map⁺ : ∀ {as bs} (f : A → C) (g : B → D) →
         Pointwise (λ a b → R (f a) (g b)) as bs →
         Pointwise R (List.map f as) (List.map g bs)
  map⁺ f g []       = []
  map⁺ f g (r ∷ rs) = r ∷ map⁺ f g rs

  map⁻ : ∀ {as bs} (f : A → C) (g : B → D) →
         Pointwise R (List.map f as) (List.map g bs) →
         Pointwise (λ a b → R (f a) (g b)) as bs
  map⁻ {[]} {[]} f g [] = []
  map⁻ {[]} {b ∷ bs} f g rs with Pointwise-length rs
  ... | ()
  map⁻ {a ∷ as} {[]} f g rs with Pointwise-length rs
  ... | ()
  map⁻ {a ∷ as} {b ∷ bs} f g (r ∷ rs) = r ∷ map⁻ f g rs

------------------------------------------------------------------------
-- filter

module _ {a b r p q} {A : Set a} {B : Set b} {R : REL A B r}
         {P : Pred A p} {Q : Pred B q} (P? : U.Decidable P) (Q? : U.Decidable Q)
         (P⇒Q : ∀ {a b} → R a b → P a → Q b) (Q⇒P : ∀ {a b} → R a b → Q b → P a)
         where

  filter⁺ : ∀ {as bs} → Pointwise R as bs → Pointwise R (filter P? as) (filter Q? bs)
  filter⁺ []       = []
  filter⁺ {a ∷ _} {b ∷ _} (r ∷ rs) with P? a | Q? b
  ... | yes p | yes q = r ∷ filter⁺ rs
  ... | yes p | no ¬q = contradiction (P⇒Q r p) ¬q
  ... | no ¬p | yes q = contradiction (Q⇒P r q) ¬p
  ... | no ¬p | no ¬q = filter⁺ rs

------------------------------------------------------------------------
-- replicate

module _ {a b r} {A : Set a} {B : Set b} {R : REL A B r} where

  replicate⁺ : ∀ {a b} → R a b → ∀ n → Pointwise R (replicate n a) (replicate n b)
  replicate⁺ r 0       = []
  replicate⁺ r (suc n) = r ∷ replicate⁺ r n

------------------------------------------------------------------------
-- Irrelevance

module _ {a b r} {A : Set a} {B : Set b} {R : REL A B r} where

  irrelevant : Irrelevant R → Irrelevant (Pointwise R)
  irrelevant R-irr [] [] = P.refl
  irrelevant R-irr (r ∷ rs) (r₁ ∷ rs₁) =
    P.cong₂ _∷_ (R-irr r r₁) (irrelevant R-irr rs rs₁)

------------------------------------------------------------------------
-- Properties of propositional pointwise

module _ {a} {A : Set a} where

  Pointwise-≡⇒≡ : Pointwise {A = A} _≡_ ⇒ _≡_
  Pointwise-≡⇒≡ []               = P.refl
  Pointwise-≡⇒≡ (P.refl ∷ xs∼ys) with Pointwise-≡⇒≡ xs∼ys
  ... | P.refl = P.refl

  ≡⇒Pointwise-≡ :  _≡_ ⇒ Pointwise {A = A} _≡_
  ≡⇒Pointwise-≡ P.refl = refl P.refl

  Pointwise-≡↔≡ : Inverse (setoid (P.setoid A)) (P.setoid (List A))
  Pointwise-≡↔≡ = record
    { to         = record { _⟨$⟩_ = id; cong = Pointwise-≡⇒≡ }
    ; from       = record { _⟨$⟩_ = id; cong = ≡⇒Pointwise-≡ }
    ; inverse-of = record
      { left-inverse-of  = λ _ → refl P.refl
      ; right-inverse-of = λ _ → P.refl
      }
    }

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 0.15

Rel    = Pointwise
{-# WARNING_ON_USAGE Rel
"Warning: Rel was deprecated in v0.15.
Please use Pointwise instead."
#-}
Rel≡⇒≡ = Pointwise-≡⇒≡
{-# WARNING_ON_USAGE Rel≡⇒≡
"Warning: Rel≡⇒≡ was deprecated in v0.15.
Please use Pointwise-≡⇒≡ instead."
#-}
≡⇒Rel≡ = ≡⇒Pointwise-≡
{-# WARNING_ON_USAGE ≡⇒Rel≡
"Warning: ≡⇒Rel≡ was deprecated in v0.15.
Please use ≡⇒Pointwise-≡ instead."
#-}
Rel↔≡  = Pointwise-≡↔≡
{-# WARNING_ON_USAGE Rel↔≡
"Warning: Rel↔≡ was deprecated in v0.15.
Please use Pointwise-≡↔≡ instead."
#-}

-- Version 1.0

decidable-≡ = ≡-dec
{-# WARNING_ON_USAGE decidable-≡
"Warning: decidable-≡ was deprecated in v1.0.
Please use ≡-dec from `Data.List.Properties` instead."
#-}


open import Data.Product using ( _,_ ; proj₁ ; proj₂ )
open import Data.Sum using ( inj₁ ; inj₂ )
open import Relation.Unary using ( _⊆_ )
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl ; sym ; cong )
open import Web.Semantic.DL.ABox using ( ε ; _,_ ; _∼_ ; _∈₁_ ; _∈₂_ ; ⟨ABox⟩ ; Assertions )
open import Web.Semantic.DL.ABox.Interp using ( ⌊_⌋ ; ind )
open import Web.Semantic.DL.ABox.Model using ( ⊨a-impl-⊨b ; ⟨ABox⟩-Assertions )
open import Web.Semantic.DL.Category.Composition using ( _∙_ )
open import Web.Semantic.DL.Category.Object using ( Object ; _,_ ; IN ; fin ; iface )
open import Web.Semantic.DL.Category.Morphism using ( _⇒_ ; _⊑_ ; _≣_ ; _,_ )
open import Web.Semantic.DL.Category.Properties.Composition.RespectsEquiv using ( compose-resp-≣ )
open import Web.Semantic.DL.Category.Properties.Composition.RespectsWiring using ( compose-resp-wiring )
open import Web.Semantic.DL.Category.Properties.Equivalence using ( ≣-refl ; ≣-sym ; ≣-trans )
open import Web.Semantic.DL.Category.Properties.Tensor.RespectsEquiv using ( tensor-resp-≣ )
open import Web.Semantic.DL.Category.Properties.Tensor.RespectsWiring using ( tensor-resp-wiring )
open import Web.Semantic.DL.Category.Tensor using ( _⊗_ ; _⟨⊗⟩_ )
open import Web.Semantic.DL.Category.Unit using ( I )
open import Web.Semantic.DL.Category.Wiring using
  ( wiring ; wires ; wires-≈ ; wires-≈⁻¹
  ; identity ; symm ; assoc ; assoc⁻¹ ; unit₁ ; unit₁⁻¹ ; unit₂ ; unit₂⁻¹
  ; id✓ ; ⊎-swap✓ ; ⊎-assoc✓ ; ⊎-assoc⁻¹✓ ; inj₁✓ ; inj₂✓ ; ⊎-unit₁✓ ; ⊎-unit₂✓ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox )
open import Web.Semantic.DL.TBox.Interp using ( ≈-refl′ ; ≈-trans )
open import Web.Semantic.Util using
  ( id ; _∘_ ; inode ; _⟨⊎⟩_ ; ⊎-swap ; ⊎-assoc ; ⊎-assoc⁻¹ ; ⊎-unit₁ ; ⊎-unit₂ )

module Web.Semantic.DL.Category.Properties.Wiring
  {Σ : Signature} {S T : TBox Σ} where

wiring-⊑-refl : ∀ (A B : Object S T) (f g : IN B → IN A)
  (f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A))
    (g✓ : Assertions (⟨ABox⟩ g (iface B)) ⊆ Assertions (iface A)) →
      (∀ x → f x ≡ g x) → (wiring A B f f✓ ⊑ wiring A B g g✓)
wiring-⊑-refl A B f g f✓ g✓ f≡g I I⊨STA I⊨F =
  ⊨a-impl-⊨b I (wires g (proj₁ (fin B)))
    (wires-≈⁻¹ g
      (λ x → ≈-trans ⌊ I ⌋
        (≈-refl′ ⌊ I ⌋ (cong (ind I ∘ inode) (sym (f≡g x))))
        (wires-≈ f (proj₂ (fin B) x) I⊨F))
      (proj₁ (fin B)))

wiring-≣-refl : ∀ (A B : Object S T) (f g : IN B → IN A)
  (f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A))
    (g✓ : Assertions (⟨ABox⟩ g (iface B)) ⊆ Assertions (iface A)) →
      (∀ x → f x ≡ g x) → (wiring A B f f✓ ≣ wiring A B g g✓)
wiring-≣-refl A B f g f✓ g✓ f≡g =
  ( wiring-⊑-refl A B f g f✓ g✓ f≡g
  , wiring-⊑-refl A B g f g✓ f✓ (λ x → sym (f≡g x)) )

∘✓ :  (A B C : Object S T) →
  (f : IN B → IN A) → (g : IN C → IN B) →
  (Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)) →
  (Assertions (⟨ABox⟩ g (iface C)) ⊆ Assertions (iface B)) →
  (Assertions (⟨ABox⟩ (f ∘ g) (iface C)) ⊆ Assertions (iface A))
∘✓ A B (X , X∈Fin , ε) f g f✓ g✓ ()
∘✓ A B (X , X∈Fin , C₁ , C₂) f g f✓ g✓ (inj₁ a∈C₁) = ∘✓ A B (X , X∈Fin , C₁) f g f✓ (g✓ ∘ inj₁) a∈C₁
∘✓ A B (X , X∈Fin , C₁ , C₂) f g f✓ g✓ (inj₂ a∈C₂) = ∘✓ A B (X , X∈Fin , C₂) f g f✓ (g✓ ∘ inj₂) a∈C₂
∘✓ A B (X , X∈Fin , x ∼ y) f g f✓ g✓ refl = f✓ (⟨ABox⟩-Assertions f (iface B) (g✓ refl))
∘✓ A B (X , X∈Fin , x ∈₁ c) f g f✓ g✓ refl = f✓ (⟨ABox⟩-Assertions f (iface B) (g✓ refl))
∘✓ A B (X , X∈Fin , (x , y) ∈₂ r) f g f✓ g✓ refl = f✓ (⟨ABox⟩-Assertions f (iface B) (g✓ refl))

⟨⊎⟩✓ : (A₁ A₂ B₁ B₂ : Object S T) →
  (f₁ : IN B₁ → IN A₁) → (f₂ : IN B₂ → IN A₂) →
  (Assertions (⟨ABox⟩ f₁ (iface B₁)) ⊆ Assertions (iface A₁)) →
  (Assertions (⟨ABox⟩ f₂ (iface B₂)) ⊆ Assertions (iface A₂)) →
  (Assertions (⟨ABox⟩ (f₁ ⟨⊎⟩ f₂) (iface (B₁ ⊗ B₂))) ⊆ Assertions (iface (A₁ ⊗ A₂)))
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , ε) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ ())
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , B₁₁ , B₁₂) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ (inj₁ b∈B₁₁)) =
  ⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , B₁₁) B₂ f₁ f₂ (f₁✓ ∘ inj₁) f₂✓ (inj₁ b∈B₁₁)
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , B₁₁ , B₁₂) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ (inj₂ b∈B₁₂)) =
  ⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , B₁₂) B₂ f₁ f₂ (f₁✓ ∘ inj₂) f₂✓ (inj₁ b∈B₁₂)
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , x ∼ y) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ refl) =
  inj₁ (⟨ABox⟩-Assertions inj₁ (iface A₁) (f₁✓ refl))
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , x ∈₁ c) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ refl) =
  inj₁ (⟨ABox⟩-Assertions inj₁ (iface A₁) (f₁✓ refl))
⟨⊎⟩✓ A₁ A₂ (X₁ , X₁∈Fin , (x , y) ∈₂ r) B₂ f₁ f₂ f₁✓ f₂✓ (inj₁ refl) =
  inj₁ (⟨ABox⟩-Assertions inj₁ (iface A₁) (f₁✓ refl))
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , ε) f₁ f₂ f₁✓ f₂✓ (inj₂ ())
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , B₂₁ , B₂₂) f₁ f₂ f₁✓ f₂✓ (inj₂ (inj₁ b∈B₂₁)) =
  ⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , B₂₁) f₁ f₂ f₁✓ (f₂✓ ∘ inj₁) (inj₂ b∈B₂₁)
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , B₂₁ , B₂₂) f₁ f₂ f₁✓ f₂✓ (inj₂ (inj₂ b∈B₂₂)) =
  ⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , B₂₂) f₁ f₂ f₁✓ (f₂✓ ∘ inj₂) (inj₂ b∈B₂₂)
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , x ∼ y) f₁ f₂ f₁✓ f₂✓ (inj₂ refl) =
  inj₂ (⟨ABox⟩-Assertions inj₂ (iface A₂) (f₂✓ refl))
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , x ∈₁ c) f₁ f₂ f₁✓ f₂✓ (inj₂ refl) =
  inj₂ (⟨ABox⟩-Assertions inj₂ (iface A₂) (f₂✓ refl))
⟨⊎⟩✓ A₁ A₂ B₁ (X₂ , X₂∈Fin , (x , y) ∈₂ r) f₁ f₂ f₁✓ f₂✓ (inj₂ refl) =
  inj₂ (⟨ABox⟩-Assertions inj₂ (iface A₂) (f₂✓ refl))

data Rewrite : (A B : Object S T) (F : A ⇒ B) (f : IN B → IN A) →
  (Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)) → Set₁ where
    rewrite-wiring : ∀ A B f (f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)) →
      Rewrite A B (wiring A B f f✓) f f✓
    rewrite-compose : ∀ {A B C F G f g}
      {f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)}
        {g✓ : Assertions (⟨ABox⟩ g (iface C)) ⊆ Assertions (iface B)} →
      Rewrite A B F f f✓ → Rewrite B C G g g✓ → Rewrite A C (F ∙ G) (f ∘ g) (∘✓ A B C f g f✓ g✓)
    rewrite-tensor : ∀ {A₁ A₂ B₁ B₂ F₁ F₂ f₁ f₂}
      {f₁✓ : Assertions (⟨ABox⟩ f₁ (iface B₁)) ⊆ Assertions (iface A₁)}
        {f₂✓ : Assertions (⟨ABox⟩ f₂ (iface B₂)) ⊆ Assertions (iface A₂)} →
          Rewrite A₁ B₁ F₁ f₁ f₁✓ → Rewrite A₂ B₂ F₂ f₂ f₂✓ →
            Rewrite (A₁ ⊗ A₂) (B₁ ⊗ B₂) (F₁ ⟨⊗⟩ F₂) (f₁ ⟨⊎⟩ f₂) (⟨⊎⟩✓ A₁ A₂ B₁ B₂ f₁ f₂ f₁✓ f₂✓)

rewrite-identity : ∀ A → Rewrite A A (identity A) id (id✓ A)
rewrite-identity A = rewrite-wiring A A id (id✓ A)

rewrite-symm : ∀ A B → Rewrite (A ⊗ B) (B ⊗ A) (symm A B) ⊎-swap (⊎-swap✓ A B)
rewrite-symm A B = rewrite-wiring (A ⊗ B) (B ⊗ A) ⊎-swap (⊎-swap✓ A B)

rewrite-assoc : ∀ A B C → Rewrite ((A ⊗ B) ⊗ C) (A ⊗ (B ⊗ C)) (assoc A B C) ⊎-assoc⁻¹ (⊎-assoc⁻¹✓ A B C)
rewrite-assoc A B C = rewrite-wiring ((A ⊗ B) ⊗ C) (A ⊗ (B ⊗ C)) ⊎-assoc⁻¹ (⊎-assoc⁻¹✓ A B C)

rewrite-assoc⁻¹ : ∀ A B C → Rewrite (A ⊗ (B ⊗ C)) ((A ⊗ B) ⊗ C) (assoc⁻¹ A B C) ⊎-assoc (⊎-assoc✓ A B C)
rewrite-assoc⁻¹ A B C = rewrite-wiring (A ⊗ (B ⊗ C)) ((A ⊗ B) ⊗ C) ⊎-assoc (⊎-assoc✓ A B C)

rewrite-unit₁ : ∀ A → Rewrite (I ⊗ A) A (unit₁ A) inj₂ (inj₂✓ A)
rewrite-unit₁ A = rewrite-wiring (I ⊗ A) A inj₂ (inj₂✓ A)

rewrite-unit₁⁻¹ : ∀ A → Rewrite A (I ⊗ A) (unit₁⁻¹ A) ⊎-unit₁ (⊎-unit₁✓ A)
rewrite-unit₁⁻¹ A = rewrite-wiring A (I ⊗ A) ⊎-unit₁ (⊎-unit₁✓ A)

rewrite-unit₂ : ∀ A → Rewrite (A ⊗ I) A (unit₂ A) inj₁ (inj₁✓ A)
rewrite-unit₂ A = rewrite-wiring (A ⊗ I) A inj₁ (inj₁✓ A)

rewrite-unit₂⁻¹ : ∀ A → Rewrite A (A ⊗ I) (unit₂⁻¹ A) ⊎-unit₂ (⊎-unit₂✓ A)
rewrite-unit₂⁻¹ A = rewrite-wiring A (A ⊗ I) ⊎-unit₂ (⊎-unit₂✓ A)

rewriting : ∀ {A B F G f g}
  {f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)}
    {g✓ : Assertions (⟨ABox⟩ g (iface B)) ⊆ Assertions (iface A)} →
      (Rewrite A B F f f✓) → (∀ x → f x ≡ g x) → (Rewrite A B G g g✓) →
        (F ≣ G)
rewriting {A} {B} {F} {G} {f} {g} {f✓} {g✓} F⇓f f≡g G⇓g =
  ≣-trans (lemma F⇓f)
    (≣-trans (wiring-≣-refl A B f g f✓ g✓ f≡g)
      (≣-sym (lemma G⇓g))) where

  lemma :  ∀ {A B F f}
    {f✓ : Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)} →
      (Rewrite A B F f f✓) →
        (F ≣ wiring A B f f✓)
  lemma (rewrite-wiring A B f f✓) = ≣-refl (wiring A B f f✓)
  lemma (rewrite-compose {A} {B} {C} {F} {G} {f} {g} {f✓} {g✓} F⇓f G⇓g) =
    ≣-trans (compose-resp-≣ (lemma F⇓f) (lemma G⇓g))
      (compose-resp-wiring A B C f f✓ g g✓ (f ∘ g)
        (∘✓ A B C f g f✓ g✓) (λ x → refl))
  lemma (rewrite-tensor {A₁} {A₂} {B₁} {B₂} {F₁} {F₂} {f₁} {f₂} {f₁✓} {f₂✓} F₁⇓f₁ F₂⇓f₂) =
    ≣-trans (tensor-resp-≣ (lemma F₁⇓f₁) (lemma F₂⇓f₂))
      (tensor-resp-wiring A₁ A₂ B₁ B₂ f₁ f₁✓ f₂ f₂✓ (f₁ ⟨⊎⟩ f₂)
        (⟨⊎⟩✓ A₁ A₂ B₁ B₂ f₁ f₂ f₁✓ f₂✓) (λ x → refl) (λ x → refl))


module calculus-examples where

open import Data.List using (List ; _∷_ ; [] ; _++_)
open import Data.List.Any using (here ; there ; any)
open import Data.List.Any.Properties
open import Data.OrderedListMap
open import Data.Sum using (inj₁ ; inj₂ ; _⊎_)
open import Data.Maybe.Base
open import Data.Empty using (⊥ ; ⊥-elim)
open import Data.Bool.Base using (true ; false)
open import Relation.Nullary using (Dec ; yes ; no ; ¬_)
open import Agda.Builtin.Equality using (refl ; _≡_)
open import Relation.Nullary.Decidable
  using (⌊_⌋)
open import Relation.Binary.PropositionalEquality
  using (subst ; cong ; sym ; trans)

open import calculus
open import utility

open import Esterel.Environment as Env
open import Esterel.Variable.Signal as Signal
  using (Signal ; _ₛ ; unknown ; present ; absent ; _≟ₛₜ_)
open import Esterel.Variable.Shared as SharedVar using (SharedVar)
open import Esterel.Variable.Sequential as SeqVar using (SeqVar)

open import Esterel.Lang
open import Esterel.Lang.CanFunction
open import Esterel.Lang.CanFunction.Properties
open import Esterel.Lang.CanFunction.SetSigMonotonic using (canₛ-set-sig-monotonic)
open import Esterel.Lang.Properties
open import Esterel.Lang.Binding
open import Esterel.Context
open import Esterel.CompletionCode as Code
  using () renaming (CompletionCode to Code)
open import context-properties
open import Esterel.Lang.CanFunction.Base using (canₛ-⊆-FV)

open import Data.Nat as Nat
  using (ℕ ; zero ; suc ; _+_) renaming (_⊔_ to _⊔ℕ_)
open import Data.Nat.Properties.Simple
  using (+-comm)
open import Data.Product
  using (Σ-syntax ; Σ ; _,_ ; _,′_ ; proj₁ ; proj₂ ; _×_)

open import eval

{-

This example is not true (and not provable under the current calculus)

{- rewriting a present to the true branch -}
{- cannot prove without the `signal` form -}
ex1 : ∀ S p q -> CB p ->
    signl S (emit S >> (present S ∣⇒ p ∣⇒ q)) ≡ₑ
    signl S (emit S >> p) # []
ex1 S p q CBp = calc where

  θS→unk : Env
  θS→unk = Θ SigMap.[ S ↦ Signal.unknown ] [] []

  S∈θS→unk : SigMap.∈Dom S (sig θS→unk)
  S∈θS→unk = sig-∈-single S Signal.unknown

  θS→unk[S]≡unk : sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.unknown
  θS→unk[S]≡unk = sig-stats-1map' S Signal.unknown S∈θS→unk

  θS→pre : Env
  θS→pre = set-sig{S} θS→unk S∈θS→unk Signal.present

  S∈θS→pre : SigMap.∈Dom S (sig θS→pre)
  S∈θS→pre = sig-set-mono' {S} {S} {θS→unk} {Signal.present} {S∈θS→unk} S∈θS→unk

  θS→pre[S]≡pre : sig-stats{S = S} θS→pre S∈θS→pre ≡ Signal.present
  θS→pre[S]≡pre = sig-putget{S} {θS→unk} {Signal.present} S∈θS→unk S∈θS→pre

  θS→unk[S]≠absent : ¬ sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.absent
  θS→unk[S]≠absent is rewrite θS→unk[S]≡unk = unknotabs is where
    unknotabs : Signal.unknown ≡ Signal.absent → ⊥
    unknotabs ()

  CBsignalS[emitS>>p] : CB (signl S (emit S >> p))
  CBsignalS[emitS>>p]
   = CBsig (CBseq (CBemit{S}) CBp ((λ z → λ ()) , (λ z → λ ()) , (λ z → λ ())))

  calc : signl S (emit S >> (present S ∣⇒ p ∣⇒ q)) ≡ₑ
         signl S (emit S >> p) # []
  calc =
    ≡ₑtran {r = (ρ θS→unk · (emit S >> (present S ∣⇒ p ∣⇒ q)))}
           (≡ₑstep [raise-signal])
   (≡ₑtran {r = (ρ θS→pre · (nothin >> (present S ∣⇒ p ∣⇒ q)))}
           (≡ₑstep ([emit] S∈θS→unk θS→unk[S]≠absent (deseq dehole)))
   (≡ₑtran {r = (ρ θS→pre · (present S ∣⇒ p ∣⇒ q))}
           (≡ₑctxt (dcenv dchole) (dcenv dchole) (≡ₑstep [seq-done]))
   (≡ₑtran {r = (ρ θS→pre · p)}
           (≡ₑstep ([is-present] S S∈θS→pre θS→pre[S]≡pre dehole))
   (≡ₑsymm CBsignalS[emitS>>p]
           (≡ₑtran {r = (ρ θS→unk · (emit S >> p))}
                   (≡ₑstep [raise-signal])
           (≡ₑtran {r = (ρ θS→pre · (nothin >> p))}
                   (≡ₑstep ([emit] S∈θS→unk θS→unk[S]≠absent (deseq dehole)))
           (≡ₑtran {r = ρ θS→pre · p}
                   (≡ₑctxt (dcenv dchole) (dcenv dchole) (≡ₑstep [seq-done]))
           ≡ₑrefl)))))))
           -}

Canₛpresent-fact : ∀ S p q ->
  (Signal.unwrap S) ∈ Canₛ (present S ∣⇒ p ∣⇒ q) (Θ SigMap.[ S ↦ Signal.unknown ] [] []) ->
  (Signal.unwrap S ∈ Canₛ p (Θ SigMap.[ S ↦ Signal.unknown ] [] [])) ⊎
  (Signal.unwrap S ∈ Canₛ q (Θ SigMap.[ S ↦ Signal.unknown ] [] []))
Canₛpresent-fact S  p q S∈Canₛ[presentSpq]
  with Env.Sig∈ S (Θ SigMap.[ S ↦ Signal.unknown ] [] [])
Canₛpresent-fact S p q S∈Canₛ[presentSpq] | yes S∈
  with  ⌊ present ≟ₛₜ (Env.sig-stats{S} (Θ SigMap.[ S ↦ Signal.unknown ] [] []) S∈) ⌋
Canₛpresent-fact S p q S∈Canₛ[presentSpq] | yes S∈ | true
 = inj₁ S∈Canₛ[presentSpq]
Canₛpresent-fact S p q S∈Canₛ[presentSpq] | yes S∈ | false
  with ⌊ absent ≟ₛₜ Env.sig-stats{S} (Θ SigMap.[ S ↦ Signal.unknown ] [] []) S∈ ⌋
Canₛpresent-fact S p q S∈Canₛ[presentSpq] | yes S∈ | false | true
  = inj₂ S∈Canₛ[presentSpq]
Canₛpresent-fact S p q S∈Canₛ[presentSpq] | yes S∈ | false | false
  =  ++⁻ (Canₛ p (Θ SigMap.[ S ↦ Signal.unknown ] [] [])) S∈Canₛ[presentSpq]
Canₛpresent-fact S p q _ | no ¬p
  = ⊥-elim (¬p (SigMap.update-in-keys [] S unknown))

{- rewriting a present to the false branch -}
ex2b : ∀ S p q C -> CB (C ⟦ signl S q ⟧c) ->
   Signal.unwrap S ∉ (Canₛ p (Θ SigMap.[ S ↦ unknown ] [] [])) ->
   Signal.unwrap S ∉ (Canₛ q (Θ SigMap.[ S ↦ unknown ] [] [])) ->
   signl S (present S ∣⇒ p ∣⇒ q) ≡ₑ
   signl S q # C
ex2b S p q C CBq s∉Canₛp s∉Canₛq = calc where

 θS→unk : Env
 θS→unk = Θ SigMap.[ S ↦ Signal.unknown ] [] []

 S∈θS→unk : SigMap.∈Dom S (sig θS→unk)
 S∈θS→unk = sig-∈-single S Signal.unknown

 θS→unk[S]≡unk : sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.unknown
 θS→unk[S]≡unk = sig-stats-1map' S Signal.unknown S∈θS→unk

 θS→abs : Env
 θS→abs = set-sig{S} θS→unk S∈θS→unk Signal.absent

 S∈θS→abs : SigMap.∈Dom S (sig θS→abs)
 S∈θS→abs = sig-set-mono' {S} {S} {θS→unk} {Signal.absent} {S∈θS→unk} S∈θS→unk

 θS→abs[S]≡abs : sig-stats{S = S} θS→abs S∈θS→abs ≡ Signal.absent
 θS→abs[S]≡abs = sig-putget{S} {θS→unk} {Signal.absent} S∈θS→unk S∈θS→abs

 S∉Canθₛq : Signal.unwrap S ∉ Canθₛ SigMap.[ S ↦ unknown ] 0 q []env
 S∉Canθₛq S∈Canθₛq = s∉Canₛq (Canθₛunknown->Canₛunknown S q S∈Canθₛq)

 S∉CanθₛpresentSpq : Signal.unwrap S ∉ Canθₛ SigMap.[ S ↦ unknown ]
                                            0
                                            (present S ∣⇒ p ∣⇒ q)
                                            []env
 S∉CanθₛpresentSpq S∈Canθₛ
   with Canθₛunknown->Canₛunknown S (present S ∣⇒ p ∣⇒ q) S∈Canθₛ
 ... | fact
   with Canₛpresent-fact S p q fact
 ... | inj₁ s∈Canₛp = s∉Canₛp s∈Canₛp
 ... | inj₂ s∈Canₛq = s∉Canₛq s∈Canₛq

 bwd : signl S q ≡ₑ (ρ⟨ θS→abs , WAIT ⟩· q) # C
 bwd =
    ≡ₑtran (≡ₑstep [raise-signal])
   (≡ₑtran (≡ₑstep ([absence] S S∈θS→unk θS→unk[S]≡unk S∉Canθₛq))
    ≡ₑrefl)

 fwd : signl S (present S ∣⇒ p ∣⇒ q) ≡ₑ
       (ρ⟨ θS→abs , WAIT ⟩· q) # C
 fwd =
     ≡ₑtran (≡ₑstep [raise-signal])
    (≡ₑtran (≡ₑstep ([absence] S S∈θS→unk θS→unk[S]≡unk S∉CanθₛpresentSpq))
    (≡ₑtran (≡ₑstep ([is-absent] S S∈θS→abs θS→abs[S]≡abs dehole))
     ≡ₑrefl)) where

 calc :  signl S (present S ∣⇒ p ∣⇒ q) ≡ₑ
         signl S q # C
 calc = ≡ₑtran fwd (≡ₑsymm CBq bwd)

ex2 : ∀ S p q C ->
   CB (C ⟦ signl S q ⟧c) ->
   (∀ status -> Signal.unwrap S ∉ (Canₛ p (Θ SigMap.[ S ↦ status ] [] []))) ->
   (∀ status -> Signal.unwrap S ∉ (Canₛ q (Θ SigMap.[ S ↦ status ] [] []))) ->
   signl S (present S ∣⇒ p ∣⇒ q) ≡ₑ
   signl S q # C
ex2 S p q C CB noSp noSq = ex2b S p q C CB (noSp unknown) (noSq unknown)

{-
 Although true, the this example is not provable under the current calculus
{- lifting an emit out of a par -}
ex3 : ∀ S p q -> CB (p ∥ q) ->
  signl S ((emit S >> p) ∥ q) ≡ₑ
  signl S  (emit S >> (p ∥ q)) # []
ex3 S p q CBp∥q = calc where

  θS→unk : Env
  θS→unk = Θ SigMap.[ S ↦ Signal.unknown ] [] []

  S∈θS→unk : SigMap.∈Dom S (sig θS→unk)
  S∈θS→unk = sig-∈-single S Signal.unknown

  θS→unk[S]≡unk : sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.unknown
  θS→unk[S]≡unk = sig-stats-1map' S Signal.unknown S∈θS→unk

  θS→pre : Env
  θS→pre = set-sig{S} θS→unk S∈θS→unk Signal.present

  θS→unk[S]≠absent : ¬ sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.absent
  θS→unk[S]≠absent is rewrite θS→unk[S]≡unk = unknotabs is where
    unknotabs : Signal.unknown ≡ Signal.absent → ⊥
    unknotabs ()

  calc : signl S ((emit S >> p) ∥ q) ≡ₑ
         signl S  (emit S >> (p ∥ q)) # []
  calc =
    ≡ₑtran {r = ρ⟨ θS→unk · ((emit S >> p) ∥ q)}
           (≡ₑstep [raise-signal])
   (≡ₑtran {r = ρ θS→pre · (nothin >> p ∥ q)}
           (≡ₑstep ([emit]{S = S} S∈θS→unk θS→unk[S]≠absent (depar₁ (deseq dehole))))
   (≡ₑtran {r = ρ θS→pre · (p ∥ q)}
           (≡ₑctxt (dcenv (dcpar₁ dchole))
                   (dcenv (dcpar₁ dchole))
                   (≡ₑstep [seq-done]))
           (≡ₑsymm (CBsig (CBseq CBemit CBp∥q
                                 (((λ z → λ ()) ,
                                   (λ z → λ ()) ,
                                   (λ z → λ ())))))
                   (≡ₑtran {r = ρ θS→unk · (emit S >> (p ∥ q))}
                           (≡ₑstep [raise-signal])
                   (≡ₑtran (≡ₑstep ([emit] S∈θS→unk θS→unk[S]≠absent (deseq dehole)))
                           (≡ₑctxt (dcenv dchole)  (dcenv dchole) (≡ₑstep [seq-done])))))))
                           -}

{- pushing a trap across a par -}
ex4 : ∀ n p q -> CB p ->
      done q -> p ≡ₑ q # [] ->
      (trap (exit (suc n) ∥ p)) ≡ₑ (exit n ∥ trap p) # []

ex4 = ex4-split where

  basealwaysdistinct : ∀ x -> distinct base x
  basealwaysdistinct x = (λ { z () x₂ }) , (λ { z () x₂ }) , (λ { z () x₂})

  CBplugrpartrap : ∀ n -> ∀ {r r′ BVp FVp} ->
      CorrectBinding r BVp FVp →
      r′ ≐ ceval (epar₂ (exit n)) ∷ ceval etrap ∷ [] ⟦ r ⟧c →
      CB r′
  CBplugrpartrap n {p} {p′} {BVp} {FVp} CBp r′dc
    with BVFVcorrect p BVp FVp CBp | unplugc r′dc
  ... | refl , refl | refl = CBpar CBexit (CBtrap CBp)
                                   (basealwaysdistinct BVp)
                                   (basealwaysdistinct (BVars p))
                                   (basealwaysdistinct (FVars p))
                                   (λ { _ () _ })

  CBplugtraprpar : ∀ n -> {r r′ : Term}
      {BVp FVp : Σ (List ℕ) (λ x → List ℕ × List ℕ)} →
      CorrectBinding r BVp FVp →
      r′ ≐ ceval etrap ∷ ceval (epar₂ (exit (suc n))) ∷ [] ⟦ r ⟧c →
      CB r′
  CBplugtraprpar n {r} {r′} {BVp} {FVp} CBr r′C
    with unplugc r′C
  ... | refl
    with BVFVcorrect r BVp FVp CBr
  ... | refl , refl
   = CBtrap (CBpar CBexit CBr
                   (basealwaysdistinct BVp)
                   (basealwaysdistinct (BVars r))
                   (basealwaysdistinct (FVars r))
                   (λ { _ () _}))

  ex4-split : ∀ n p q -> CB p ->
              done q -> p ≡ₑ q # [] ->
              (trap (exit (suc n) ∥ p)) ≡ₑ (exit n ∥ trap p) # []
  ex4-split n p .nothin CBp (dhalted hnothin) p≡ₑnothin =
    ≡ₑtran {r = trap (exit (suc n) ∥ nothin)}
           (≡ₑctxt (dctrap (dcpar₂ dchole))
                   (dctrap (dcpar₂ dchole))
                   (≡ₑ-context [] _ (CBplugtraprpar n) p≡ₑnothin))
    (≡ₑtran {r = trap (exit (suc n))}
            (≡ₑctxt (dctrap dchole)
                    (dctrap dchole)
                    (≡ₑtran (≡ₑstep [par-swap])
                            (≡ₑstep ([par-nothing] (dhalted (hexit (suc n)))))))
    (≡ₑtran {r = exit n}
            (≡ₑstep ([trap-done] (hexit (suc n))))
    (≡ₑsymm (CBpar CBexit (CBtrap CBp)
                   (basealwaysdistinct (BVars p))
                   (basealwaysdistinct (BVars p))
                   (basealwaysdistinct (FVars p))
                   (λ { _ () _ }))
            (≡ₑtran {r = exit n ∥ trap nothin}
                    (≡ₑctxt (dcpar₂ (dctrap dchole))
                            (dcpar₂ (dctrap dchole))
                            (≡ₑ-context [] _ (CBplugrpartrap n) p≡ₑnothin))
            (≡ₑtran {r = exit n ∥ nothin}
                    (≡ₑctxt (dcpar₂ dchole) (dcpar₂ dchole) (≡ₑstep ([trap-done] hnothin)))
            (≡ₑtran {r = nothin ∥ exit n}
                    (≡ₑstep [par-swap])
            (≡ₑtran {r = exit n}
                    (≡ₑstep ([par-nothing] (dhalted (hexit n))))
            ≡ₑrefl)))))))

  ex4-split n p .(exit 0) CBp (dhalted (hexit 0)) p≡ₑexit0 =
    ≡ₑtran {r = trap (exit (suc n) ∥ exit 0) }
           (≡ₑctxt (dctrap (dcpar₂ dchole))
                   (dctrap (dcpar₂ dchole))
                   (≡ₑ-context [] _ (CBplugtraprpar n) p≡ₑexit0))
           (≡ₑtran {r = trap (exit (suc n))}
                   (≡ₑctxt (dctrap dchole) (dctrap dchole)
                           (≡ₑstep ([par-2exit] (suc n) zero)))
           (≡ₑtran {r = exit n}
                   (≡ₑstep ([trap-done] (hexit (suc n))))
           (≡ₑsymm (CBpar CBexit (CBtrap CBp)
                          (basealwaysdistinct (BVars p))
                          (basealwaysdistinct (BVars p))
                          (basealwaysdistinct (FVars p))
                          (λ { _ () _ }))
                   (≡ₑtran {r = exit n ∥ trap (exit 0)}
                           (≡ₑctxt (dcpar₂ (dctrap dchole))
                                   (dcpar₂ (dctrap dchole))
                                   (≡ₑ-context [] _ (CBplugrpartrap n) p≡ₑexit0))
                   (≡ₑtran {r = exit n ∥ nothin }
                           (≡ₑctxt (dcpar₂ dchole) (dcpar₂ dchole)
                                   (≡ₑstep ([trap-done] (hexit zero))))
                   (≡ₑtran {r = nothin ∥ exit n}
                           (≡ₑstep [par-swap])
                   (≡ₑtran {r = exit n}
                           (≡ₑstep ([par-nothing] (dhalted (hexit n))))
                   ≡ₑrefl)))))))

  ex4-split n p .(exit (suc m)) CBp (dhalted (hexit (suc m))) p≡ₑexitm =
    ≡ₑtran {r = trap (exit (suc n) ∥ exit (suc m)) }
           (≡ₑctxt (dctrap (dcpar₂ dchole))
                   (dctrap (dcpar₂ dchole))
                   (≡ₑ-context [] _ (CBplugtraprpar n) p≡ₑexitm))
    (≡ₑtran {r = trap (exit (suc n ⊔ℕ (suc m)))}
            (≡ₑctxt (dctrap dchole) (dctrap dchole)
                    (≡ₑstep ([par-2exit] (suc n) (suc m))))
    (≡ₑtran {r = ↓_ {p = (exit (suc n ⊔ℕ (suc m)))} (hexit (suc n ⊔ℕ (suc m))) }
            (≡ₑstep ([trap-done] (hexit (suc (n ⊔ℕ m)))))
    (≡ₑsymm (CBpar CBexit (CBtrap CBp)
                          (basealwaysdistinct (BVars p))
                          (basealwaysdistinct (BVars p))
                          (basealwaysdistinct (FVars p))
                          (λ { _ () _ }))
            (≡ₑtran {r = exit n ∥ trap (exit (suc m))}
                    (≡ₑctxt (dcpar₂ (dctrap dchole))
                            (dcpar₂ (dctrap dchole))
                            (≡ₑ-context [] _ (CBplugrpartrap n) p≡ₑexitm))
            (≡ₑtran {r = exit n ∥  ↓_ {p = exit (suc m)} (hexit (suc m))}
                    (≡ₑctxt (dcpar₂ dchole) (dcpar₂ dchole)
                            (≡ₑstep ([trap-done] (hexit (suc m)))) )
            (≡ₑtran {r =  ↓_ {p = (exit (suc n ⊔ℕ (suc m)))} (hexit (suc n ⊔ℕ (suc m))) }
                    (≡ₑstep ([par-2exit] n m))
            ≡ₑrefl))))))

  ex4-split n p q CBp (dpaused pausedq) p≡ₑq =
    ≡ₑtran {r = trap (exit (suc n) ∥ q)}
           (≡ₑctxt (dctrap (dcpar₂ dchole))
                   (dctrap (dcpar₂ dchole))
                   (≡ₑ-context [] _ (CBplugtraprpar n) p≡ₑq))
    (≡ₑtran {r = trap (exit (suc n))}
            (≡ₑctxt (dctrap dchole) (dctrap dchole)
                    (≡ₑstep ([par-1exit] (suc n) pausedq)))
    (≡ₑtran {r = exit n}
            (≡ₑstep ([trap-done] (hexit (suc n))))
    (≡ₑsymm (CBpar CBexit (CBtrap CBp)
                        (basealwaysdistinct (BVars p))
                        (basealwaysdistinct (BVars p))
                        (basealwaysdistinct (FVars p))
                        (λ { _ () _ }))
            (≡ₑtran {r = exit n ∥ trap q}
                    (≡ₑctxt (dcpar₂ (dctrap dchole))
                            (dcpar₂ (dctrap dchole))
                            (≡ₑ-context [] _ (CBplugrpartrap n) p≡ₑq))
            (≡ₑtran {r = exit n}
                    (≡ₑstep ([par-1exit] n (ptrap pausedq)))
            ≡ₑrefl)))))

{- lifting a signal out of an evaluation context -}
ex5 : ∀ S p q r E -> CB r ->
      q ≐ E ⟦(signl S p)⟧e ->
      r ≐ E ⟦ p ⟧e ->
      (ρ⟨ []env , WAIT ⟩· q) ≡ₑ (ρ⟨ []env , WAIT ⟩· (signl S r)) # []
ex5 S p q r E CBr decomp1 decomp2 = calc where

  θS→unk : Env
  θS→unk = Θ SigMap.[ S ↦ Signal.unknown ] [] []

  replugit : q ≐ Data.List.map ceval E ⟦ signl S p ⟧c ->
             E ⟦ ρ⟨ θS→unk , WAIT ⟩· p ⟧e ≐ Data.List.map ceval E ⟦ ρ⟨ θS→unk , WAIT ⟩· p ⟧c
  replugit x = ⟦⟧e-to-⟦⟧c Erefl

  calc : (ρ⟨ []env , WAIT ⟩· q) ≡ₑ (ρ⟨ []env , WAIT ⟩· (signl S r)) # []
  calc =
    ≡ₑtran {r = ρ⟨ []env , WAIT ⟩· (E ⟦ (ρ⟨ θS→unk , WAIT ⟩· p) ⟧e)}
           (≡ₑctxt (dcenv (⟦⟧e-to-⟦⟧c decomp1))
                   (dcenv (replugit (⟦⟧e-to-⟦⟧c decomp1)))
                   (≡ₑstep [raise-signal]))
   (≡ₑtran {r = ρ⟨ θS→unk , WAIT ⟩· (E ⟦ p ⟧e)}
           (≡ₑstep ([merge]{E = E} Erefl))
           (≡ₑsymm (CBρ (CBsig CBr))
                 (≡ₑtran {r = ρ⟨ []env , WAIT ⟩· (ρ⟨ θS→unk , WAIT ⟩· r)}
                         (≡ₑctxt (dcenv dchole) (dcenv dchole) (≡ₑstep [raise-signal]))
                         (≡ₑstep ([merge] {E = []}
                                          (subst (\ x ->
                                            ρ⟨ θS→unk , WAIT ⟩· x ≐ [] ⟦
                                            ρ⟨ θS→unk , WAIT ⟩· E ⟦ p ⟧e ⟧e)
                                            (unplug decomp2) dehole))))))

{- two specific examples of lifting a signal out of an evaluation context -}
ex6 : ∀ S p q -> CB (p ∥ q) ->
      (ρ⟨ []env , WAIT ⟩· ((signl S p) ∥ q)) ≡ₑ (ρ⟨ []env , WAIT ⟩· (signl S (p ∥ q))) # []
ex6 S p q CBp∥q =
  ex5 S p (signl S p ∥ q) (p ∥ q) ((epar₁ q) ∷ []) CBp∥q (depar₁ dehole) (depar₁ dehole)

ex7 : ∀ S p q -> CB (p >> q) ->
      (ρ⟨ []env , WAIT ⟩· ((signl S p) >> q)) ≡ₑ (ρ⟨ []env , WAIT ⟩· (signl S (p >> q))) # []
ex7 S p q CBp>>q =
  ex5 S p (signl S p >> q) (p >> q) ((eseq q) ∷ []) CBp>>q (deseq dehole) (deseq dehole)

{- pushing a seq into a binding form (a signal in this case).

   this shows the need for the outer environment ρ [} · _
   in order to manipulate the environment.
-}
ex8-worker : ∀ {BV FV} S p q ->
      CorrectBinding ((signl S  p) >> q) BV FV ->
      (ρ⟨ []env , WAIT ⟩· (signl S  p) >> q) ≡ₑ
      (ρ⟨ []env , WAIT ⟩·  signl S (p  >> q)) # []
ex8-worker S p q (CBseq {BVp = BVsigS·p} {FVq = FVq} (CBsig {BV = BVp} CBp) CBq BVsigS·p≠FVq) =
             ≡ₑtran {r = ρ⟨ []env , WAIT ⟩· (ρ⟨ [S]-env S , WAIT ⟩· p) >> q} {C = []}
               (≡ₑctxt {C = []} {C′ = cenv []env WAIT ∷ ceval (eseq q) ∷ []}
                 Crefl Crefl
                 (≡ₑstep ([raise-signal] {p} {S})))
            (≡ₑtran {r = ρ⟨ [S]-env S , WAIT ⟩· p >> q} {C = []}
               (≡ₑstep ([merge] (deseq dehole)))
            (≡ₑsymm
              (CBρ
                (CBsig
                  (CBseq CBp CBq
                    (⊆-respect-distinct-left (∪ʳ (+S S base) ⊆-refl) BVsigS·p≠FVq))))
            (≡ₑtran {r = ρ⟨ []env , WAIT ⟩· (ρ⟨ [S]-env S , WAIT ⟩· p >> q)} {C = []}
              (≡ₑctxt {C = []} {C′ = cenv []env WAIT ∷ []}
                Crefl Crefl
                (≡ₑstep ([raise-signal] {p >> q} {S})))
            (≡ₑstep ([merge] dehole)))))

ex8 : ∀ S p q ->
             CB ((signl S  p) >> q) ->
      (ρ⟨ []env , WAIT ⟩· (signl S  p) >> q) ≡ₑ
      (ρ⟨ []env , WAIT ⟩·  signl S (p  >> q)) # []
ex8 S p q cb = ex8-worker S p q cb

{- rearranging signal forms -}
ex9 : ∀ S1 S2 p ->
     CB p ->
     signl S1 (signl S2 p) ≡ₑ signl S2 (signl S1 p) # []
ex9 S1 S2 p CBp =
  ≡ₑtran {r = ρ⟨ (Θ SigMap.[ S1 ↦ Signal.unknown ] [] []) , WAIT ⟩· (signl S2 p)}
         (≡ₑstep [raise-signal])
 (≡ₑtran {r = (ρ⟨ Θ SigMap.[ S1 ↦ Signal.unknown ] [] [] , WAIT ⟩·
              (ρ⟨ Θ SigMap.[ S2 ↦ Signal.unknown ] [] [] , WAIT ⟩· p))}
         (≡ₑctxt (dcenv dchole) (dcenv dchole) (≡ₑstep [raise-signal]))
 (≡ₑtran {r = (ρ⟨ (Θ SigMap.[ S1 ↦ Signal.unknown ] [] []) ←
               (Θ SigMap.[ S2 ↦ Signal.unknown ] [] []) , A-max WAIT WAIT ⟩·
               p)}
         (≡ₑstep ([merge] dehole))
 (≡ₑsymm (CBsig (CBsig CBp))
         (≡ₑtran {r = ρ⟨ (Θ SigMap.[ S2 ↦ Signal.unknown ] [] []) , WAIT ⟩· (signl S1 p)}
                 (≡ₑstep [raise-signal])
         (≡ₑtran {r = (ρ⟨ Θ SigMap.[ S2 ↦ Signal.unknown ] [] [] , WAIT ⟩·
                        (ρ⟨ Θ SigMap.[ S1 ↦ Signal.unknown ] [] [] , WAIT ⟩· p))}
                 (≡ₑctxt (dcenv dchole) (dcenv dchole) (≡ₑstep [raise-signal]))
         (≡ₑtran {r = (ρ⟨ (Θ SigMap.[ S2 ↦ Signal.unknown ] [] []) ←
                        (Θ SigMap.[ S1 ↦ Signal.unknown ] [] []) ,
                        A-max WAIT WAIT ⟩·
                          p)}
                 (≡ₑstep ([merge] dehole))
                 (map-order-irr S1 S2 WAIT p))))))) where

  distinct-Ss-env'' : ∀ S1 S2 S3 ->
     ¬ Signal.unwrap S1 ≡ Signal.unwrap S2 ->
     Data.List.Any.Any (_≡_ S3) (Signal.unwrap S1 ∷ []) ->
     Data.List.Any.Any (_≡_ S3) (Signal.unwrap S2 ∷ []) ->
     ⊥
  distinct-Ss-env'' S1 S2 S3 S1≠S2 (here S3=S1) (here S3=S2) rewrite S3=S1 = S1≠S2 S3=S2
  distinct-Ss-env'' S1 S2 S3 S1≠S2 (here px) (there ())
  distinct-Ss-env'' S1 S2 S3 S1≠S2 (there ()) S3∈Domθ2

  distinct-Ss-env' : ∀ S1 S2 S3 ->
     ¬ Signal.unwrap S1 ≡ Signal.unwrap S2 ->
     Data.List.Any.Any (_≡_ S3) (Signal.unwrap S1 ∷ []) ->
     S3 ∈ proj₁ (Dom (Θ SigMap.[ S2 ↦ Signal.unknown ] [] [])) ->
     ⊥
  distinct-Ss-env' S1 S2 S3 S1≠S2 S3∈Domθ1 S3∈Domθ2
     rewrite SigMap.keys-1map S2 Signal.unknown =
       distinct-Ss-env'' S1 S2 S3 S1≠S2 S3∈Domθ1 S3∈Domθ2

  distinct-Ss-env : ∀ S1 S2 S3 ->
     ¬ Signal.unwrap S1 ≡ Signal.unwrap S2 ->
     S3 ∈ proj₁ (Dom (Θ SigMap.[ S1 ↦ Signal.unknown ] [] [])) ->
     S3 ∈ proj₁ (Dom (Θ SigMap.[ S2 ↦ Signal.unknown ] [] [])) ->
     ⊥
  distinct-Ss-env S1 S2 S3 S1≠S2 S3∈Domθ1 S3∈Domθ2
    rewrite SigMap.keys-1map S1 Signal.unknown =
      distinct-Ss-env' S1 S2 S3 S1≠S2 S3∈Domθ1 S3∈Domθ2

  distinct-doms : ∀ S1 S2 ->
    ¬ (Signal.unwrap S1 ≡ Signal.unwrap S2) ->
    distinct (Env.Dom (Θ SigMap.[ S1 ↦ Signal.unknown ] [] []))
             (Env.Dom (Θ SigMap.[ S2 ↦ Signal.unknown ] [] []))
  distinct-doms S1 S2 S1≠S2 =
    (λ {S3 S3∈θ1 S3∈θ2 -> distinct-Ss-env S1 S2 S3 S1≠S2 S3∈θ1 S3∈θ2})
    ,
    (λ x₁ x₂ → λ ())
    ,
    (λ x₁ x₂ → λ ())

  map-order-irr' : ∀ S1 S2 ->
     (Θ SigMap.[ S1 ↦ Signal.unknown ] [] []) ←
     (Θ SigMap.[ S2 ↦ Signal.unknown ] [] [])
     ≡
     (Θ SigMap.[ S2 ↦ Signal.unknown ] [] []) ←
     (Θ SigMap.[ S1 ↦ Signal.unknown ] [] [])
  map-order-irr' S1 S2 with (Signal.unwrap S1) Nat.≟ (Signal.unwrap S2)
  map-order-irr' S1 S2 | yes S1=S2 rewrite S1=S2 = refl
  map-order-irr' S1 S2 | no ¬S1=S2 =  Env.←-comm
                               ((Θ SigMap.[ S1 ↦ Signal.unknown ] [] []))
                               ((Θ SigMap.[ S2 ↦ Signal.unknown ] [] []))
                               (distinct-doms S1 S2 ¬S1=S2)

  map-order-irr : ∀ S1 S2 A p ->
       (ρ⟨
         Θ SigMap.[ S2 ↦ Signal.unknown ] [] [] ←
         Θ SigMap.[ S1 ↦ Signal.unknown ] [] []
         ,
         A ⟩· p)
        ≡ₑ
        (ρ⟨
         Θ SigMap.[ S1 ↦ Signal.unknown ] [] [] ←
         Θ SigMap.[ S2 ↦ Signal.unknown ] [] []
         ,
         A ⟩· p) # []
  map-order-irr S1 S2 A p rewrite map-order-irr' S1 S2 = ≡ₑrefl

{- dropping a loop whose body is just `exit` -}

ex10 : ∀ C n -> (loop (exit n)) ≡ₑ (exit n) # C
ex10 C n = ≡ₑtran (≡ₑstep [loop-unroll]) (≡ₑstep [loopˢ-exit])

{- (nothin ∥ p) ≡ₑ p, but only if we know that
   p goes to something that's done -}
ex11 : ∀ p q ->
  CB p -> done q -> p ≡ₑ q # [] ->
  (nothin ∥ p) ≡ₑ p # []
ex11 = calc where

  ex11-pq : ∀ q C -> done q -> (nothin ∥ q) ≡ₑ q # C
  ex11-pq .nothin C (dhalted hnothin) =
    ≡ₑtran (≡ₑstep [par-swap]) (≡ₑstep ([par-nothing] (dhalted hnothin)))
  ex11-pq .(exit n) C (dhalted (hexit n)) =
    ≡ₑstep ([par-nothing] (dhalted (hexit n)))
  ex11-pq q C (dpaused p/paused) =
   ≡ₑstep ([par-nothing] (dpaused p/paused))

  basealwaysdistinct : ∀ x -> distinct base x
  basealwaysdistinct x = (λ { z () x₂ }) , (λ { z () x₂ }) , (λ { z () x₂})

  CBp->CBnothingp : {r r′ : Term}
      {BVp FVp : Σ (List ℕ) (λ x → List ℕ × List ℕ)} →
      CorrectBinding r BVp FVp →
      r′ ≐ ceval (epar₂ nothin) ∷ [] ⟦ r ⟧c → CB r′
  CBp->CBnothingp {r} CBr r′dc
   with sym (unplugc r′dc) | BVFVcorrect _ _ _ CBr
  ... | refl | refl , refl
   = CBpar CBnothing CBr
      (basealwaysdistinct (BVars r))
      (basealwaysdistinct (BVars r))
      (basealwaysdistinct (FVars r))
      (λ { _ () _ })

  calc : ∀ p q ->
    CB p -> done q -> p ≡ₑ q # [] ->
    (nothin ∥ p) ≡ₑ p # []
  calc p q CBp doneq p≡ₑq =
    ≡ₑtran {r = (nothin ∥ q)}
           (≡ₑctxt (dcpar₂ dchole)
                   (dcpar₂ dchole)
                   (≡ₑ-context [] _ CBp->CBnothingp p≡ₑq))
           (≡ₑtran {r = q} (ex11-pq q [] doneq) (≡ₑsymm CBp p≡ₑq))

{- although true, can no longer prove
{- an emit (first) in sequence is the same as emit in parallel -}
ex12 : ∀ S p q ->
      CB p -> done q -> p ≡ₑ q # [] ->
      (signl S ((emit S) ∥ p)) ≡ₑ (signl S ((emit S) >> p)) # []
ex12 S p q CBp doneq p≡ₑq = calc where

  θS→unk : Env
  θS→unk = Θ SigMap.[ S ↦ Signal.unknown ] [] []

  S∈θS→unk : SigMap.∈Dom S (sig θS→unk)
  S∈θS→unk = sig-∈-single S Signal.unknown

  θS→unk[S]≡unk : sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.unknown
  θS→unk[S]≡unk = sig-stats-1map' S Signal.unknown S∈θS→unk

  θS→pre : Env
  θS→pre = set-sig{S} θS→unk S∈θS→unk Signal.present

  θS→unk[S]≠absent : ¬ sig-stats{S = S} θS→unk S∈θS→unk ≡ Signal.absent
  θS→unk[S]≠absent is rewrite θS→unk[S]≡unk = unknotabs is where
    unknotabs : Signal.unknown ≡ Signal.absent → ⊥
    unknotabs ()

  CBp->CBρθSp : {r r′ : Term}
      {BVp FVp : Σ (List ℕ) (λ x → List ℕ × List ℕ)} →
      CorrectBinding r BVp FVp → r′ ≐ cenv θS→pre ∷ [] ⟦ r ⟧c → CB r′
  CBp->CBρθSp {r} CBr r′dc
    with sym (unplugc r′dc) | BVFVcorrect _ _ _ CBr
  ... | refl | refl , refl = CBρ CBr

  basealwaysdistinct : ∀ x -> distinct base x
  basealwaysdistinct x = (λ { z () x₂ }) , (λ { z () x₂ }) , (λ { z () x₂})

  CBsiglSemitS>>p : CB (signl S (emit S >> p))
  CBsiglSemitS>>p =
    CBsig (CBseq CBemit CBp (basealwaysdistinct (FVars p)))

  calc : (signl S ((emit S) ∥ p)) ≡ₑ
         (signl S ((emit S) >> p)) # []
  calc =
     ≡ₑtran {r = ρ θS→unk · ((emit S) ∥ p)}
            (≡ₑstep [raise-signal])
    (≡ₑtran {r =  ρ θS→pre · (nothin ∥ p)}
            (≡ₑstep ([emit] S∈θS→unk θS→unk[S]≠absent (depar₁ dehole)))
    (≡ₑtran {r = ρ θS→pre · p}
            (≡ₑctxt (dcenv dchole) (dcenv dchole)
                    (≡ₑ-context [] (cenv _ ∷ [])
                                CBp->CBρθSp
                                (ex11 p q CBp doneq p≡ₑq)))
    (≡ₑsymm CBsiglSemitS>>p
            (≡ₑtran {r = ρ θS→unk · (emit S >> p)}
                    (≡ₑstep [raise-signal])
            (≡ₑtran {r = ρ θS→pre · nothin >> p}
                    (≡ₑstep ([emit] S∈θS→unk θS→unk[S]≠absent (deseq dehole)))
                    (≡ₑctxt (dcenv dchole) (dcenv dchole)
                            (≡ₑstep [seq-done])))))))

-}


{-# OPTIONS --rewriting --confluence-check #-}

open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite

postulate
  A : Set
  a b : A

  f : (X : Set) → X → A
  g : (X : Set) → X

  rewf₁ : f (A → A) (λ _ → a) ≡ a
  rewf₂ : (X : Set) → f X (g X) ≡ b

  rewg : (x : A) → g (A → A) x ≡ a

test = f (A → A) (λ x → g (A → A) x)

{-# REWRITE rewf₁ rewg #-}

foo : test ≡ a
foo = refl

{-# REWRITE rewf₂ #-}

bar : test ≡ b
bar = refl

fails : a ≡ b
fails = refl


module Function.Axioms where

open import Lang.Instance
open import Logic.Predicate
open import Logic
import      Lvl
import      Structure.Function.Names as Names
open import Structure.Setoid
open import Type

private variable ℓₒ ℓₒ₁ ℓₒ₂ ℓₒ₃ ℓₗ ℓₗ₁ ℓₗ₂ ℓₗ₃ : Lvl.Level

module _
  {A : Type{ℓₒ₁}}
  {B : Type{ℓₒ₂}} ⦃ _ : Equiv{ℓₗ₂}(B) ⦄
  ⦃ _ : Equiv{ℓₗ₃}(A → B) ⦄
  (f g : A → B)
  where

  record FunctionExtensionalityOn : Stmt{ℓₒ₁ Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓₗ₃} where
    constructor intro
    field proof : Names.FunctionExtensionalityOn(f)(g)
  functionExtensionalityOn = inst-fn FunctionExtensionalityOn.proof

  record FunctionApplicationOn : Stmt{ℓₒ₁ Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓₗ₃} where
    constructor intro
    field proof : Names.FunctionApplicationOn(f)(g)
  functionApplicationOn = inst-fn FunctionApplicationOn.proof

module _ (A : Type{ℓₒ₁}) (B : Type{ℓₒ₂}) ⦃ _ : Equiv{ℓₗ₂}(B) ⦄ ⦃ _ : Equiv{ℓₗ₃}(A → B) ⦄ where
  FunctionExtensionality : Stmt
  FunctionExtensionality = ∀²(FunctionExtensionalityOn{ℓₒ₁}{ℓₒ₂}{ℓₗ₂}{ℓₗ₃}{A}{B})
  functionExtensionality : ⦃ funcExt : FunctionExtensionality ⦄ → Names.FunctionExtensionality(A)(B)
  functionExtensionality {f}{g} = functionExtensionalityOn f g

  FunctionApplication : Stmt
  FunctionApplication = ∀²(FunctionApplicationOn{ℓₒ₁}{ℓₒ₂}{ℓₗ₂}{ℓₗ₃}{A}{B})
  functionApplication : ⦃ funcApp : FunctionApplication ⦄ → Names.FunctionApplication(A)(B)
  functionApplication {f}{g} = functionApplicationOn f g

module _
  {A : Type{ℓₒ₁}}
  {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄
  ⦃ _ : Equiv{ℓₗ₃}((a : A) → B(a)) ⦄
  (f g : ((a : A) → B(a)))
  where

  record DependentFunctionExtensionalityOn : Stmt{ℓₒ₁ Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓₗ₃} where
    constructor intro
    field proof : Names.DependentFunctionExtensionalityOn(f)(g)
  dependentFunctionExtensionalityOn = inst-fn DependentFunctionExtensionalityOn.proof

module _ (A : Type{ℓₒ₁}) (B : A → Type{ℓₒ₂}) ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄ ⦃ _ : Equiv{ℓₗ₃}((a : A) → B(a)) ⦄ where
  DependentFunctionExtensionality : Stmt
  DependentFunctionExtensionality = ∀²(DependentFunctionExtensionalityOn{ℓₒ₁}{ℓₒ₂}{ℓₗ₂}{ℓₗ₃}{A}{B})
  dependentFunctionExtensionality : ⦃ depFuncExt : DependentFunctionExtensionality ⦄ → Names.DependentFunctionExtensionality(A)(B)
  dependentFunctionExtensionality {f}{g} = dependentFunctionExtensionalityOn f g

module _
  {A : Type{ℓₒ₁}}
  {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄
  ⦃ _ : Equiv{ℓₗ₃}({a : A} → B(a)) ⦄
  (f g : ({a : A} → B(a)))
  where

  record DependentImplicitFunctionExtensionalityOn : Stmt{ℓₒ₁ Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓₗ₃} where
    constructor intro
    field proof : Names.DependentImplicitFunctionExtensionalityOn(f)(g)
  dependentImplicitFunctionExtensionalityOn = inst-fn DependentImplicitFunctionExtensionalityOn.proof

module _ (A : Type{ℓₒ₁}) (B : A → Type{ℓₒ₂}) ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄ ⦃ _ : Equiv{ℓₗ₃}({a : A} → B(a)) ⦄ where
  DependentImplicitFunctionExtensionality : Stmt
  DependentImplicitFunctionExtensionality = ∀²(DependentImplicitFunctionExtensionalityOn{ℓₒ₁}{ℓₒ₂}{ℓₗ₂}{ℓₗ₃}{A}{B})
  dependentImplicitFunctionExtensionality : ⦃ depFuncExt : DependentImplicitFunctionExtensionality ⦄ → Names.DependentImplicitFunctionExtensionality(A)(B)
  dependentImplicitFunctionExtensionality {f}{g} = dependentImplicitFunctionExtensionalityOn f g

module _
  {A : Type{ℓₒ₁}}
  {B : A → Type{ℓₒ₂}} ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄
  ⦃ _ : Equiv{ℓₗ₃}(⦃ a : A ⦄ → B(a)) ⦄
  (f g : (⦃ a : A ⦄ → B(a)))
  where

  record DependentInstanceFunctionExtensionalityOn : Stmt{ℓₒ₁ Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓₗ₃} where
    constructor intro
    field proof : Names.DependentInstanceFunctionExtensionalityOn(f)(g)
  dependentInstanceFunctionExtensionalityOn = inst-fn DependentInstanceFunctionExtensionalityOn.proof

module _ (A : Type{ℓₒ₁}) (B : A → Type{ℓₒ₂}) ⦃ _ : ∀{a} → Equiv{ℓₗ₂}(B(a)) ⦄ ⦃ _ : Equiv{ℓₗ₃}(⦃ a : A ⦄ → B(a)) ⦄ where
  DependentInstanceFunctionExtensionality : Stmt
  DependentInstanceFunctionExtensionality = ∀²(DependentInstanceFunctionExtensionalityOn{ℓₒ₁}{ℓₒ₂}{ℓₗ₂}{ℓₗ₃}{A}{B})
  dependentInstanceFunctionExtensionality : ⦃ depFuncExt : DependentInstanceFunctionExtensionality ⦄ → Names.DependentInstanceFunctionExtensionality(A)(B)
  dependentInstanceFunctionExtensionality {f}{g} = dependentInstanceFunctionExtensionalityOn f g