\import Category.CartesianClosed
\import Category.Functor
\import Equiv
\import Function.Meta
\import Logic
\import Order.Lattice
\import Order.PartialOrder
\import Paths

\class HeytingAlebra \extends BoundedDistributiveLattice {
  | \infixr 5 --> : E -> E -> E
  | <=-curry {x a b : E} : x  a <= b -> x <= a --> b
  | <=-uncurry {x a b : E} : x <= a --> b -> x  a <= b

  \default top \as top-impl => bottom --> bottom
  \default top-univ {x : E} : x <= top-impl => <=-curry meet-right
  \default ldistr>= {x y z : E} : x  (y  z) <= (x  y)  (x  z) => meet-comm =<= <=-uncurry (join-univ (<=-curry (meet-comm =<= join-left)) (<=-curry (meet-comm =<= join-right)))

  \lemma <=-eval {x y : E} : (x --> y)  x <= y
    => <=-uncurry <=-refl

  \lemma modus-ponens {x y : E} : x  (x --> y) <= y
    => meet-comm =<= <=-eval

  \lemma composition-law {x y z : E} : (x --> y)  (y --> z) <= x --> z
    => <=-curry $ (meet-assoc *> meet-comm *> meet-assoc) =<= meet-monotone <=-refl modus-ponens <=∘ <=-eval

  \func continuation (y : E) (x : E) => (x --> y) --> y

  \func neg (x : E) => x --> bottom

  \lemma neg-inverse {x y : E} (p : x <= y) : neg y <= neg x
    => <=-curry $ meet-monotone <=-refl p <=∘ <=-eval

  \lemma neg_bottom : neg bottom = top
    => <=-antisymmetric TopMeetSemilattice.top-univ (<=-curry meet-right)

  \lemma neg_top : neg top = bottom
    => <=-antisymmetric (meet-univ <=-refl TopMeetSemilattice.top-univ <=∘ <=-eval) bottom-univ

  \lemma id<=neg_neg {x : E} : x <= neg (neg x)
    => double-exponent-increasing

  \lemma neg_join {x y : E} : neg (x  y) = neg x  neg y
    => exponent_join

  \func IsNegated (x : E) : \Prop
    => neg (neg x) <= x

  \lemma bottom-negated : IsNegated bottom
    => =_<= $ pmap neg neg_bottom *> neg_top

  \lemma meet-negated {x y : E} (xn : IsNegated x) (yn : IsNegated y) : IsNegated (x  y)
    => meet-univ (double-exponent-monotone meet-left <=∘ xn) (double-exponent-monotone meet-right <=∘ yn)

  \lemma neg-negated {x : E} : IsNegated (neg x)
    => exponent-monotone double-exponent-increasing <=-refl

  \protected \func curry {x y z : E} : x  y --> z = x --> (y --> z)
    => <=-antisymmetric (<=-curry $ <=-curry $ meet-assoc =<= <=-eval) (<=-curry $ inv meet-assoc =<= meet-monotone <=-eval <=-refl <=∘ <=-eval)

  \lemma top_exponent {x y : E} (p : x <= y) : top <= x --> y
    => <=-curry (meet-right <=∘ p)

  \lemma exponent-monotone {x y x' y' : E} (p : x' <= x) (q : y <= y') : x --> y <= x' --> y'
    => <=-curry $ meet-monotone <=-refl p <=∘ <=-eval <=∘ q

  \lemma exponent_meet {x y z : E} : x --> y  z = (x --> y)  (x --> z)
    => <=-antisymmetric (meet-univ (exponent-monotone <=-refl meet-left) (exponent-monotone <=-refl meet-right)) (<=-curry (meet-univ (meet-monotone meet-left <=-refl <=∘ <=-eval) (meet-monotone meet-right <=-refl <=∘ <=-eval)))

  \lemma exponent_join {x y z : E} : x  y --> z = (x --> z)  (y --> z)
    => <=-antisymmetric (meet-univ (exponent-monotone join-left <=-refl) (exponent-monotone join-right <=-refl)) $ <=-curry $
        ldistr>= <=∘ join-univ (meet-monotone meet-left <=-refl <=∘ <=-eval) (meet-monotone meet-right <=-refl <=∘ <=-eval)

  \lemma double-exponent-increasing {x y : E} : x <= (x --> y) --> y
    => <=-curry modus-ponens

  \lemma double-exponent-idempotent {x y : E} : (((x --> y) --> y) --> y) --> y <= (x --> y) --> y
    => <=-curry (meet-monotone <=-refl double-exponent-increasing <=∘ <=-eval)

  \lemma double-exponent-monotone {x y z : E} (p : y <= z) : continuation x y <= continuation x z
    => <=-curry (meet-monotone <=-refl (exponent-monotone p <=-refl) <=∘ <=-eval)

  \lemma exponent-unit {x y : E} : x <= y --> x  y
    => <=-curry <=-refl

  \func exponent-functor (x : E) : Functor \this \this \cowith
    | F y => x --> y
    | Func f => exponent-monotone <=-refl f
    | Func-id => idp
    | Func-o => idp

  \func toCCC : CartesianClosedPrecat \cowith
    | CartesianPrecat => \this
    | CHom => -->
    | CHom-eval => <=-eval
    | CHom-univ => inP \new QEquiv {
      | ret => <=-curry
      | ret_f p => prop-pi
      | f_sec p => prop-pi
    }
}