{-# OPTIONS --universe-polymorphism #-}
-- vim: iskeyword=a-z,A-Z,48-57,-,+,',#,/,~,],[,=,_,`,⊆
module NaPa where

import Level as L
open import Irrelevance
import Relation.Binary.PropositionalEquality.NP as 
open  using (_≡_; _≢_; _≗_; module ≗-Reasoning; module ≡-Reasoning)
open import Data.Nat.NP as Nat using (; zero; suc; s≤s; z≤n; ≤-pred; pred; _<=_; module <=)
                            renaming (_+_ to _+ℕ_ ; _∸_ to _∸ℕ_ ; _==_ to _==ℕ_; ¬≤ to ¬≤ℕ;
                                      _<_ to _<ℕ_ ; _≤_ to _≤ℕ_; _≥_ to _≥ℕ_; _≤?_ to _≤?ℕ_)
open import Data.Nat.Logical using (⟦ℕ⟧; zero; suc; ⟦ℕ⟧-setoid; ⟦ℕ⟧-equality; ⟦ℕ⟧-cong)
                          renaming (_≟_ to _≟⟦ℕ⟧_)
import Data.Nat.Properties as Nat
open import Function
open import Function.Equality as ⟶≡ using (_⟶_; _⟨$⟩_)
open import Data.Sum.NP as Sum using (_⊎_; inj₁; inj₂; [_,_]′) renaming (map to ⊎-map)
open import Data.Bool.Extras using (Bool; true; false; if_then_else_; T; not; T'not'¬)
open import Data.Unit using ()
open import Data.Empty using (; ⊥-elim)
open import Data.Product.Extras using (_,_)
open import Data.Maybe.NP using (Maybe; nothing; just; maybe; _→?_)
open import Relation.Binary.Extras as Bin
open import Relation.Nullary
open import Relation.Nullary.Decidable
import Relation.Binary.On.NP as On
open import NomPa.Worlds
open import NomPa.Worlds.Syntax as WorldSyntax
open import NomPa.Subtyping
open ListBoolWorlds public hiding (_⊆_) renaming (_⊆′_ to _⊆_)
import Category.Monad as Cat
open Cat.RawMonad (Data.Maybe.NP.monad {L.zero})
open Data.Maybe.NP.FunctorLemmas

private
  module ℕs = Setoid ⟦ℕ⟧-setoid
  module ℕe = Equality ⟦ℕ⟧-equality

infixr 4 _,_
record Name α : Set where
  constructor _,_
  field
    name    : 
    name∈α : name  α

open Name public

mkName :  {α} (x : )  x  α  Name α
mkName x pf = x , pf

infix 4 _≡ᴺ_ _≟ᴺ_ _==ᴺ_

-- prim
_≡ᴺ_ :  {α} (x y : Name α)  Set
_≡ᴺ_ = ⟦ℕ⟧ on name

-- prim
_≟ᴺ_ :  {α}  Decidable (_≡ᴺ_ {α})
x ≟ᴺ y = name x ≟⟦ℕ⟧ name y

-- prim
_==ᴺ_ :  {α} (x y : Name α)  Bool
_==ᴺ_ x y = name x ==ℕ name y

name-injective :  {α} {x y : Name α}  name x  name y  x  y
name-injective {α} {_ , p₁} {a , p₂} eq rewrite eq
--= ≡.refl {_} {_} {a , p₂}
  = .cong (_,_ a) (∈-uniq α p₁ p₂)

Nm : World  Setoid L.zero L.zero
Nm α = On.setoid (name {α}) ℕs.isEquivalence

≡ᴺ-equality :  {α}  Equality (_≡ᴺ_ {α})
≡ᴺ-equality {α}
   = record { isEquivalence = Setoid.isEquivalence (Nm α)
            ; subst = λ P  .subst P  name-injective  Equality.to-propositional ⟦ℕ⟧-equality }

module NmEq {α} = Equality (≡ᴺ-equality {α})

≡ᴺ⇒≡ :  {α}  _≡ᴺ_ {α}  _≡_
≡ᴺ⇒≡ = NmEq.to-propositional

≡ᴺ-cong :  {α β} (f : Name α  Name β)  _≡ᴺ_ =[ f ]⇒ _≡ᴺ_
≡ᴺ-cong f = NmEq.reflexive  .cong f  ≡ᴺ⇒≡

≡ᴺ-⟦ℕ⟧-cong :  {α} (f : Name α  )  _≡ᴺ_ =[ f ]⇒ ⟦ℕ⟧
≡ᴺ-⟦ℕ⟧-cong f = ℕe.reflexive  .cong f  ≡ᴺ⇒≡

≡-⟦ℕ⟧-cong :  {a} {A : Set a} (f : A  )  _≡_ =[ f ]⇒ ⟦ℕ⟧
≡-⟦ℕ⟧-cong f = ℕe.reflexive  .cong f

≡ᴺ-≡-cong :  {a α} {A : Set a} (f : Name α  A)  _≡ᴺ_ =[ f ]⇒ _≡_
≡ᴺ-≡-cong f = .cong f  ≡ᴺ⇒≡

⟦ℕ⟧-≡ᴺ-cong :  {α} (f :   Name α)  ⟦ℕ⟧ =[ f ]⇒ _≡ᴺ_
⟦ℕ⟧-≡ᴺ-cong f = NmEq.reflexive  .cong f  ℕe.to-propositional

⟦ℕ⟧-≡-cong :  {a} {A : Set a} (f :   A)  ⟦ℕ⟧ =[ f ]⇒ _≡_
⟦ℕ⟧-≡-cong f = .cong f  ℕe.to-propositional

module ≡ᴺ-Reasoning {α} = Bin.Setoid-Reasoning (Nm α)

open WorldSymantics listBoolWorlds public
open WorldOps listBoolWorlds public
open WorldOps listBoolWorlds using ()
open NomPa.Subtyping.⊆-Pack ⊆′ᵇ-pack public hiding (_⊆_; _⊈_)
open NomPa.Subtyping.SyntacticOnBools ⊆′ᵇ-pack public hiding (minimalSymantics)

-- Here is a first set of core primitives easy to define

-- prim
zeroᴺ :  {α}  Name (α ↑1)
zeroᴺ = 0 , _

-- prim
¬Nameø : ¬(Name ø)
¬Nameø (_ , ())

-- prim
addᴺ :  {α} k (x : Name α)  Name (α +ᵂ k)
addᴺ {α} k x = k +ℕ name x , proof k where
  proof :  k  k +ℕ name x  α +ᵂ k
  proof zero    = name∈α x
  proof (suc k) = proof k

infixl 6 addᴺ
syntax addᴺ k x = x +ᴺ k

-- prim
subtractᴺ :  {α} k (x : Name (α +ᵂ k))  Name α
subtractᴺ k x = name x ∸ℕ k , proof (name x) k (name∈α x) where
  proof :  {α} x k  (x  α +ᵂ k)  (x ∸ℕ k  α)
  proof x       zero = id
  proof zero    (suc k) = λ()
  proof (suc x) (suc k) = proof x k

infixl 4 subtractᴺ
syntax subtractᴺ k x = x ∸ᴺ k

-- prim
coerceᴺ :  {α β}  α  β  Name α  Name β
coerceᴺ α⊆β x = name x , coe α⊆β (name x) (name∈α x)

-- Then we define some convenience functions on top of them.

infix 0 _⟨-because_-⟩
_⟨-because_-⟩ :  {α β}  Name α  α  β  Name β
_⟨-because_-⟩ n pf = coerceᴺ pf n

addᴺ↑ :  {α}   Name α  Name (α  )
addᴺ↑  x = addᴺ  x  ⟨-because ⊆-+-↑  -⟩

sucᴺ :  {α}  Name α  Name (α +1)
sucᴺ = addᴺ 1

sucᴺ↑ :  {α}  Name α  Name (α ↑1)
sucᴺ↑ = addᴺ↑ 1

-- Then comes <ᴺ and the necessary intermediate definitions

private
  lem-<ℓ :  {α β x }  x ≤ℕ   x  (α  suc )  x  (β  suc )
  lem-<ℓ z≤n       = _
  lem-<ℓ (s≤s m≤n) = lem-<ℓ m≤n

  lem-≥ℓ :  {α x }  x ≥ℕ   x  α    x  α +ᵂ 
  lem-≥ℓ z≤n        = id
  lem-≥ℓ (s≤s m≤n)  = lem-≥ℓ m≤n

  lem-<ℓ? :  {α β n}   n  β    n  (if suc n <=  then α   else β +ᵂ )
  lem-<ℓ? {n = n}  with suc n <=  | <=.sound (suc n)  | <=.complete {suc n} {}
  ... | true  | n<ℓ | _    = lem-<ℓ (n<ℓ _)
  ... | false | _   | ¬n<ℓ = lem-≥ℓ (¬≤ℕ ¬n<ℓ)

-- prim
cmpᴺ-bool :  {α}   Name (α  )  Bool
cmpᴺ-bool  x = suc (name x) <= 

syntax cmpᴺ-bool k x = x <ᴺ-bool k

-- prim
cmpᴺ-name :  {α}  (x : Name (α  ))  Name (if cmpᴺ-bool  x then ø   else α +ᵂ )
cmpᴺ-name  x = name x , lem-<ℓ?  (name∈α x)

syntax cmpᴺ-name ℓ x = x <ᴺ-name ℓ

-- prim
cmpᴺ-name∈α :  {α}  (x : Name (α  ))  name x  (if cmpᴺ-bool  x then ø   else α +ᵂ )
cmpᴺ-name∈α  x = lem-<ℓ?  (name∈α x)

syntax cmpᴺ-name∈α ℓ x = x <ᴺ-name∈α ℓ

-- prim
cmpᴺ :  {α}   Name (α  )  Name (ø  )  Name (α +ᵂ )
cmpᴺ  x with cmpᴺ-bool  x | cmpᴺ-name∈α  x
... | true  | pf = inj₁ (name x , pf)
... | false | pf = inj₂ (name x , pf)

infix 4 cmpᴺ
syntax cmpᴺ k x = x <ᴺ k

cmpᴺ-ind :  {a α}  (x : Name (α  ))
            (P : (Name (ø  )  Name (α +ᵂ ))  Set a)
            (Pinj₁ : T (cmpᴺ-bool  x)   pf  P (inj₁ (name x , pf)))
            (Pinj₂ : T (not (cmpᴺ-bool  x))   pf  P (inj₂ (name x , pf)))
           P (cmpᴺ  x)
cmpᴺ-ind  x P p₁ p₂
 with cmpᴺ-bool  x | cmpᴺ-name∈α  x
... | true         | pf = p₁ _ pf
... | false        | pf = p₂ _ pf

easy-cmpᴺ :  {α}   Name (α  )  Name (ø  )  Name (α +ᵂ )
easy-cmpᴺ zero x               = inj₂ x
easy-cmpᴺ (suc _) (zero , _)   = inj₁ (zero , _)
easy-cmpᴺ (suc ) (suc x , pf) = ⊎-map sucᴺ↑ sucᴺ (easy-cmpᴺ  (x , pf))

syntax easy-cmpᴺ ℓ x = x <ᴺ-easy ℓ

easy-cmpᴺ≗cmpᴺ :  {α}   easy-cmpᴺ {α}   cmpᴺ 
easy-cmpᴺ≗cmpᴺ zero x = .cong inj₂ (name-injective .refl)
easy-cmpᴺ≗cmpᴺ (suc n) (zero , _) = .refl
easy-cmpᴺ≗cmpᴺ {α} (suc n) (suc x , pf) rewrite easy-cmpᴺ≗cmpᴺ n (x , pf) = helper n x pf
  where
    helper :   x pf  ⊎-map sucᴺ↑ sucᴺ (cmpᴺ {α}  (x , pf))  cmpᴺ (suc ) (suc x , pf)
    helper  x pf = cmpᴺ-ind  (x , pf)  r  ⊎-map sucᴺ↑ sucᴺ r  cmpᴺ (suc ) (suc x , pf))
                       h pf'  cmpᴺ-ind (suc ) (suc x , pf)  r  inj₁ (suc x , pf')  r)
                                          _ _  .cong inj₁ (name-injective .refl))
                                          h'  ⊥-elim (T'not'¬ h' h)))
                       h pf'  cmpᴺ-ind (suc ) (suc x , pf)  r  inj₂ (suc x , pf')  r)
                                          h'  ⊥-elim (T'not'¬ h h'))
                                          _ _  .cong inj₂ (name-injective .refl)))

