open import 1Lab.Reflection.Signature
open import 1Lab.Path.IdentitySystem
open import 1Lab.Function.Embedding
open import 1Lab.Reflection.HLevel
open import 1Lab.Reflection.Subst
open import 1Lab.HLevel.Closure
open import 1Lab.Reflection
open import 1Lab.Truncation
open import 1Lab.Type.Sigma
open import 1Lab.Type.Pi
open import 1Lab.HLevel
open import 1Lab.Equiv
open import 1Lab.Path
open import 1Lab.Type
open import Data.Nat.Base
module 1Lab.Extensionality where
private variable
  ℓ ℓ' ℓ'' ℓr : Level
  A B C : Type ℓ
  P Q R : A → Type ℓ
record Extensional (A : Type ℓ) ℓ-rel : Type (ℓ ⊔ lsuc ℓ-rel) where
  no-eta-equality
  field
    Pathᵉ : A → A → Type ℓ-rel
    reflᵉ : ∀ x → Pathᵉ x x
    idsᵉ : is-identity-system Pathᵉ reflᵉ
{-# INLINE Extensional.constructor #-}
open Extensional using (Pathᵉ ; reflᵉ ; idsᵉ) public
instance
  Extensional-default : Extensional A (level-of A)
  Extensional-default .Pathᵉ   = _≡_
  Extensional-default .reflᵉ _ = refl
  Extensional-default .idsᵉ    = Path-identity-system
  
  
  
  {-# INCOHERENT Extensional-default #-}
  
  
  
  
  Extensional-Lift : ⦃ Extensional A ℓr ⦄ → Extensional (Lift ℓ' A) ℓr
  Extensional-Lift ⦃ sa ⦄ .Pathᵉ (lift x) (lift y) = sa .Pathᵉ x y
  Extensional-Lift ⦃ sa ⦄ .reflᵉ (lift x) = sa .reflᵉ x
  Extensional-Lift ⦃ sa ⦄ .idsᵉ .to-path p = ap lift (sa .idsᵉ .to-path p)
  Extensional-Lift ⦃ sa ⦄ .idsᵉ .to-path-over p = sa .idsᵉ .to-path-over p
  Extensional-Π
    : ⦃ ∀ {x} → Extensional (P x) ℓr ⦄
    → Extensional ((x : A) → P x) (level-of A ⊔ ℓr)
  Extensional-Π ⦃ sb ⦄ .Pathᵉ f g = ∀ x → Pathᵉ sb (f x) (g x)
  Extensional-Π ⦃ sb ⦄ .reflᵉ f x = reflᵉ sb (f x)
  Extensional-Π ⦃ sb ⦄ .idsᵉ .to-path h = funext λ i → sb .idsᵉ .to-path (h i)
  Extensional-Π ⦃ sb ⦄ .idsᵉ .to-path-over h = funextP λ i → sb .idsᵉ .to-path-over (h i)
  
  {-# OVERLAPPABLE Extensional-Π #-}
  Extensional-Π'
    : ⦃ ∀ {x} → Extensional (P x) ℓr ⦄
    → Extensional ({x : A} → P x) (level-of A ⊔ ℓr)
  Extensional-Π' ⦃ sb ⦄ .Pathᵉ f g = ∀ {x} → Pathᵉ (sb {x}) f g
  Extensional-Π' ⦃ sb ⦄ .reflᵉ f = reflᵉ sb f
  Extensional-Π' ⦃ sb ⦄ .idsᵉ .to-path h i = sb .idsᵉ .to-path h i
  Extensional-Π' ⦃ sb ⦄ .idsᵉ .to-path-over h i = sb .idsᵉ .to-path-over h i
  Extensional-Π''
    : ⦃ ∀ ⦃ x ⦄ → Extensional (P x) ℓr ⦄
    → Extensional (⦃ x : A ⦄ → P x) (level-of A ⊔ ℓr)
  Extensional-Π'' ⦃ sb ⦄ .Pathᵉ f g = ∀ ⦃ x ⦄ → Pathᵉ (sb ⦃ x ⦄) f g
  Extensional-Π'' ⦃ sb ⦄ .reflᵉ f = reflᵉ sb f
  Extensional-Π'' ⦃ sb ⦄ .idsᵉ .to-path h i = sb .idsᵉ .to-path h i
  Extensional-Π'' ⦃ sb ⦄ .idsᵉ .to-path-over h i = sb .idsᵉ .to-path-over h i
  
  
  
  
  
  
  
  
  
  
  Extensional-uncurry
    : ∀ {ℓ ℓ' ℓ'' ℓr} {A : Type ℓ} {B : A → Type ℓ'} {C : (x : A) → B x → Type ℓ''}
    → ⦃ sb : Extensional ((x : A) (y : B x) → C x y) ℓr ⦄
    → Extensional ((p : Σ A B) → C (p .fst) (p .snd)) ℓr
  Extensional-uncurry ⦃ sb ⦄ .Pathᵉ f g = sb .Pathᵉ (curry f) (curry g)
  Extensional-uncurry ⦃ sb ⦄ .reflᵉ f = sb .reflᵉ (curry f)
  Extensional-uncurry ⦃ sb = sb ⦄ .idsᵉ .to-path h i (a , b) = sb .idsᵉ .to-path h i a b
  Extensional-uncurry ⦃ sb = sb ⦄ .idsᵉ .to-path-over h = sb .idsᵉ .to-path-over h
  Extensional-lift-map
    : ∀ {ℓ ℓ' ℓ'' ℓr} {A : Type ℓ} {B : Lift ℓ' A → Type ℓ''}
    → ⦃ sa : Extensional ((x : A) → B (lift x)) ℓr ⦄
    → Extensional ((x : Lift ℓ' A) → B x) ℓr
  Extensional-lift-map ⦃ sa = sa ⦄ .Pathᵉ f g = sa .Pathᵉ (f ∘ lift) (g ∘ lift)
  Extensional-lift-map ⦃ sa = sa ⦄ .reflᵉ x = sa .reflᵉ (x ∘ lift)
  Extensional-lift-map ⦃ sa = sa ⦄ .idsᵉ .to-path h i (lift x) = sa .idsᵉ .to-path h i x
  Extensional-lift-map ⦃ sa = sa ⦄ .idsᵉ .to-path-over h = sa .idsᵉ  .to-path-over h
ext
  : ∀ {ℓ ℓr} {A : Type ℓ} {x y : A} ⦃ r : Extensional A ℓr ⦄
  → Pathᵉ r x y → x ≡ y
ext ⦃ r ⦄ p = r .idsᵉ .to-path p
private
  trivial-worker
    : ∀ {ℓ ℓr} {A : Type ℓ} (r : Extensional A ℓr)
    → (x y : A) → Term → TC ⊤
  trivial-worker r x y goal = try where
    error : ∀ {ℓ} {A : Type ℓ} → TC A
    
    
    
    
    try : TC ⊤
    try = do
      `r ← wait-for-type =<< quoteTC r
      `x ← quoteTC x
      unify goal (it reflᵉ ##ₙ `r ##ₙ `x) <|> error
    error = do
      `x ← quoteTC x
      `y ← quoteTC y
      typeError
        [ "trivial! failed: the values\n  "
        , termErr `x
        , "\nand\n  "
        , termErr `y
        , "\nare not extensionally equal by refl.\n"
        ]
opaque
  trivial!
    : ∀ {ℓ ℓr} {A : Type ℓ} {x y : A}
    → ⦃ r : Extensional A ℓr ⦄
    → {@(tactic trivial-worker r x y) p : Pathᵉ r x y}
    → x ≡ y
  trivial! ⦃ r ⦄ {p = p} = r .idsᵉ .to-path p
trivial-iso!
  : ∀ {ℓ ℓ' ℓr ℓs} {A : Type ℓ} {B : Type ℓ'}
  → ⦃ r : Extensional (A → A) ℓr ⦄
  → ⦃ s : Extensional (B → B) ℓs ⦄
  → (f : A → B)
  → (g : B → A)
  → {@(tactic trivial-worker r (g ∘ f) id) p : Pathᵉ r (g ∘ f) id}
  → {@(tactic trivial-worker s (f ∘ g) id) q : Pathᵉ s (f ∘ g) id}
  → Iso A B
trivial-iso! ⦃ r ⦄ ⦃ s ⦄ f g {p = p} {q = q} =
  f , iso g (happly (s .idsᵉ .to-path q)) (happly (r .idsᵉ .to-path p))
abstract
  unext : ∀ {ℓ ℓr} {A : Type ℓ} ⦃ e : Extensional A ℓr ⦄ {x y : A} → x ≡ y → e .Pathᵉ x y
  unext ⦃ e ⦄ {x = x} p = transport (λ i → e .Pathᵉ x (p i)) (e .reflᵉ x)
macro
  reext!
    : ∀ {ℓ ℓr} {A : Type ℓ} ⦃ ea : Extensional A ℓr ⦄ {x y : A}
    → x ≡ y → Term → TC ⊤
  reext! p goal = do
    `p ← quoteTC p
    unify goal (def (quote ext) [ argN (def (quote unext) [ argN `p ]) ])
Pathᵉ-is-hlevel
  : ∀ {ℓ ℓr} {A : Type ℓ} n (sa : Extensional A ℓr)
  → is-hlevel A (suc n)
  → ∀ {x y}
  → is-hlevel (Pathᵉ sa x y) n
Pathᵉ-is-hlevel n sa hl =
  Equiv→is-hlevel _ (identity-system-gives-path (sa .idsᵉ)) (Path-is-hlevel' _ hl _ _)
embedding→extensional
  : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
  → (f : A ↪ B)
  → Extensional B ℓr
  → Extensional A ℓr
{-# INLINE embedding→extensional #-}
embedding→extensional f ext = record where
  Pathᵉ x y = ext .Pathᵉ (f .fst x) (f .fst y)
  reflᵉ x   = ext .reflᵉ (f .fst x)
  idsᵉ      = pullback-identity-system (ext .idsᵉ) f
iso→extensional
  : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
  → Iso A B
  → Extensional B ℓr
  → Extensional A ℓr
{-# INLINE iso→extensional #-}
iso→extensional f ext = embedding→extensional (Iso→Embedding f) ext
injection→extensional
  : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
  → is-set B
  → {f : A → B}
  → (∀ {x y} → f x ≡ f y → x ≡ y)
  → Extensional B ℓr
  → Extensional A ℓr
{-# INLINE injection→extensional #-}
injection→extensional b-set {f} inj ext = record where
  Pathᵉ x y = ext .Pathᵉ (f x) (f y)
  reflᵉ x   = ext .reflᵉ (f x)
  idsᵉ = record where
    to-path x = inj (ext .idsᵉ .to-path x)
    to-path-over p = is-prop→pathp (λ i → Pathᵉ-is-hlevel 1 ext b-set) _ _
injection→extensional!
  : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
  → ⦃ _ : H-Level B 2 ⦄
  → {f : A → B}
  → (∀ {x y} → f x ≡ f y → x ≡ y)
  → Extensional B ℓr
  → Extensional A ℓr
{-# INLINE injection→extensional! #-}
injection→extensional! = injection→extensional (hlevel 2)
Σ-prop-extensional
  : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : A → Type ℓ'}
  → (∀ x → is-prop (B x))
  → Extensional A ℓr
  → Extensional (Σ A B) ℓr
{-# INLINE Σ-prop-extensional #-}
Σ-prop-extensional bprop = embedding→extensional (fst , Subset-proj-embedding bprop)
instance
  Extensional-Σ-trunc
    : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : A → Type ℓ'}
    → ⦃ ea : Extensional A ℓr ⦄ → Extensional (Σ A λ x → ∥ B x ∥) ℓr
  Extensional-Σ-trunc ⦃ ea ⦄ = Σ-prop-extensional (λ x → hlevel 1) ea
  Extensional-equiv
    : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
    → ⦃ ea : Extensional (A → B) ℓr ⦄ → Extensional (A ≃ B) ℓr
  Extensional-equiv ⦃ ea ⦄ = Σ-prop-extensional (λ x → hlevel 1) ea
  Extensional-emb
    : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
    → ⦃ ea : Extensional (A → B) ℓr ⦄ → Extensional (A ↪ B) ℓr
  Extensional-emb ⦃ ea ⦄ = Σ-prop-extensional (λ x → hlevel 1) ea
  Extensional-tr-map
    : ∀ {ℓ ℓ' ℓr} {A : Type ℓ} {B : Type ℓ'}
    → ⦃ bset : H-Level B 2 ⦄
    → ⦃ ea : Extensional (A → B) ℓr ⦄
    → Extensional (∥ A ∥ → B) ℓr
  Extensional-tr-map ⦃ ea = ea ⦄ =
    injection→extensional! {f = λ f → f ∘ inc}
      (λ p → funext $ ∥-∥-elim (λ _ → hlevel 1) (happly p)) ea
private module test where
  _ : {f g : A → B} → ((x : A) → f x ≡ g x) → f ≡ g
  _ = ext
  _ : {f g : A × B → C} → ((a : A) (b : B) → f (a , b) ≡ g (a , b)) → f ≡ g
  _ = ext
  _ : {f g : Σ A P → B} → ((a : A) (b : P a) → f (a , b) ≡ g (a , b)) → f ≡ g
  _ = ext
  open Lift
  _ : {f g : Σ (Lift ℓ' A) (λ x → Lift ℓ' (P (x .lower))) → Lift ℓ'' B}
    → ((a : A) (b : P a) → f (lift a , lift b) .lower ≡ g (lift a , lift b) .lower)
    → f ≡ g
  _ = ext
  _ : {f g : A → B ≃ C} → ((a : A) (b : B) → f a .fst b ≡ g a .fst b) → f ≡ g
  _ = ext