module NCILL.Examples.Equivalences where

open import NCILL

-- All equivalences in Girard's paper (applicable to ILL)
-- are possible in NCILL, except commutativity of ⊗

-- Linear equivalence
------------------------------------------------------------------------

-- | Linear equivalence: A ⊢ B and B ⊢ A are provable
record _o-o_ (A B : Ty) : Set where
  field
    lfwd : [ A ]  B
    lbwd : [ B ]  A

infix 6 _o-o_

o-o-refl :  A  A o-o A
o-o-refl _ = record { lfwd = var ; lbwd = var }

o-o-trans :  A B C  A o-o B  B o-o C  A o-o C
o-o-trans _ _ _ record { lfwd = ab ; lbwd = ba } record { lfwd = bc ; lbwd = cb } = record
  { lfwd = cut [] ab bc
  ; lbwd = cut [] cb ba
  }

-- ♯1 and ⊗
------------------------------------------------------------------------

⊗-left-identity :  A  ♯1  A o-o A
⊗-left-identity _ = record
  { lfwd = times-L [] (unit-L [] var)
  ; lbwd = times-R unit-R var
  }

⊗-right-identity :  A  A  ♯1 o-o A
⊗-right-identity _ = record
  { lfwd = times-L [] (unit-L [ _ ] var)
  ; lbwd = times-R var unit-R
  }

⊗-assoc :  A B C  (A  B)  C o-o A  B  C
⊗-assoc _ _ _ = record
  { lfwd = times-L [] (times-L [] (times-R var (times-R var var)))
  ; lbwd = times-L [] (times-L [ _ ] (times-R (times-R var var) var))
  }

-- Note: ⊗ is not commutative!

-- ♯0 and ⊕
------------------------------------------------------------------------

⊕-left-identity :  A  ♯0  A o-o A
⊕-left-identity _ = record
  { lfwd = plus-L [] (zero-L []) var
  ; lbwd = plus-R₂ var
  }

⊕-right-identity :  A  A  ♯0 o-o A
⊕-right-identity _ = record
  { lfwd = plus-L [] var (zero-L [])
  ; lbwd = plus-R₁ var
  }

⊕-assoc :  A B C  (A  B)  C o-o A  B  C
⊕-assoc _ _ _ = record
  { lfwd = plus-L [] (plus-L [] (plus-R₁ var) (plus-R₂ (plus-R₁ var))) (plus-R₂ (plus-R₂ var))
  ; lbwd = plus-L [] (plus-R₁ (plus-R₁ var)) (plus-L [] (plus-R₁ (plus-R₂ var)) (plus-R₂ var))
  }

⊕-comm :  A B  A  B o-o B  A
⊕-comm _ _ = record
  { lfwd = plus-L [] (plus-R₂ var) (plus-R₁ var)
  ; lbwd = plus-L [] (plus-R₂ var) (plus-R₁ var)
  }

-- not an isomorphism!
⊕-idemp :  A  A  A o-o A
⊕-idemp _ = record
  { lfwd = plus-L [] var var
  ; lbwd = plus-R₁ var -- could be plus-R₂ too
  }

-- ♯⊤ and &
------------------------------------------------------------------------

&-left-identity :  A  ♯⊤ & A o-o A
&-left-identity _ = record
  { lfwd = with-L₂ [] var
  ; lbwd = with-R top-R var
  }

&-right-identity :  A  A & ♯⊤ o-o A
&-right-identity _ = record
  { lfwd = with-L₁ [] var
  ; lbwd = with-R var top-R
  }

&-assoc :  A B C  (A & B) & C o-o A & B & C
&-assoc _ _ _ = record
  { lfwd = with-R
    (with-L₁ [] (with-L₁ [] var))
    (with-R
      (with-L₁ [] (with-L₂ [] var))
      (with-L₂ [] var))
  ; lbwd = with-R
    (with-R
      (with-L₁ [] var)
      (with-L₂ [] (with-L₁ [] var)))
    (with-L₂ [] (with-L₂ [] var))
  }

&-comm :  A B  A & B o-o B & A
&-comm _ _ = record
  { lfwd = with-R (with-L₂ [] var) (with-L₁ [] var)
  ; lbwd = with-R (with-L₂ [] var) (with-L₁ [] var)
  }

-- not an isomorphism!
&-idemp :  A  A & A o-o A
&-idemp _ = record
  { lfwd = with-L₁ [] var
  ; lbwd = with-R var var
  }

-- Distributivity
------------------------------------------------------------------------

⊗-⊕-left-distr :  A B C  A  (B  C) o-o (A  B)  (A  C)
⊗-⊕-left-distr _ _ _ = record
  { lfwd = times-L [] (plus-L [ _ ]
    (plus-R₁ (times-R var var))
    (plus-R₂ (times-R var var)))
  ; lbwd = plus-L []
    (times-L [] (times-R var (plus-R₁ var)))
    (times-L [] (times-R var (plus-R₂ var)))
  }
  
