module Cat.Displayed.Instances.Subobjects.Reasoning
  {o β„“} {C : Precategory o β„“} (pb : has-pullbacks C) where

Subobjects in a cartesian categoryπŸ”—

_^*_ : (f : Hom X Y) (m : Subobject Y) β†’ Subobject X
f ^* m = pullback-subobject pb f m

^*-univ : ≀-over f m n β†’ m β‰€β‚˜ f ^* n
^*-univ = Cartesian-fibration.has-lift.universalv (Subobject-fibration pb) _ _

infixr 35 _^*_

^*-id : id ^* m β‰…β‚˜ m
^*-id .to       = Ix.^*-id-to
^*-id .from     = Ix.^*-id-from
^*-id .inverses = record { invl = prop! ; invr = prop! }

^*-assoc : f ^* (g ^* m) β‰…β‚˜ (g ∘ f) ^* m
^*-assoc .to       = Ix.^*-comp-from
^*-assoc .from     = Ix.^*-comp-to
^*-assoc .inverses = record { invl = prop! ; invr = prop! }

βŠ€β‚˜ : Subobject X
βŠ€β‚˜ .domain = _
βŠ€β‚˜ .map = id
βŠ€β‚˜ .monic = id-monic

opaque
  !β‚˜ : m β‰€β‚˜ βŠ€β‚˜
  !β‚˜ {m = m} = record { map = m .map ; sq = refl }

opaque
  _βˆ©β‚˜_ : Subobject X β†’ Subobject X β†’ Subobject X
  m βˆ©β‚˜ n = Sub-products pb m n .Product.apex

  infixr 30 _βˆ©β‚˜_

opaque
  unfolding _βˆ©β‚˜_

  βˆ©β‚˜β‰€l : m βˆ©β‚˜ n β‰€β‚˜ m
  βˆ©β‚˜β‰€l = Sub-products pb _ _ .Product.π₁

  βˆ©β‚˜β‰€r : m βˆ©β‚˜ n β‰€β‚˜ n
  βˆ©β‚˜β‰€r = Sub-products pb _ _ .Product.Ο€β‚‚

  βˆ©β‚˜-univ : βˆ€ {p} β†’ p β‰€β‚˜ m β†’ p β‰€β‚˜ n β†’ p β‰€β‚˜ m βˆ©β‚˜ n
  βˆ©β‚˜-univ = Sub-products pb _ _ .Product.⟨_,_⟩

opaque
  βˆ©β‚˜-idl : βŠ€β‚˜ βˆ©β‚˜ m β‰…β‚˜ m
  βˆ©β‚˜-idl = Sub-antisym βˆ©β‚˜β‰€r (βˆ©β‚˜-univ !β‚˜ Sub.id)

  βˆ©β‚˜-idr : m βˆ©β‚˜ βŠ€β‚˜ β‰…β‚˜ m
  βˆ©β‚˜-idr = Sub-antisym βˆ©β‚˜β‰€l (βˆ©β‚˜-univ Sub.id !β‚˜)

  βˆ©β‚˜-assoc : l βˆ©β‚˜ m βˆ©β‚˜ n β‰…β‚˜ (l βˆ©β‚˜ m) βˆ©β‚˜ n
  βˆ©β‚˜-assoc = Sub-antisym
    (βˆ©β‚˜-univ (βˆ©β‚˜-univ βˆ©β‚˜β‰€l (βˆ©β‚˜β‰€l Sub.∘ βˆ©β‚˜β‰€r)) (βˆ©β‚˜β‰€r Sub.∘ βˆ©β‚˜β‰€r))
    (βˆ©β‚˜-univ (βˆ©β‚˜β‰€l Sub.∘ βˆ©β‚˜β‰€l) (βˆ©β‚˜-univ (βˆ©β‚˜β‰€r Sub.∘ βˆ©β‚˜β‰€l) βˆ©β‚˜β‰€r))

opaque
  unfolding _βˆ©β‚˜_

  ^*-βˆ©β‚˜ : f ^* (m βˆ©β‚˜ n) β‰…β‚˜ f ^* m βˆ©β‚˜ f ^* n
  ^*-βˆ©β‚˜ {f = f} {m = m} {n = n} = Sub-antisym
    (βˆ©β‚˜-univ
      (^*-univ record { sq = pb _ _ .square βˆ™ pullr refl })
      (^*-univ record { sq = pb _ _ .square βˆ™ pullr refl βˆ™ extendl (pb _ _ .square) }))
    record
      { map = pb _ _ .universal
        {p₁' = pb _ _ .p₁ ∘ pb _ _ .p₁}
        {pβ‚‚' = pb _ _ .universal {p₁' = pb _ _ .pβ‚‚ ∘ pb _ _ .p₁} {pβ‚‚' = pb _ _ .pβ‚‚ ∘ pb _ _ .pβ‚‚}
          (pulll (sym (pb _ _ .square)) βˆ™ pullr (pb _ _ .square) βˆ™ extendl (pb _ _ .square))}
        (sym (pullr (pb _ _ .pβ‚βˆ˜universal) βˆ™ extendl (sym (pb _ _ .square))))
      ; sq  = sym (pb _ _ .pβ‚βˆ˜universal βˆ™ introl refl)
      }

  ^*-βŠ€β‚˜ : f ^* βŠ€β‚˜ β‰…β‚˜ βŠ€β‚˜
  ^*-βŠ€β‚˜ {f = f} = Sub-antisym !β‚˜ record
    { map = pb _ _ .universal {p₁' = id} {pβ‚‚' = f} id-comm
    ; sq  = sym (pb _ _ .pβ‚βˆ˜universal βˆ™ introl refl)
    }

opaque
  is-pullback-alongβ†’iso : is-pullback-along C (m .map) h (n .map) β†’ m β‰…β‚˜ h ^* n
  is-pullback-along→iso pba = Sub-antisym
    record { map = pb _ _ .universal (pba .square) ; sq = sym (pb _ _ .pβ‚βˆ˜universal βˆ™ introl refl) }
    record { map = pba .universal (pb _ _ .square) ; sq = sym (pba .pβ‚βˆ˜universal βˆ™ introl refl) }

  isoβ†’is-pullback-along : m β‰…β‚˜ h ^* n β†’ is-pullback-along C (m .map) h (n .map)
  iso→is-pullback-along _ .top = _
  iso→is-pullback-along {h = h} {n = n} m .has-is-pb = subst-is-pullback
    (sym (m .Sub.to .sq) βˆ™ idl _) refl refl refl
    (is-pullback-iso
      record
        { to       = m .Sub.from .map
        ; from     = m .Sub.to .map
        ; inverses = record
          { invl = ap ≀-over.map (m .Sub.invr)
          ; invr = ap ≀-over.map (m .Sub.invl)
          }
        }
      (pb h (n .map) .has-is-pb))