module Cat.Cartesian.Solver where
module NbE
  {o β„“} {C : Precategory o β„“}
  (term : Terminal C)
  (prod : Binary-products C) where
  open Cat.Reasoning C
  open Binary-products prod
  open Terminal term

ExpressionsπŸ”—

  data β€œOb” : Type (o βŠ” β„“) where
    β€œtop” : β€œOb”
    _β€œβŠ—β€_ : β€œOb” β†’ β€œOb” β†’ β€œOb”
    β€œ_”   : Ob β†’ β€œOb”

  sem-ob : β€œOb” β†’ Ob
  sem-ob β€œtop” = top
  sem-ob (X β€œβŠ—β€ Y) = sem-ob X βŠ—β‚€ sem-ob Y
  sem-ob β€œ X ” = X

  instance
    Brackets-β€œOb” : ⟦⟧-notation β€œOb”
    Brackets-β€œOb” .⟦⟧-notation.lvl = o
    Brackets-β€œOb” .⟦⟧-notation.Sem = Ob
    Brackets-β€œOb” .⟦⟧-notation.⟦_⟧ = sem-ob

  data β€œHom” : β€œOb” β†’ β€œOb” β†’ Type (o βŠ” β„“) where
    β€œid”    : βˆ€ {X} β†’ β€œHom” X X
    _β€œβˆ˜β€_   : βˆ€ {X Y Z} β†’ β€œHom” Y Z β†’ β€œHom” X Y β†’ β€œHom” X Z
    β€œ!”     : βˆ€ {X} β†’ β€œHom” X β€œtop”
    β€œΟ€β‚β€    : βˆ€ {X Y} β†’ β€œHom” (X β€œβŠ—β€ Y) X
    β€œΟ€β‚‚β€    : βˆ€ {X Y} β†’ β€œHom” (X β€œβŠ—β€ Y) Y
    β€œβŸ¨_,_βŸ©β€ : βˆ€ {X Y Z} β†’ β€œHom” X Y β†’ β€œHom” X Z β†’ β€œHom” X (Y β€œβŠ—β€ Z)
    β€œ_”     : βˆ€ {X Y} β†’ Hom ⟦ X ⟧ ⟦ Y ⟧ β†’ β€œHom” X Y

  sem-hom : βˆ€ {X Y} β†’ β€œHom” X Y β†’ Hom ⟦ X ⟧ ⟦ Y ⟧
  sem-hom β€œid” = id
  sem-hom (f β€œβˆ˜β€ g) = sem-hom f ∘ sem-hom g
  sem-hom β€œ!” = !
  sem-hom β€œΟ€β‚β€ = π₁
  sem-hom β€œΟ€β‚‚β€ = Ο€β‚‚
  sem-hom β€œβŸ¨ f , g βŸ©β€ = ⟨ sem-hom f , sem-hom g ⟩
  sem-hom β€œ f ” = f

  instance
    Brackets-β€œHom” : βˆ€ {X Y} β†’ ⟦⟧-notation (β€œHom” X Y)
    Brackets-β€œHom” .⟦⟧-notation.lvl = _
    Brackets-β€œHom” .⟦⟧-notation.Sem = _
    Brackets-β€œHom” .⟦⟧-notation.⟦_⟧ = sem-hom

ValuesπŸ”—

  data Value : β€œOb” β†’ β€œOb” β†’ Type (o βŠ” β„“) where
    vhom  : βˆ€ {X Y} β†’ Hom ⟦ X ⟧ ⟦ Y ⟧ β†’ Value X Y
    vbang : βˆ€ {X} β†’ Value X β€œtop”
    vpair : βˆ€ {X Y Z} β†’ Value X Y β†’ Value X Z β†’ Value X (Y β€œβŠ—β€ Z)
  vfst : βˆ€ {X Y Z} β†’ Value X (Y β€œβŠ—β€ Z) β†’ Value X Y
  vfst (vhom f) = vhom (π₁ ∘ f)
  vfst (vpair v1 v2) = v1

  vsnd : βˆ€ {X Y Z} β†’ Value X (Y β€œβŠ—β€ Z) β†’ Value X Z
  vsnd (vhom f) = vhom (Ο€β‚‚ ∘ f)
  vsnd (vpair v1 v2) = v2

  vid : βˆ€ {X} β†’ Value X X
  vid = vhom id

QuotationπŸ”—

  reflect : βˆ€ X Y β†’ Value X Y β†’ Hom ⟦ X ⟧ ⟦ Y ⟧
  reflect X β€œtop” v = !
  reflect X (Y β€œβŠ—β€ Z) v = ⟨ (reflect X Y (vfst v)) , reflect X Z (vsnd v) ⟩
  reflect X β€œ Y ” (vhom f) = f