⊗-⊕-right-distr :  A B C  (A  B)  C o-o (A  C)  (B  C)
⊗-⊕-right-distr _ _ _ = record
  { lfwd = times-L [] (plus-L []
    (plus-R₁ (times-R var var))
    (plus-R₂ (times-R var var)))
  ; lbwd = plus-L []
    (times-L [] (times-R (plus-R₁ var) var))
    (times-L [] (times-R (plus-R₂ var) var))
  }

left-zero :  A  ♯0  A o-o ♯0
left-zero _ = record
  { lfwd = times-L [] (zero-L [])
  ; lbwd = zero-L []
  }

right-zero :  A  A  ♯0 o-o ♯0
right-zero _ = record
  { lfwd = times-L [] (zero-L [ _ ])
  ; lbwd = zero-L []
  }

absurd :  A  ♯0 ⊸ʳ A o-o ♯⊤
absurd _ = record
  { lfwd = top-R
  ; lbwd = lam (zero-L (♯⊤  []))
  }

boring :  A  A ⊸ʳ ♯⊤ o-o ♯⊤
boring _ = record
  { lfwd = top-R
  ; lbwd = lam top-R
  }

distr-1 :  A B C  A  B ⊸ʳ C o-o (A ⊸ʳ C) & (B ⊸ʳ C)
distr-1 _ _ _ = record
  { lfwd = with-R
    (lam (app [] (plus-R₁ var) var))
    (lam (app [] (plus-R₂ var) var))
  ; lbwd = lam (plus-L [ _ ]
    (with-L₁ [] (app [] var var))
    (with-L₂ [] (app [] var var)))
  }

distr-2 :  A B C  A ⊸ʳ B & C o-o (A ⊸ʳ B) & (A ⊸ʳ C)
distr-2 _ _ _ = record
  { lfwd = with-R
    (lam (app [] var (with-L₁ [] var)))
    (lam (app [] var (with-L₂ [] var)))
  ; lbwd = lam (with-R
    (with-L₁ [] (app [] var var))
    (with-L₂ [] (app [] var var)))
  }

-- Half-distributivity
------------------------------------------------------------------------

-- This is interesting, why it works only in one direction!
half-distr-1 :  A B C  [ A  (B & C) ]  (A  B) & (A  C)
half-distr-1 _ _ _ = with-R
  (times-L [] (times-R var (with-L₁ [] var)))
  (times-L [] (times-R var (with-L₂ [] var)))

-- half-distr-1-rev : ∀ A B C → Sqnt [ (A ⊗ B) & (A ⊗ C) ] (A ⊗ (B & C)) 
-- half-distr-1-rev _ _ _ = ?

half-distr-2 :  A B C  [ (A ⊸ʳ C)  (B ⊸ʳ C) ]  A & B ⊸ʳ C
half-distr-2 _ _ _ = lam (plus-L []
  (app [] (with-L₁ [] var) var)
  (app [] (with-L₂ [] var) var))

half-distr-3 :  A B C  [ (A ⊸ʳ B)  (A ⊸ʳ C) ]  A ⊸ʳ B  C
half-distr-3 _ _ _ = lam (plus-L []
  (app [] var (plus-R₁ var))
  (app [] var (plus-R₂ var)) )

-- ⊗ and ⊸ are adjoint
------------------------------------------------------------------------

-- We need CUT in the forward direction
-- Also see how ⊸ "swaps" ⊗ halfs.
--
-- /This/ makes me think that I (and my references) have ⊸ and ⊸ʳ in the wrong way
--
curry :  A B C  B  A  C o-o A  B  C
curry _ _ _ = record
  { lfwd = impl-R (impl-R (cut [] (times-R var var) (impl-L [] var var)))
  ; lbwd = impl-R (times-L [] (impl-L [ _ ] var (impl-L [] var var)))
  }

curryʳ :  A B C  A  B ⊸ʳ C o-o A ⊸ʳ B ⊸ʳ C
curryʳ _ _ _ = record
  { lfwd = impl-Rʳ (impl-Rʳ (cut [ _ ] (times-R var var) (impl-Lʳ [] var var)))
  ; lbwd = impl-Rʳ (times-L [ _ ] (impl-Lʳ [] var (impl-Lʳ [] var var)))
  }

-- Implication equivalence
------------------------------------------------------------------------

-- Note: this equivalence is weaker than o-o !

lin-equiv₁ :  A B  []  A  B  []  A ⊸ʳ B
lin-equiv₁ _ _ f = impl-Rʳ (cut [ _ ] f (impl-L [] var var))

lin-equiv₂ :  A B  []  A ⊸ʳ B  []  A  B
lin-equiv₂ _ _ f = impl-R (cut [] f (impl-Lʳ [] var var))