Ayberk Tosun, 17 August 2023.

\begin{code}

{-# OPTIONS --safe --without-K --lossy-unification #-}

open import MLTT.List hiding ([_])
open import MLTT.Spartan
open import Slice.Family
open import UF.FunExt
open import UF.PropTrunc
open import UF.Size
open import UF.SubtypeClassifier

module Locales.Spectrality.BasisDirectification
        (pt : propositional-truncations-exist)
        (fe : Fun-Ext)
        (sr : Set-Replacement pt) where

open import Locales.Frame pt fe
open import Locales.SmallBasis pt fe sr

open import UF.Logic
open AllCombinators pt fe

open PropositionalTruncation pt

open Locale

\end{code}

Given a basis closed under binary meets, the directification of that basis using
the `directify` function is also closed under binary meets. The reason is that
the meets of joins can be turned into joins of meets. In this section, we prove
this fact.

To define CNF transformation, we need the following function
`conjunct-with-list` which takes some `x` and some list `y₁ , … , yₙ` and
computes `(x ∧ y₁) , … , (x ∧ yₙ)`.

\begin{code}

conjunct-with-list : (F : Frame 𝓤 𝓥 𝓦)
                     F   List  F   List  F 
conjunct-with-list F x = map  -  x ∧[ F ] -)

cnf-transform : (F : Frame 𝓤 𝓥 𝓦)
               List  F   List  F    F 
cnf-transform F []       ys = 𝟎[ F ]
cnf-transform F (x  xs) ys =
 (⋁ₗ[ F ] conjunct-with-list F x ys) ∨[ F ] cnf-transform F xs ys

\end{code}

Given some `x : ⟨ F ⟩` and a list `(y₁ , … , yₙ) : List ⟨ F ⟩`,
`x ∧ (y₁ ∨ … ∨ yₙ) = (x ∧ y₁) ∨ … ∨ (x ∧ yₙ)`, which is of course just an
instance of the distributivity law. We prove this fact next.

\begin{code}

distributivity-list : (F : Frame 𝓤 𝓥 𝓦) (x :  F ) (ys : List  F )
                     x ∧[ F ] (⋁ₗ[ F ] ys)  ⋁ₗ[ F ] (conjunct-with-list F x ys)
distributivity-list F x []       = 𝟎-right-annihilator-for-∧ F x
distributivity-list F x (y  ys) =
 x ∧[ F ] (y ∨[ F ] (⋁ₗ[ F ] ys))                         =⟨     
 (x ∧[ F ] y) ∨[ F ] (x ∧[ F ] (⋁ₗ[ F ] ys))              =⟨     
 (x ∧[ F ] y) ∨[ F ] (⋁ₗ[ F ] conjunct-with-list F x ys)  =⟨ refl 
 ⋁ₗ[ F ] (conjunct-with-list F x (y  ys))                
  where
    = binary-distributivity F x y (join-list F ys)
    = ap  -  (x ∧[ F ] y) ∨[ F ] -) (distributivity-list F x ys)

\end{code}

With `distributivity-list` in hand, we are ready to prove the correctness of the
CNF transformation procedure.

\begin{code}

cnf-transform-correct : (F : Frame 𝓤 𝓥 𝓦) (xs ys : List  F )
                       (⋁ₗ[ F ] xs) ∧[ F ] (⋁ₗ[ F ] ys)  cnf-transform F xs ys
cnf-transform-correct F []       ys = 𝟎-left-annihilator-for-∧ F ((⋁ₗ[ F ] ys))
cnf-transform-correct F (x  xs) ys =
 (x ∨[ F ] (⋁ₗ[ F ] xs)) ∧[ F ] (⋁ₗ[ F ] ys)                       =⟨     
 (x ∧[ F ] (⋁ₗ[ F ] ys)) ∨[ F ] ((⋁ₗ[ F ] xs) ∧[ F ] (⋁ₗ[ F ] ys)) =⟨     
 (x ∧[ F ] (⋁ₗ[ F ] ys)) ∨[ F ] cnf-transform F xs ys              =⟨     
 (⋁ₗ[ F ] conjunct-with-list F x ys) ∨[ F ] cnf-transform F xs ys  =⟨ refl 
 cnf-transform F (x  xs) ys                                       
  where
    = binary-distributivity-right F
    = ap
         -  (x ∧[ F ] (⋁ₗ[ F ] ys)) ∨[ F ] -)
        (cnf-transform-correct F xs ys)
    = ap  -  - ∨[ F ] cnf-transform F xs ys) (distributivity-list F x ys)