-- prim (could be defined with (cmpᴺ 1)
protect↑1 :  {α β}  (Name α  Name β)  Name (α ↑1)  Name (β ↑1)
protect↑1 f x with name x | name∈α x
... | zero  | _  = zero , _
... | suc m | pf = sucᴺ↑ (f (m , pf))

-- mkName; _,_; name; name∈α are forbidden from now on (except in proofs).

zeroᴺ↑1+ :  {α}   Name (α  (1 +ℕ ))
zeroᴺ↑1+ _ = zeroᴺ

infix 10 _ᴺ
_ᴺ :  {α}   Name (α  suc )
_ᴺ {α}  = zeroᴺ +ᴺ  ⟨-because (α ↑1 +ᵂ ) ⊆⟨ ⊆-+-↑  
                                α ↑1      ⊆⟨ ⊆-exch-↑-↑ ⊆-refl 1  ⟩∎
                                α  suc   -⟩
   where open ⊆-Reasoning

-- Handy name eliminator
subtractWithᴺ :  {a α} {A : Set a}   A  (Name α  A)  Name (α  )  A
subtractWithᴺ  v f x = [ const v , f ∘′ subtractᴺ  ]′ (x <ᴺ )

subtractᴺ? :  {α}   Name (α  ) →? Name α
subtractᴺ?  = subtractWithᴺ  nothing just

infixl 4 subtractᴺ?
syntax subtractᴺ? k x = x ∸ᴺ? k

private
 module CoeExamples where
  Nameα+→Nameα↑ :  {α} k  Name (α +ᵂ k)  Name (α  k)
  Nameα+→Nameα↑ k = coerceᴺ (⊆-ctx-+↑ ⊆-refl k)
 module Unused where
  open import Data.List using ([]; _∷_; replicate; _++_)
  n∈trueⁿ⁺¹ :  {α} n  n  replicate (suc n) true ++ α
  n∈trueⁿ⁺¹ zero    = _
  n∈trueⁿ⁺¹ (suc n) = n∈trueⁿ⁺¹ n

predᴺ :  {α}  Name (α +1)  Name α
predᴺ = subtractᴺ 1

predᴺ? :  {α}  Name (α ↑1) →? Name α
predᴺ? = subtractᴺ? 1

Name→-to-Nm⟶ :  {b₁ b₂ α} {B : Setoid b₁ b₂} 
                 (Name α  Setoid.Carrier B)  Nm α  B
Name→-to-Nm⟶ {α = α} {B} f = record { _⟨$⟩_ = f; cong = cong′ }
  where
  open Setoid B renaming (_≈_ to _≈B_)

  cong≡ :  {x y}  x  y  f x ≈B f y
  cong≡ .refl = Setoid.refl B

  cong′ :  {x y}  x ≡ᴺ y  f x ≈B f y
  cong′ = cong≡  Equality.to-propositional ≡ᴺ-equality

¬Name :  {α}  α  ø  ¬(Name α)
¬Name α⊆ø = ¬Nameø  coerceᴺ α⊆ø

Name-elim :  {a} {A : Set a} {α} (α⊆ø : α  ø) (x : Name α)  A
Name-elim x y with ¬Name x y
... | ()

Nameø-elim :  {a} {A : Set a}  Name ø  A
Nameø-elim = Name-elim ⊆-refl

¬Nameø+ :  k  ¬(Name (ø +ᵂ k))
¬Nameø+ = ¬Name  ⊆-ø+

Nameø+-elim :  {a} {A : Set a} k  Name (ø +ᵂ k)  A
Nameø+-elim = Name-elim  ⊆-ø+

Nameø↑→Nameα↑ :  {}  Name (ø  )   {α}  Name (α  )
Nameø↑→Nameα↑ {} x = x ⟨-because ⊆-cong-↑ ⊆-ø  -⟩

is0? :  {α}  Name (α ↑1)  Bool
is0? = cmpᴺ-bool 1
-- is0? = [ const true , const false ]′ (x <ᴺ 1)

shiftℕ :  ( k n : )  
shiftℕ  k n = if suc n <=  then n else n +ℕ k

shiftᴺ :  {α β}  k  α +ᵂ k  β  Name (α  )  Name (β  )
shiftᴺ  k pf n
  with n <ᴺ 
...  | inj₁ n′ = n′       ⟨-because ⊆-cong-↑ ⊆-ø  -⟩
...  | inj₂ n′ = n′ +ᴺ k  ⟨-because ⊆-trans (⊆-exch-+-+ ⊆-refl  k) (⊆-ctx-+↑ pf ) -⟩

module Singletons where

  SWorld :   World
  SWorld n = ø ↑1 +ᵂ n

  Nameˢ :   Set
  Nameˢ = Name  SWorld

   :  n  Nameˢ n
   n = zeroᴺ +ᴺ n

  addˢ :  {n} k  Nameˢ n  Nameˢ (k +ℕ n)
  addˢ {n} k x = addᴺ k x  ⟨-because ⊆-assoc-+ ⊆-refl n k -⟩

  subtractˢ :  {n} k  Nameˢ (k +ℕ n)  Nameˢ n
  subtractˢ {n} k x = subtractᴺ k (x ⟨-because ⊆-assoc-+′ ⊆-refl n k -⟩)

-- Properties

coerceᴺ-names :  {α β} (α⊆β : α  β)  name  coerceᴺ α⊆β  name
coerceᴺ-names _ _ = .refl

<ᴺ-names :  {α } {x : Name (α  )}  [ name , name ]′ (x <ᴺ )  name x
<ᴺ-names {α} {} {x} with x <ᴺ-bool  | x <ᴺ-name∈α 
... | true  | _ = .refl
... | false | _ = .refl

Dec-≡ᴺ→≡ :  {α}  Decidable (_≡ᴺ_ {α})  Decidable _≡_
Dec-≡ᴺ→≡ _≟_ x y with x  y
... | yes x≡y = yes (≡ᴺ⇒≡ x≡y)
... | no  x≢y = no  (x≢y ∘′ ℕs.reflexive ∘′ .cong name)

shiftᴺ-ℓ-0≡coerceᴺ :  {α β}  (pf : α  β) n  name (shiftᴺ  0 pf n)  name n
shiftᴺ-ℓ-0≡coerceᴺ  pf x with x <ᴺ-bool  | x <ᴺ-name∈α 
... | true  | _ = .refl
... | false | _ = .refl

pred-shiftℕ-comm :  k x  pred (shiftℕ 1 k (suc x))  x +ℕ k
pred-shiftℕ-comm k x = .refl

.shift11-predᴺ?-comm :
   {α β} (pf : α +1  β) (x : Name (α ↑1)) 
  predᴺ? (shiftᴺ 1 1 pf x)  coerceᴺ pf  sucᴺ <$> predᴺ? x
shift11-predᴺ?-comm {α} pf x
   = <$>-injective₁ name-injective (.trans (helper (name x) {name∈α x} (suc (name x) ≤?ℕ 1)) (<$>-assoc (predᴺ? x)))
  where
    -- getting the name unpacked (and the Dec), works around an Agda bug in 2.2.8.
    .helper :  n {pn : n  α ↑1}  Dec (n <ℕ 1)  let x = n , pn in
             name <$> (predᴺ? (shiftᴺ 1 1 pf x))  name  (coerceᴺ pf  sucᴺ) <$> predᴺ? x
    helper zero    (no ¬p)         = ⊥-elim (¬p (s≤s z≤n))
    helper .0      (yes (s≤s z≤n)) = .refl
    helper (suc _) (no _)          = .refl

1+ :  {α}  Name α  Name (α +1)
1+ x = x +ᴺ 1

module Name↑⇔Fin where
  open import Data.Fin as Fin

   :  n  Name (ø  n)  Fin n
   zero    = Nameø-elim
   (suc n) = maybe (suc ∘′  n) zero ∘′ predᴺ?

   :  {α n}  Fin n  Name (α  n)
   (zero {n})  = zeroᴺ↑1+ n
   (suc i)     = sucᴺ↑ ( i)

module ℕ⇒Name`α` where
  open WorldSyntax
  open WorldSyntax.El listBoolWorlds
   :  `α` x  x  El `α`  Name (El `α`)
   (`α` `↑1`) zero    _     = zero , _
   (`α` `↑1`) (suc i) pf    = sucᴺ↑ ( `α` i pf)
   (`α` `+1`) zero    ()
   (`α` `+1`) (suc i) pf    = sucᴺ ( `α` i pf)
   `ø`        _       ()

module Name`α`⇔Fin where
  open import Data.Fin as F renaming (_+_ to _+F_)
  open WorldSyntax
  open WorldSyntax.El listBoolWorlds
  open WorldSyntax.Properties listBoolWorlds
  mutual

    ⇒+1 :  `α`  Name (El `α` +1)  Fin (suc (bnd `α`))
    ⇒+1 `α` = F.raise 1   `α`  predᴺ

     :  `α`  Name (El `α`)  Fin (bnd `α`)
     (`α` `↑1`)  = [ inject+ _  Name↑⇔Fin. 1 , ⇒+1 `α` ]′  cmpᴺ 1
     (`α` `+1`)  = ⇒+1 `α`
     `ø`         = Nameø-elim

  ⇐₁ :  {n} `α`  (x : Fin n)  toℕ x  El `α`  Name (El `α`)
  ⇐₁ `α` = ℕ⇒Name`α`. `α`  toℕ

  x∈ø↑x :  {n} (x : Fin n)  toℕ x  (ø  n)
  x∈ø↑x zero    = _
  x∈ø↑x (suc n) = x∈ø↑x n

  ⇐₂ :  {n}  (x : Fin n)  Name (ø  n)
  ⇐₂ {n} x with x∈ø↑x x
  ...         | y       rewrite comm-El-↑ `ø` n = ⇐₁ (`ø` `↑` n) x y

module WellFormedWorld where
  data WF : World  Set where
     : WF ø
    _`+_ :  {α} ( : WF α) k  WF (α +ᵂ k)
    _`↑_ :  {α} ( : WF α) k  WF (α  k)

_+↑ᴺ_ :  {α}  Name α     Name (α  )
_+↑ᴺ_ x  = coerceᴺ (⊆-ctx-+↑ ⊆-refl ) (x +ᴺ )

subtractᴺ↑ :  {α}   Name (α  )  Name (ø  )  Name α
subtractᴺ↑  x with x <ᴺ 
... | inj₁ x′ = inj₁ x′
... | inj₂ x′ = inj₂ (x′ ∸ᴺ )

infixl 4 subtractᴺ↑
syntax subtractᴺ↑ ℓ x = x -↑ᴺ ℓ

data WithLim (R : (α β : World)  Set) : (α β : World)  Set where
  mkWithLim :  {α₀ β₀}  (Rα₀β₀ : R α₀ β₀)  WithLim R (α₀  ) (β₀  )

withLim :  {R α β}  R α β  WithLim R α β
withLim = mkWithLim 0

-- functions known to be ``bad'' and that we don't want to export
private
 module Bad where
  Name2ℕ :  {α}  Name α  
  Name2ℕ = name
  isZero? :  {α}  Name α  Bool
  isZero? (zero  , _) = true
  isZero? (suc _ , _) = false