EvaluationπŸ”—

  eval : βˆ€ {X Y Z} β†’ β€œHom” Y Z β†’ Value X Y β†’ Value X Z
  eval β€œid” v = v
  eval (e1 β€œβˆ˜β€ e2) v = eval e1 (eval e2 v)
  eval β€œ!” v = vbang
  eval β€œΟ€β‚β€ v = vfst v
  eval β€œΟ€β‚‚β€ v = vsnd v
  eval β€œβŸ¨ e1 , e2 βŸ©β€ v = vpair (eval e1 v) (eval e2 v)
  eval β€œ f ” v = vhom (f ∘ reflect _ _ v)
  nf : βˆ€ X Y β†’ β€œHom” X Y β†’ Hom ⟦ X ⟧ ⟦ Y ⟧
  nf X Y e = reflect X Y (eval e vid)

SoundnessπŸ”—

  vhom-sound : βˆ€ X Y β†’ (f : Hom ⟦ X ⟧ ⟦ Y ⟧) β†’ reflect X Y (vhom f) ≑ f
  vhom-sound X β€œtop” f = !-unique f
  vhom-sound X (Y β€œβŠ—β€ Z) f =
    ⟨ reflect X Y (vhom (π₁ ∘ f)) , reflect X Z (vhom (Ο€β‚‚ ∘ f)) ⟩ β‰‘βŸ¨ apβ‚‚ ⟨_,_⟩ (vhom-sound X Y (π₁ ∘ f)) (vhom-sound X Z (Ο€β‚‚ ∘ f)) ⟩
    ⟨ π₁ ∘ f , Ο€β‚‚ ∘ f ⟩                                           β‰‘Λ˜βŸ¨ ⟨⟩-unique refl refl ⟩
    f                                                             ∎
  vhom-sound X β€œ Y ” f = refl
  vfst-sound : βˆ€ X Y Z β†’ (v : Value X (Y β€œβŠ—β€ Z)) β†’ reflect X Y (vfst v) ≑ π₁ ∘ reflect X (Y β€œβŠ—β€ Z) v
  vfst-sound X Y Z (vhom f) =
    reflect X Y (vhom (π₁ ∘ f))       β‰‘βŸ¨ vhom-sound X Y (π₁ ∘ f) ⟩
    π₁ ∘ f                            β‰‘Λ˜βŸ¨ refl⟩∘⟨ vhom-sound X (Y β€œβŠ—β€ Z) f ⟩
    π₁ ∘ reflect X (Y β€œβŠ—β€ Z) (vhom f) ∎
  vfst-sound X Y Z (vpair v1 v2) =
    reflect X Y v1                               β‰‘Λ˜βŸ¨ Ο€β‚βˆ˜βŸ¨βŸ© ⟩
    π₁ ∘ ⟨ (reflect X Y v1) , (reflect X Z v2) ⟩ ∎

  vsnd-sound : βˆ€ X Y Z β†’ (v : Value X (Y β€œβŠ—β€ Z)) β†’ reflect X Z (vsnd v) ≑ Ο€β‚‚ ∘ reflect X (Y β€œβŠ—β€ Z) v
  vsnd-sound X Y Z (vhom f) =
    reflect X Z (vhom (Ο€β‚‚ ∘ f))       β‰‘βŸ¨ vhom-sound X Z (Ο€β‚‚ ∘ f) ⟩
    Ο€β‚‚ ∘ f                            β‰‘Λ˜βŸ¨ refl⟩∘⟨ vhom-sound X (Y β€œβŠ—β€ Z) f ⟩
    Ο€β‚‚ ∘ reflect X (Y β€œβŠ—β€ Z) (vhom f) ∎
  vsnd-sound X Y Z (vpair v1 v2) =
    reflect X Z v2                               β‰‘Λ˜βŸ¨ Ο€β‚‚βˆ˜βŸ¨βŸ© ⟩
    Ο€β‚‚ ∘ ⟨ (reflect X Y v1) , (reflect X Z v2) ⟩ ∎
  sound-k : βˆ€ X Y Z β†’ (e : β€œHom” Y Z) β†’ (v : Value X Y)
          β†’ reflect X Z (eval e v) ≑ ⟦ e ⟧ ∘ reflect X Y v
  sound-k X Y Y β€œid” v = sym (idl _)
  sound-k X Y Z (e1 β€œβˆ˜β€ e2) v =
    reflect X Z (eval e1 (eval e2 v)) β‰‘βŸ¨ sound-k X _ Z e1 (eval e2 v) ⟩
    ⟦ e1 ⟧ ∘ reflect X _ (eval e2 v) β‰‘βŸ¨ refl⟩∘⟨ sound-k X Y _ e2 v ⟩
    ⟦ e1 ⟧ ∘ ⟦ e2 ⟧ ∘ reflect X Y v β‰‘βŸ¨ assoc _ _ _ ⟩
    ⟦ e1 β€œβˆ˜β€ e2 ⟧ ∘ reflect X Y v    ∎
  sound-k X Y β€œtop” β€œ!” v = !-unique (! ∘ reflect X Y v)
  sound-k X (Y β€œβŠ—β€ Z) Y β€œΟ€β‚β€ v = vfst-sound X Y Z v
  sound-k X (Y β€œβŠ—β€ Z) Z β€œΟ€β‚‚β€ v = vsnd-sound X Y Z v
  sound-k X Y (Z1 β€œβŠ—β€ Z2) β€œβŸ¨ e1 , e2 βŸ©β€ v =
    ⟨ reflect X Z1 (eval e1 v) , reflect X Z2 (eval e2 v) ⟩ β‰‘βŸ¨ apβ‚‚ ⟨_,_⟩ (sound-k X Y Z1 e1 v) (sound-k X Y Z2 e2 v) ⟩
    ⟨ ⟦ e1 ⟧ ∘ reflect X Y v , ⟦ e2 ⟧ ∘ reflect X Y v ⟩   β‰‘Λ˜βŸ¨ ⟨⟩∘ _ ⟩
    ⟨ ⟦ e1 ⟧ , ⟦ e2 ⟧ ⟩ ∘ reflect X Y v                   ∎
  sound-k X Y Z β€œ x ” v = vhom-sound X Z _
  sound : βˆ€ X Y β†’ (e : β€œHom” X Y) β†’ nf X Y e ≑ ⟦ e ⟧
  sound X Y e = sound-k X X Y e vid βˆ™ elimr (vhom-sound X X id)