\end{code}

We now start proving, making use of `cnf-transform-correct`, that the CNF
transformation of two basic opens is itself basic.

We first prove the analogous fact that the `conjunct-with-list` function:

\begin{code}

conjunct-with-list-is-basic : (F : Frame 𝓤 𝓥 𝓦) ( : Fam 𝓦  F )
                             (β : is-basis-for F )
                             closed-under-binary-meets F  holds
                             let
                               ℬ↑ = directify F 
                               β↑ = directified-basis-is-basis F  β
                              in
                               (i : index ) (is : index ℬ↑) 
                                 ks  index ℬ↑ ,
                                  ℬ↑ [ ks ]
                                   ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> is))
conjunct-with-list-is-basic F  β p i []       =  [] , refl 
conjunct-with-list-is-basic F  β p i (j  js) = ∥∥-rec ∃-is-prop γ μ
 where
  open Meets  x y  x ≤[ poset-of F ] y)

  ℬ↑ = directify F 

\end{code}

We know by the closure of `ℬ` under binary meets that the meet of `ℬ[ i ]` and
`ℬ[ j ]` is in the basis, given by some index `k`.

\begin{code}

  μ :  k  index  , (( [ k ]) is-glb-of ( [ i ] ,  [ j ])) holds
  μ = p i j

\end{code}

We unpack this truncated sigma inside `γ`:

\begin{code}

  γ : Σ k  index  , (( [ k ]) is-glb-of ( [ i ] ,  [ j ])) holds
      ks  index ℬ↑ ,
       ℬ↑ [ ks ]  ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> (j  js)))
  γ (k , q) = ∥∥-rec ∃-is-prop δ IH
   where

\end{code}

Now, by the I.H. on `ℬ[ i ]`, we also get some `ks` corresponding to the index
of conjuncting `ℬ[ i ]` with each `ℬ[ j ]` given by `js.`

\begin{code}

    IH :  ks  index ℬ↑ ,
          ℬ↑ [ ks ]  ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> js))
    IH = conjunct-with-list-is-basic F  β p i js

\end{code}

Once again, we unpack this truncated sigma inside `δ`:

\begin{code}

    δ : Σ ks  index ℬ↑ ,
         ℬ↑ [ ks ]  ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> js))
        ls  index ℬ↑ ,
         ℬ↑ [ ls ]  ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> (j  js)))
    δ (ks , r) =  (k  ks) ,  
     where

\end{code}

The list of indices that we need for the desired result is then simply `k ∷ ks`.
The proof that this satisfies the desired property is given in `†` below.

\begin{code}

      w = ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> js))

       : ℬ↑ [ k  ks ]
           ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> (j  js)))
       =
        [ k ] ∨[ F ] ℬ↑ [ ks ]                                        =⟨     
        [ k ] ∨[ F ] w                                                =⟨     
       ( [ i ] ∧[ F ]  [ j ]) ∨[ F ] w                               =⟨ refl 
       ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> (j  js))) 
        where
          = ap  -   [ k ] ∨[ F ] -) r
          = ap  -  - ∨[ F ] w) (∧[ F ]-unique q)

\end{code}

We are now ready to prove the desired result: that the meet of two basic opens
is basic.

\begin{code}

