Derivative.Isolated.Grafting

{-# OPTIONS --safe #-}
module Derivative.Isolated.Grafting where

open import Derivative.Prelude
open import Derivative.Isolated.Base
open import Derivative.Basics.Decidable as Dec
open import Derivative.Remove

private
  variable
     : Level
    A B : Type 

-- This lets us define an elimination principle for a type `A` with some isolated point `a₀ : A`:
-- To give a section `(a : A) → B a`, it suffices to give `B a`, once for all `a` such that `a₀ ≡ a`,
-- and another time for all `a` such that `a₀ ≢ a`.
-- This principle is not valid for arbitrary pointed types!
-- ```agda
module _ {ℓB : Level} {B : A  Type ℓB}
  ((a₀ , a₀≟_) : A °)
  (eq* :  a  a₀  a  B a)
  (neq* : (a : A  a₀)  B (a .fst))
  where
  elimIsolated :  a  B a
  elimIsolated a = Dec.elim (eq* a)  neq  neq* (a , neq)) (a₀≟ a)

  elimIsolated-β-yes :  a  (p : a₀  a)  elimIsolated a  eq* a p
  elimIsolated-β-yes a p i = Dec.elim (eq* a)  neq  neq* (a , neq)) (isIsolated→isPropDecPath a₀ a₀≟_ a (a₀≟ a) (yes p) i)

  elimIsolated-β-refl : elimIsolated a₀  eq* a₀ refl
  elimIsolated-β-refl = elimIsolated-β-yes a₀ refl

  elimIsolated-β-no :  a  (¬p : a₀  a)  elimIsolated a  neq* (a , ¬p)
  elimIsolated-β-no a ¬p i = Dec.elim (eq* a)  neq  neq* (a , neq)) (isIsolated→isPropDecPath a₀ a₀≟_ a (a₀≟ a) (no ¬p) i)
-- ```

graft : ( : A °)  (((A   .fst)  B) × B)  (A  B)
graft  (f , b₀) = elimIsolated   _ _  b₀) f

graft-β-yes : ( : A °) (f : (A   .fst)  B) {b₀ : B}  graft  (f , b₀) ( .fst)  b₀
graft-β-yes  f {b₀} = elimIsolated-β-refl   _ _  b₀) f

graft-β-no : ( : A °) (f : (A   .fst)  B) {b₀ : B} (a : A   .fst)  graft  (f , b₀) (a .fst)  f a
graft-β-no  f {b₀} = uncurry $ elimIsolated-β-no   _ _  b₀) f

graft-eval : ( : A °)  (f : A  B) (b₀ : B)
   (p : b₀  f ( .fst))
   graft  (f  fst , b₀)  f
graft-eval  f b₀ p using (a₀ , a₀≟_) = funExt λ a  eval-dec a (a₀≟ a) module graft-eval where
  eval-dec :  a  Dec (a₀  a)  graft  (f  fst , b₀) a  f a
  eval-dec a (yes a₀≡a) = elimIsolated-β-yes   _ _  b₀) (f  fst) a a₀≡a ∙∙ p ∙∙ cong f a₀≡a
  eval-dec a (no ¬a₀≡a) = graft-β-no  (f  fst) (a , ¬a₀≡a)

graft-eval-yes-filler : ( : A °)  (f : A  B) (b₀ : B)
   (p : b₀  f ( .fst))
   Square (graft-β-yes  (f  fst) {b₀ = b₀}) (refl′ (f ( .fst))) (graft-eval  f b₀ p ≡$  .fst) p
graft-eval-yes-filler  f b₀ p using (a₀ , a₀≟_) = λ i j  eval-dec-filler (a₀≟ a₀) (~ j) i module graft-eval-yes-filler where
  open graft-eval  f b₀ p

  eval-dec-filler : (a₀≟a₀ : Dec (a₀  a₀))  Square p (eval-dec a₀ a₀≟a₀) (sym $ graft-β-yes  (f  fst) {b₀}) (refl′ (f a₀))
  eval-dec-filler (yes a₀≡a₀) = goal
    where
      filler : Square p (eval-dec a₀ (yes a₀≡a₀)) _ (cong f a₀≡a₀)
      filler = doubleCompPath-filler (elimIsolated-β-yes   _ _  b₀) (f  fst) a₀ a₀≡a₀) p (cong f a₀≡a₀)

      trivial-loop : a₀≡a₀  refl
      trivial-loop = isIsolated→K a₀ a₀≟_ a₀≡a₀

      adjust₁ : cong f a₀≡a₀  refl
      adjust₁ i j = f (trivial-loop i j)

      adjust₂ : elimIsolated-β-yes   _ _  b₀) (f  fst) a₀ a₀≡a₀  graft-β-yes  (f  fst) {b₀}
      adjust₂ = cong (elimIsolated-β-yes  _ (f  fst) a₀) trivial-loop

      goal : Square p (eval-dec a₀ (yes a₀≡a₀)) (sym $ graft-β-yes  (f  fst)) (refl′ (f a₀))
      goal = subst2  r s  Square p (eval-dec a₀ (yes a₀≡a₀)) (sym r) s) adjust₂ adjust₁ filler

  eval-dec-filler (no ¬a₀≡a₀) = ex-falso $ ¬a₀≡a₀ refl

ungraft : ( : A °)  (A  B)  (((A   .fst)  B) × B)
ungraft (a₀ , _) f .fst = f  fst
ungraft (a₀ , _) f .snd = f a₀

isEquiv-graft : ( : A °)  isEquiv (graft {B = B} )
isEquiv-graft {A} {B} @(a₀ , a₀≟_) = isoToIsEquiv graft-iso module isEquivgraft where

  graft-iso : Iso (((A  a₀)  B) × B) (A  B)
  graft-iso .Iso.fun = graft 
  graft-iso .Iso.inv = ungraft 
  graft-iso .Iso.rightInv f = funExt λ a  goal a (a₀≟ a) where
    goal : (a : A)  Dec (a₀  a)  graft  (ungraft  f) a  f a
    goal a (yes p) = elimIsolated-β-yes   _ _  f a₀) (f  fst) a p  cong f p
    goal a (no ¬p) = elimIsolated-β-no   _ _  f a₀) (f  fst) a ¬p
  graft-iso .Iso.leftInv ( , b) = ΣPathP λ where
    .fst  funExt λ (a , a₀≢a)  elimIsolated-β-no   _ _  b)  a a₀≢a
    .snd  elimIsolated-β-refl   _ _  b) 

graftEquiv : ( : A °)  ((A   .fst  B) × B)  (A  B)
graftEquiv  .fst = graft 
graftEquiv  .snd = isEquiv-graft 

ungraftEquiv : ( : A °)  (A  B)  ((A   .fst  B) × B)
ungraftEquiv  .fst = ungraft 
ungraftEquiv  .snd = isoToIsEquiv $ invIso $ isEquivgraft.graft-iso _ ( .snd)