Solver interfaceπŸ”—

  abstract
    solve : βˆ€ X Y β†’ (e1 e2 : β€œHom” X Y) β†’ nf X Y e1 ≑ nf X Y e2 β†’ ⟦ e1 ⟧ ≑ ⟦ e2 ⟧
    solve X Y e1 e2 p = sym (sound X Y e1) Β·Β· p Β·Β· sound X Y e2
module _
  {o β„“} {C : Precategory o β„“}
  {term : Terminal C}
  {prod : Binary-products C} where
  open Cat.Reasoning C
  open Binary-products prod
  open Terminal term
  open NbE term prod

  record Cartesian-ob (X : Ob) : Typeω where
    field
      β€œob” : β€œOb”
      ob-repr : ⟦ β€œob” ⟧ ≑ᡒ X

  β€œob” : (X : Ob) β†’ ⦃ β€œX” : Cartesian-ob X ⦄ β†’ β€œOb”
  β€œob” X ⦃ β€œX” ⦄ = Cartesian-ob.β€œob” β€œX”

  ob-repr : (X : Ob) β†’ ⦃ β€œX” : Cartesian-ob X ⦄ β†’ ⟦ β€œob” X ⟧ ≑ᡒ X
  ob-repr X ⦃ β€œX” ⦄ = Cartesian-ob.ob-repr β€œX”

  record Cartesian-hom
    {X Y : Ob}
    ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
    (f : Hom X Y) : Typeω where
    field
      β€œhom” : β€œHom” (β€œob” X) (β€œob” Y)

  β€œhom”
    : βˆ€ {X Y : Ob} β†’ (f : Hom X Y)
    β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
    β†’ ⦃ β€œf” : Cartesian-hom f ⦄
    β†’ β€œHom” (β€œob” X) (β€œob” Y)
  β€œhom” f ⦃ β€œf” = β€œf” ⦄ = Cartesian-hom.β€œhom” β€œf”

  instance
    Cartesian-ob-Default
      : βˆ€ {X} β†’ Cartesian-ob X
    Cartesian-ob-Default {X = X} .Cartesian-ob.β€œob” = NbE.β€œ X ”
    Cartesian-ob-Default .Cartesian-ob.ob-repr = reflα΅’
    {-# INCOHERENT Cartesian-ob-Default #-}

    Cartesian-ob-top
      : Cartesian-ob top
    Cartesian-ob-top .Cartesian-ob.β€œob” = β€œtop”
    Cartesian-ob-top .Cartesian-ob.ob-repr = reflα΅’

    Cartesian-ob-βŠ—β‚€
      : βˆ€ {X Y}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
      β†’ Cartesian-ob (X βŠ—β‚€ Y)
    Cartesian-ob-βŠ—β‚€ {X = X} {Y = Y} .Cartesian-ob.β€œob” =
      β€œob” X β€œβŠ—β€ β€œob” Y
    Cartesian-ob-βŠ—β‚€ {X = X} {Y = Y} .Cartesian-ob.ob-repr =
      apβ‚‚α΅’ _βŠ—β‚€_ (ob-repr X) (ob-repr Y)

    Cartesian-hom-Default
      : βˆ€ {X Y} {f : Hom X Y}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
      β†’ Cartesian-hom f
    Cartesian-hom-Default {X = X} {Y = Y} {f = f} .Cartesian-hom.β€œhom” =
      β€œ substα΅’ (Ξ» X β†’ Hom X ⟦ β€œob” Y ⟧) (symα΅’ (ob-repr X)) (substα΅’ (Ξ» Y β†’ Hom X Y) (symα΅’ (ob-repr Y)) f) ”
    {-# INCOHERENT Cartesian-hom-Default #-}

    Cartesian-hom-id
      : βˆ€ {X}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄
      β†’ Cartesian-hom (id {X})
    Cartesian-hom-id {X = X} .Cartesian-hom.β€œhom” = β€œid” {X = β€œob” X}

    Cartesian-hom-∘
      : βˆ€ {X Y Z} {f : Hom Y Z} {g : Hom X Y}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄ ⦃ β€œZ” : Cartesian-ob Z ⦄
      β†’ ⦃ β€œf” : Cartesian-hom f ⦄ ⦃ β€œg” : Cartesian-hom g ⦄
      β†’ Cartesian-hom (f ∘ g)
    Cartesian-hom-∘ {f = f} {g = g} .Cartesian-hom.β€œhom” = β€œhom” f β€œβˆ˜β€ β€œhom” g

    Cartesian-hom-!
      : βˆ€ {X}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄
      β†’ Cartesian-hom (! {X})
    Cartesian-hom-! .Cartesian-hom.β€œhom” = β€œ!”

    Cartesian-hom-π₁
      : βˆ€ {X Y}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
      β†’ Cartesian-hom (π₁ {X} {Y})
    Cartesian-hom-π₁ .Cartesian-hom.β€œhom” = β€œΟ€β‚β€

    Cartesian-hom-Ο€β‚‚
      : βˆ€ {X Y}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
      β†’ Cartesian-hom (Ο€β‚‚ {X} {Y})
    Cartesian-hom-Ο€β‚‚ .Cartesian-hom.β€œhom” = β€œΟ€β‚‚β€

    Cartesian-hom-⟨⟩
      : βˆ€ {X Y Z} {f : Hom X Y} {g : Hom X Z}
      β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄ ⦃ β€œZ” : Cartesian-ob Z ⦄
      β†’ ⦃ β€œf” : Cartesian-hom f ⦄ ⦃ β€œg” : Cartesian-hom g ⦄
      β†’ Cartesian-hom ⟨ f , g ⟩
    Cartesian-hom-⟨⟩ {f = f} {g = g} .Cartesian-hom.β€œhom” = β€œβŸ¨ β€œhom” f , β€œhom” g βŸ©β€

abstract
  solve-cartesian
    : βˆ€ {o h} {C : Precategory o h} (term : Terminal C) (prod : Binary-products C)
    β†’ (let open Precategory C) (let open NbE term prod)
    β†’ βˆ€ {X Y}
    β†’ (f g : Hom X Y)
    β†’ ⦃ β€œX” : Cartesian-ob X ⦄ ⦃ β€œY” : Cartesian-ob Y ⦄
    β†’ ⦃ β€œf” : Cartesian-hom f ⦄ ⦃ β€œg” : Cartesian-hom g ⦄
    β†’ nf (β€œob” X) (β€œob” Y) (β€œhom” f) ≑ nf (β€œob” X) (β€œob” Y) (β€œhom” g)
    β†’ ⟦ β€œhom” f ⟧ ≑ ⟦ β€œhom” g ⟧
  solve-cartesian term prod {X = X} {Y = Y} f g p =
    sym (NbE.sound term prod (β€œob” X) (β€œob” Y) (β€œhom” f))
    Β·Β· p
    Β·Β· NbE.sound term prod (β€œob” X) (β€œob” Y) (β€œhom” g)

macro
  cartesian!
    : βˆ€ {o β„“} {C : Precategory o β„“}
    β†’ Terminal C
    β†’ Binary-products C
    β†’ Term β†’ TC ⊀
  cartesian! term prod hole =
    withNormalisation false $ do
    goal ← infer-type hole >>= reduce
    just (lhs , rhs) ← get-boundary goal
      where nothing β†’ typeError $ strErr "Can't determine boundary: " ∷
                                  termErr goal ∷ []
    β€œterm” ← quoteTC term
    β€œprod” ← quoteTC prod
    unify hole (def (quote solve-cartesian) (β€œterm” v∷ β€œprod” v∷ lhs v∷ rhs v∷ β€œrefl” v∷ []))