cnf-transform-is-basic : (F : Frame 𝓤 𝓥 𝓦) ( : Fam 𝓦  F )
                        (β : is-basis-for F )
                        closed-under-binary-meets F  holds
                        let
                          ℬ↑ = directify F 
                          β↑ = directified-basis-is-basis F  β
                         in
                          (is js : index ℬ↑) 
                            ks  index ℬ↑ ,
                            ℬ↑ [ ks ]  (ℬ↑ [ is ]) ∧[ F ] (ℬ↑ [ js ])
cnf-transform-is-basic F  β p [] js =
  [] , (𝟎-left-annihilator-for-∧ F (directify F  [ js ]) ⁻¹) 
cnf-transform-is-basic F  β p (i  is) js =
 ∥∥-rec ∥∥-is-prop γ (cnf-transform-is-basic F  β p is js)
  where
   ℬ↑ = directify F 

\end{code}

We first record the following trivial `lemma`:

\begin{code}

   lemma : (is js : index ℬ↑)
          ℬ↑ [ is ] ∧[ F ] ℬ↑ [ js ]
            join-in-frame′ F  is ∧[ F ] join-in-frame′ F  js
   lemma is js =
    let
       = ap
            -  - ∧[ F ] ℬ↑ [ js ])
           (join-in-frame-equality F  is)
       = ap
            -  (⋁ₗ[ F ] (( [_]) <$> is)) ∧[ F ] -)
           (join-in-frame-equality F  js)
    in
     ℬ↑ [ is ] ∧[ F ] ℬ↑ [ js ]                                   =⟨  
     (⋁ₗ[ F ] (( [_]) <$> is)) ∧[ F ] ℬ↑ [ js ]                  =⟨  
     (⋁ₗ[ F ] (( [_]) <$> is)) ∧[ F ] (⋁ₗ[ F ] (( [_]) <$> js)) 

\end{code}

In `γ`, we unpack the truncated sigma given by the I.H.:

\begin{code}

   γ : Σ ks  index ℬ↑ , ℬ↑ [ ks ]  ℬ↑ [ is ] ∧[ F ] ℬ↑ [ js ]
       ks  index ℬ↑ , ℬ↑ [ ks ]  ( [ i ] ∨[ F ] ℬ↑ [ is ]) ∧[ F ] ℬ↑ [ js ]
   γ (ks , q) =
    let

\end{code}

We know by `conjunct-with-list-is-basic` that there is a basis index
corresponding to `conjunct-with-list (ℬ [ i ]) ((ℬ [_]) <$> js)`. We refer to
this as `ls`.

\begin{code}

      :  ls  index ℬ↑ ,
          ℬ↑ [ ls ]  ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> js))
      = conjunct-with-list-is-basic F  β p i js

    in

\end{code}

We proceed by truncation recursion on this truncated sigma, with the contents
unpacked in the function `δ`.

\begin{code}

     ∥∥-rec ∃-is-prop δ 
      where

\end{code}

As we will have to refer to `(ℬ [_]) <$> is` and `(ℬ [_]) <$> js` frequently,
we introduce abbrevations for them:

\begin{code}

       ℬ-is = ( [_]) <$> is
       ℬ-js = ( [_]) <$> js

\end{code}

Combining `lemma` and the correctness of `cnf-transform`, we have that `ℬ↑[ ks
]` is the CNF transformation of the meet of the joins of `is` and `js`.

\begin{code}

        : ℬ↑ [ ks ]  cnf-transform F (( [_]) <$> is) (( [_]) <$> js)
        =
        ℬ↑ [ ks ]                                           =⟨  
        ℬ↑ [ is ] ∧[ F ] ℬ↑ [ js ]                          =⟨  
        (⋁ₗ[ F ] ℬ-is) ∧[ F ] (⋁ₗ[ F ] (( [_]) <$> js))    =⟨  
        cnf-transform F ℬ-is (( [_]) <$> js)               
         where
           = q
           = lemma is js
           = cnf-transform-correct F ℬ-is ℬ-js

\end{code}

As `⋁ₗ[ F ] (conjunct-with-list F (ℬ [ i ]) ((ℬ [_]) <$> js))` is mentioned
quite frequently, we introduce the abbreviation `w` for it:

