\import Function.Meta
\import Order.HeytingAlgebra
\import Order.Lattice
\import Order.PartialOrder
\import Paths
\import Paths.Meta
\import Set.Subset

\class BooleanAlgebra \extends BoundedDistributiveLattice, HeytingAlebra {
  | compl : E -> E
  | compl-meet {a : E} : a  compl a <= bottom
  | compl-join {a : E} : top <= a  compl a

  | implies a b => compl a  b
  | exponent-left xa<=b => meet-univ <=-refl top-univ <=∘ meet-monotone <=-refl compl-join <=∘ ldistr>= <=∘ join-comm =<= join-monotone meet-right xa<=b
  | exponent-right x<=a-->b => meet-monotone x<=a-->b <=-refl <=∘ rdistr>= <=∘ join-univ (meet-comm =<= compl-meet <=∘ bottom-univ) meet-left

  \lemma compl-adj {a b : E} (ab : a  b <= bottom) : a <= compl b
    => meet-univ <=-refl top-univ <=∘ meet-monotone <=-refl compl-join <=∘ ldistr>= <=∘ join-univ (ab <=∘ bottom-univ) meet-right

  \lemma compl-mono {a b : E} (a<=b : a <= b) : compl b <= compl a
    => compl-adj $ meet-monotone <=-refl a<=b <=∘ meet-comm =<= compl-meet

  \lemma compl_meet {a b : E} : compl (a  b) = compl a  compl b
    => <=-antisymmetric (meet-univ <=-refl top-univ <=∘ meet-monotone <=-refl compl-join <=∘ ldistr>= <=∘ join-comm =<= join-monotone meet-right (compl-adj $ =_<= meet-assoc <=∘ meet-comm =<= compl-meet)) $ join-univ (compl-mono meet-left) (compl-mono meet-right)

  \lemma compl_join {a b : E} : compl (a  b) = compl a  compl b
    => <=-antisymmetric (meet-univ (compl-mono join-left) (compl-mono join-right)) $ compl-adj $ ldistr>= <=∘ join-univ (meet-monotone meet-left <=-refl <=∘ meet-comm =<= compl-meet) (meet-monotone meet-right <=-refl <=∘ meet-comm =<= compl-meet)

  \lemma compl-inv {a : E} : compl (compl a) = a
    => <=-antisymmetric (meet-univ <=-refl top-univ <=∘ meet-monotone <=-refl compl-join <=∘ ldistr>= <=∘ join-univ meet-right (meet-comm =<= compl-meet <=∘ bottom-univ)) (compl-adj compl-meet)

  \func symm-diff (a b : E) => a  compl b  b  compl a

  \lemma compl_symm-diff {a b : E} : compl (symm-diff a b) = a  b  compl a  compl b
    => compl_join *> pmap2 () compl_meet compl_meet *> rewrite (compl-inv,compl-inv) (<=-antisymmetric
        (ldistr>= <=∘ join-comm =<= join-monotone (rdistr>= <=∘ join-univ (=_<= meet-comm <=∘ compl-meet <=∘ bottom-univ) (=_<= meet-comm)) (rdistr>= <=∘ join-univ <=-refl (compl-meet <=∘ bottom-univ)))
        (join-univ (meet-comm =<= meet-monotone join-right join-right) (meet-monotone join-left join-left)))
}

\type NegatedElem (R : HeytingAlebra) => Set.Total R.IsNegated

\instance HeytingBooleanAlgebra (R : HeytingAlebra) : BooleanAlgebra (NegatedElem R)
  | <= a b => a.1 <= b.1
  | <=-refl => <=-refl
  | <=-transitive => <=∘
  | <=-antisymmetric p q => ext (<=-antisymmetric p q)
  | meet a b => (a.1  b.1, meet-negated a.2 b.2)
  | meet-left => meet-left
  | meet-right => meet-right
  | meet-univ => meet-univ
  | join a b => (neg $ neg $ a.1  b.1, neg-negated)
  | join-left => join-left <=∘ id<=neg_neg
  | join-right => join-right <=∘ id<=neg_neg
  | join-univ {x} {y} {z} p q => double-->-monotone (join-univ p q) <=∘ z.2
  | top => (R.top,R.top-univ)
  | top-univ => R.top-univ
  | bottom => (R.bottom,R.bottom-negated)
  | bottom-univ => R.bottom-univ
  | ldistr>= => rewrite (neg_join,neg_join) $ exponent-left $ (R.meet-comm *> inv R.meet-assoc) =<= R.meet-monotone (meet-univ (exponent-left $ R.meet-assoc =<= R.meet-monotone meet-left <=-refl <=∘ eval) (exponent-left $ R.meet-assoc =<= R.meet-monotone meet-right <=-refl <=∘ eval)) <=-refl <=∘ modus-ponens
  | compl a => (neg a.1, neg-negated)
  | compl-meet => modus-ponens
  | compl-join => exponent-left $ meet-right <=∘ neg_join =<= modus-ponens
  \where {
    \open HeytingAlebra
  }