\begin{code}

       w = ⋁ₗ[ F ] (conjunct-with-list F ( [ i ]) (( [_]) <$> js))

\end{code}

The desired list of indices is just `ls ++ ks`:

\begin{code}

       δ : (Σ ls  index ℬ↑ , ℬ↑ [ ls ]  w)
           ms  index ℬ↑ ,
            ℬ↑ [ ms ]  ( [ i ] ∨[ F ] ℬ↑ [ is ]) ∧[ F ] ℬ↑ [ js ]
       δ (ls , r) =  (ls ++ ks) ,  
        where

\end{code}

\begin{code}

         : ℬ↑ [ ls ++ ks ]  ( [ i ] ∨[ F ] ℬ↑ [ is ]) ∧[ F ] ℬ↑ [ js ]
         =
         ℬ↑ [ ls ++ ks ]                                        =⟨     
         ℬ↑ [ ls ] ∨[ F ] ℬ↑ [ ks ]                             =⟨     
         w ∨[ F ] ℬ↑ [ ks ]                                     =⟨     
         w ∨[ F ] (cnf-transform F ℬ-is ℬ-js)                   =⟨ refl 
         cnf-transform F (( [_]) <$> (i  is)) ℬ-js            =⟨     
         (⋁ₗ[ F ] (( [_]) <$> (i  is))) ∧[ F ] (⋁ₗ[ F ] ℬ-js) =⟨     
         (ℬ↑ [ i  is ]) ∧[ F ] join-list F ℬ-js                =⟨     
         (ℬ↑ [ i  is ]) ∧[ F ] (ℬ↑ [ js ])                     =⟨ refl 
         ( [ i ] ∨[ F ] ℬ↑ [ is ]) ∧[ F ] (ℬ↑ [ js ])          
          where
            = directify-functorial F  ls ks
            = ap  -  - ∨[ F ] ℬ↑ [ ks ]) r
            = ap  -  w ∨[ F ] -) 
            = cnf-transform-correct F (( [_]) <$> (i  is)) ℬ-js ⁻¹
            = ap
                 -  - ∧[ F ] (⋁ₗ[ F ] ℬ-js))
                (join-in-frame-equality F  (i  is) ⁻¹)
            = ap
                 -  (ℬ↑ [ i  is ]) ∧[ F ] -)
                (join-in-frame-equality F  js ⁻¹)

\end{code}

This is the result that we wanted: directification of a basis preserves its
closure under binary meets. In the following definition, we make this a bit more
explicit:

\begin{code}

directify-preserves-closure-under-∧ : (F : Frame 𝓤 𝓥 𝓦)
                                     ( : Fam 𝓦  F )
                                     (β : is-basis-for F )
                                     closed-under-binary-meets F  holds
                                     let
                                       ℬ↑ = directify F 
                                       β↑ = directified-basis-is-basis F  β
                                      in
                                       closed-under-binary-meets F ℬ↑ holds
directify-preserves-closure-under-∧ F  β ϑ is js =
 ∥∥-rec ∃-is-prop γ (cnf-transform-is-basic F  β ϑ is js)
  where
   open Meets  x y  x ≤[ poset-of F ] y)

   ℬ↑ = directify F 
   x  = ℬ↑ [ is ]
   y  = ℬ↑ [ js ]

   γ : Σ ks  (index ℬ↑) , ℬ↑ [ ks ]  ℬ↑ [ is ] ∧[ F ] ℬ↑ [ js ]
       ks  index ℬ↑ , (((ℬ↑ [ ks ]) is-glb-of (x , y)) holds)
   γ (ks , p) =
    let
      : ((x ∧[ F ] y) is-glb-of (x , y)) holds
      = (∧[ F ]-lower₁ x y  , ∧[ F ]-lower₂ x y)
       , λ (z , p)  uncurry (∧[ F ]-greatest x y z) p
    in
      ks , transport  -  (- is-glb-of (x , y)) holds) (p ⁻¹)  

\end{code}