Finite Decidable Setoids
An Agda exploration of decidable equivalence relations over finite sets by means of standard library's finite-subset API.
In set-based mathematics an equivalence relation can be fully characterised in terms of subset-membership into equivalence classes and partitions. This is trivial to prove on paper, but I found it less obvious to be formalised in Agda at least for my novice skills in the discipline of dependent type theory. In fact, to the trained mathematician, "proofs" after the principle of Propositions-as-Types might seem affected by a 21st-century extreme bourbakism. Nonetheless, regarded as a library, I hope the following can be reused in the formalisation of more involved combinatory results dealing with invariants of algebraic structures: the paradigmatic example here is the orbital equation from Group Theory and essential results deriving from it like Sylow Theorems. In order to formalise these facts about quotients of finite sets, we apply to the finite case more general results from Cohen and Altenkirch. The extra fun comes when trying to play-by-the-rules, that is prove these ideas reusing as much as possible existing functions from Agda standard library.
Given an equivalence relation inline_formula not implemented over a finite set inline_formula not implemented, assume we can exhibit a set of representatives inline_formula not implemented, one for each equivalence class in inline_formula not implemented, then inline_formula not implemented is the disjoint union of all classes of elements in inline_formula not implemented and inline_formula not implemented is called a transversal of inline_formula not implemented:
formula not implementedin other terms inline_formula not implemented induces a partition on inline_formula not implemented and in particular its cardinality is additive on classes:
formula not implementedWe will formalise the above facts in section Properties of Finite Decidable Setoids providing terms as evidence to the following propositions i.e. types:
Covering : ∀ (x : F) → Any (x ∈_) ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧Disjointness : AllPairs Disj ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧Cardinality : n ≡ List.sum ⟦ ∣ [ x ]ω ∣ ∣ x 𝕚𝕟 Transversal ⟧In the section Partitions we'll show that for all natural numbers n the type of finite decidable Setoids on n elements is "equivalent" to the type of partitions of the finite type on n elements.
Sections Subset Lemmas and List-Relations Properties contain proofs and definitions to show the above results while some boring subset helpers are relegated to the Appendix. All sections are matched with dedicated inter-depending Agda runtime.
Definitions
Membership and subset relations in type theory is sorted out via type-valued predicates. In the finite case there's a more amenable description which employs vectors of boolean values, the properties of which are better suited for computation and decidability. This is apparent in the following definition of equivalence class
module FDS whereopen import Level renaming (suc to lsuc;  zero to lzero)open import Relation.Nullaryopen import Relation.Binaryopen import Data.Nat.Baseopen import Data.Finopen import Data.Listopen import Data.List.Relation.Unary.Anyopen import Data.List.Relation.Unary.AllPairsopen import Data.Vec as Vecopen import Data.Fin.Subsetopen import Function.Base using (_∘_)open import FDS.Fin.Subset.Lemmasrecord FinDecEq {a} (n : ℕ) : Set (lsuc a) where  field    _ω_ : Rel (Fin n) a    isDecEq : IsDecEquivalence _ω_  std : DecSetoid _ _  std = record { Carrier = (Fin n) ;                _≈_ = _ω_ ;                isDecEquivalence = isDecEq }  open DecSetoid std    renaming (_≟_ to _ω?_ ;              refl to ω-refl ;              sym to ω-sym ;              trans to ω-transitive ;              Carrier to F)    public  [_]ω : F → Subset n  [ o ]ω = Vec.tabulate (does ∘ (_ω? o))Properties of Finite Decidable Setoids
The following module reads as take a natural number inline_formula not implemented and a finite decidable Setoid FDS over a set of inline_formula not implemented elements
open import Levelopen import Data.Nat.Baseopen import FDS using (FinDecEq)module FDS.Properties {a : Level} {n : ℕ} {FDS : FinDecEq {a} n} whereopen import FDS.Fin.Subset.Lemmasopen import FDS.List.AllPairs.Lemmasopen import Data.Productopen import Function.Baseopen import Data.Bool.Base hiding (_≤_)open import Data.Fin.Subsetopen import Data.Fin.Subset.Propertiesopen import Data.Unit hiding (_≟_;⊤)open import Data.Vec as Vecopen import Data.Vec.Propertiesopen import Data.List as Listopen import Data.List.Propertiesopen import Data.List.Relation.Unary.Any as Any hiding (index)open import Data.List.Relation.Unary.Any.Properties as AnyPropsopen import Data.List.Relation.Unary.AllPairs as AllPairsopen import Data.List.Relation.Unary.AllPairs.Properties as AllPairsPropsopen import Relation.Nullaryopen import Relation.Nullary.Decidableopen import Relation.Unary hiding (_∈_;_⊆_;Empty;_∩_;⋃)open import Relation.Binary.PropositionalEquality as Eqopen Eq.≡-Reasoningopen import Data.Fin using (_≟_)by opening its record type, within this module FinDecEq definitions are directly available, in particular throughout the this section F denotes (the type of) the finite set with n elements. We distinguish inline_formula not implemented subset-membership from inline_formula not implemented list-membership. Please forgive the cheesy comprehension syntax for lists
open FinDecEq FDSimport Data.List.Membership.Propositionalmodule MP = Data.List.Membership.Propositional {A = F}open MP renaming (_∈_ to _∈ℓ_)open import Data.List.Membership.Propositional.Propertieslmap = List.mapsyntax lmap (λ x → B) L = ⟦ B ∣ x 𝕚𝕟 L ⟧Fˡ = List.allFin nFirst basic fact: being in relation means to belong to the same class
ω⇒∈ : ∀ {x y} → x ω y → x ∈ [ y ]ωω⇒∈ {x} {y} xωy = let lkp = begin                               Vec.lookup [ y ]ω x ≡⟨ lookup∘tabulate _ x ⟩                              does (x ω? y) ≡⟨ dec-true (x ω? y) xωy ⟩                              true ∎                  in lookup⇒[]= x [ y ]ω lkpIn particular every class is non empty as a consequence of reflexivity.
o∈[o]ω : ∀ { o : F } → o ∈ [ o ]ωo∈[o]ω = ω⇒∈ ω-refland the converse is also true
∈⇒ω : ∀ {x y} → x ∈ [ y ]ω → x ω y∈⇒ω {x} {y} x∈[y] =   let w = begin            isYes (x ω? y) ≡⟨ isYes≗does _ ⟩             does (x ω? y)  ≡˘⟨ lookup∘tabulate _ x ⟩            Vec.lookup [ y ]ω x ≡⟨ []=⇒lookup x∈[y] ⟩            true ∎      ≡→T : ∀ {b : Bool} → b ≡ true → T b      ≡→T = λ { refl → tt }      r : True (x ω? y)      r = ≡→T w  in toWitness rnow by symmetry we have the desired equivalent formulation
ω⇒⊆ : ∀ {x y} → x ω y → [ x ]ω ⊆ [ y ]ωω⇒⊆ {x} {y} xωy s∈[x] = ω⇒∈ (ω-transitive (∈⇒ω s∈[x]) xωy )ω⇒≡ : ∀ {x y} → x ω y → [ x ]ω ≡ [ y ]ωω⇒≡ {x} {y} xωy = ⊆-antisym (ω⇒⊆ xωy) (ω⇒⊆ (ω-sym xωy))Canonical Representatives
Now the questions we want to answer next is how many distinct classes does an equivalence posses and do such classes partition the carrier set? Or again can we find a subset on which inline_formula not implemented is a bijection? Can we find a prescribed set of computationally well-behaved (i.e. decidable) representatives for the equivalence classes?
We first introduce a canonical choice inline_formula not implemented with the following properties:
- inline_formula not implemented for all inline_formula not implemented 
- inline_formula not implemented if and only if inline_formula not implemented 
- inline_formula not implemented is idempotent 
In the finite setting (as in any well ordering), given any family of nonempty sets it is possible to find a choice-function: the minimum element. In the following code we're using index and index-inline_formula not implemented functions defined in the section Subset Lemmas
cc : F → Fcc f = index {n} [ f ]ω ( f , o∈[o]ω )ccx∈[x] : ∀ (x : F) → (cc x) ∈ [ x ]ωccx∈[x] x = proj₁ (proj₂ (index* [ x ]ω ( x , o∈[o]ω )))xωccx : ∀ (x : F) → x ω (cc x)xωccx x = ω-sym (∈⇒ω (ccx∈[x] x))c⇒ω : ∀ {x y} → cc x ≡ cc y → x ω yc⇒ω {x} {y} ccx≡ccy = let P = λ q → q ∈ [ y ]ω                          w : (cc x) ∈ [ y ]ω                          w = subst P (sym ccx≡ccy) (ccx∈[x] y)                    in ω-transitive (xωccx x) (∈⇒ω w)ω⇒c : ∀ {x y} → x ω y → cc x ≡ cc yω⇒c {x} {y} xωy = index-unique _ _ _ _ (ω⇒≡ xωy)cc-idempt : (x : F) → cc (cc x) ≡ cc xcc-idempt x = ω⇒c {cc x} {x} (ω-sym (xωccx x))A canonical representative of an inline_formula not implemented-class is the minimum element with respect to the natural order on inline_formula not implemented. This is a decidable property because the finite type has a decidable equality. As transversal of inline_formula not implementedwe can take the set of all canonical representatives constructed in this way (recall that inline_formula not implemented is the set inline_formula not implementedrepresented as a list)
cr : (i : F) → Setcr i = i ≡ (cc i)cr? : Decidable crcr? = λ i → i ≟ (cc i)Transversal : List FTransversal = (List.filter cr? Fˡ)Covering
for all inline_formula not implemented in inline_formula not implementedthere exists an equivalence class of a canonical representative which inline_formula not implemented belongs to:
Covering : ∀ (x : F) → Any (x ∈_) ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧Covering x = let               cover : ∀ (x : F) → (∃ λ k → cr k × x ω k)               cover t = (cc t) , sym (cc-idempt t) , xωccx t               (k , crk , xωk) = cover x               k∈ℓFˡ : k ∈ℓ Fˡ               k∈ℓFˡ = ∈-allFin {n} k               k∈ℓTr : k ∈ℓ Transversal               k∈ℓTr = ∈-filter⁺ cr? k∈ℓFˡ crk               a : Any (x ω_) Transversal               a = lose k∈ℓTr xωk               b : Any ((x ∈_) ∘ [_]ω) Transversal               b = Any.map ω⇒∈ a            -- AnyProps.map⁺ : Any (P ∘ f) xs → Any P (List.map f xs)            in AnyProps.map⁺ bDisjointness
Any two distinct canonical classes are disjoint. The term filter⁺₃ used below is proven in at the end of section List-Relations Properties while for definitions of Disj and disj⇒Disj see Subset Lemmas
Disjointness : AllPairs Disj ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧Disjointness =   let ap₁ : AllPairs _≢_ Fˡ      ap₁ = AllPairsProps.tabulate⁺ id      cr-injective : ∀ {x y} → (cr x) → (cr y) → x ≢ y → ¬ x ω y      cr-injective {x} {y} crx cry x≢y xωy = x≢y (begin x ≡⟨ crx ⟩                                                     cc x ≡⟨ ω⇒c xωy ⟩                                                     cc y ≡˘⟨ cry ⟩                                                     y ∎)      ω-disj : ∀ {x y} → ¬ (x ω y) → Empty ([ x ]ω ∩ [ y ]ω)      ω-disj {x} {y} ¬xωy f∈∩ = let f , [x]∩[y] = f∈∩                                    l , r = x∈p∩q⁻ _ _ [x]∩[y]                                    xωf = ω-sym (∈⇒ω l)                                    fωy = ∈⇒ω r                                    in ¬xωy (ω-transitive xωf fωy)      ω-Disj : ∀ {x y} → ¬ x ω y → Disj [ x ]ω [ y ]ω      ω-Disj ¬xωy = disj⇒Disj (ω-disj ¬xωy)      ap₂ : AllPairs (λ x y → ¬ x ω y) Transversal      ap₂ = filter⁺₃ cr? cr-injective ap₁      ap₃ = AllPairs.map ω-Disj ap₂       in AllPairsProps.map⁺ ap₃Conclusions
Now we can transpose the above results in the language of subsets finding first that the union of all canonical classes gives back the whole finite set F
Cover : ⊤ ≡ ⋃ ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧ Cover = cover-⊤ ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧ Coveringand furthermore since canonical classes are disjoint
Cardinality : n ≡ List.sum ⟦ ∣ [ x ]ω ∣ ∣ x 𝕚𝕟 Transversal ⟧Cardinality =   let c = ∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧ Disjointness      d = map-compose {g = ∣_∣} {f = [_]ω} Transversal      sum = List.sum      in begin           n ≡˘⟨ ∣⊤∣≡n n ⟩          ∣ ⊤ {n} ∣ ≡⟨ cong ∣_∣ Cover ⟩          ∣ ⋃ ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧ ∣ ≡⟨ c ⟩           sum (List.map ∣_∣ ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧) ≡˘⟨ cong sum d ⟩          sum ⟦ ∣ [ x ]ω ∣ ∣ x 𝕚𝕟 Transversal ⟧ ∎functions cover-⊤ and ∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ used above are defined in Subset Lemmas.
Partitions
module FDS.Characterisation whereopen import FDSopen import FDS.Fin.Subset.Lemmasopen import FDS.List.AllPairs.Lemmasimport FDS.Propertiesopen import Level renaming (suc to sucℓ)open import Relation.Binaryopen import Relation.Binary.PropositionalEquality as Eq --  using (_≡_; cong; subst)open Eq.≡-Reasoningopen import Relation.Nullaryopen import Relation.Nullary.Reflectsopen import Functionopen import Data.Bool.Baseopen import Data.Emptyopen import Data.Nat.Base as ℕopen import Data.Productopen import Data.Finopen import Data.Fin.Subset hiding (⊥)open import Data.Fin.Subset.Propertiesopen import Data.List as Listopen import Data.List.Membership.Propositional as Membership  renaming (_∈_ to _∈ℓ_)open import Data.List.Relation.Unary.AllPairs as AllPairsopen import Data.List.Relation.Unary.Any as Anyrecord Partition (n : ℕ) : Set (sucℓ 0ℓ) where  field    parts : List (Subset n)    disjoint : AllPairs disj parts    cover : ∀ (x : Fin n) → Any (x ∈_) parts    _ωP_ : Rel (Fin n) 0ℓ  x ωP y = Any (λ s → x ∈ s × y ∈ s) partsFDS⇒Partition : ∀ {ℓ n} → FinDecEq {ℓ} n → Partition nFDS⇒Partition FDS =   record { parts = ⟦ [ x ]ω ∣ x 𝕚𝕟 Transversal ⟧ ;           disjoint = AllPairs.map Disj⇒disj Disjointness ;           cover = Covering } where  open FinDecEq FDS  open FDS.Properties {FDS = FDS}we hide the following proof of decidability of predicate conjunction dec-×
dec-× : ∀ {a b} {P : Set a} {Q : Set b} → Dec P → Dec Q → Dec (P × Q)dec-× dp dq with dp | dq...| yes p | yes q = true because ofʸ (p , q)...| yes p | no ¬q = false because ofⁿ (¬q ∘ proj₂)...| no ¬p | yes q = false because ofⁿ (¬p ∘ proj₁)...| no ¬p | no ¬q = false because ofⁿ (¬p ∘ proj₁)Partition⇒FDS : ∀ {n} → Partition n → FinDecEq nPartition⇒FDS {n} P =   record { _ω_ = _ωP_ ;           isDecEq = isDecEq }  where    open Partition P    ωRefl : Reflexive _ωP_    ωRefl {x} = Any.map {P = λ s → x ∈ s} (λ z → z , z) (cover x)        ωSym : Symmetric _ωP_    ωSym xωPy = Any.map (λ (a , b) → b , a) xωPy        ωTrans : Transitive _ωP_    ωTrans {x} {y} {z} xωPy yωPz =       let        s , s∈ℓparts , x∈s , y∈s = Membership.find xωPy        t , t∈ℓparts , y∈t , z∈t = Membership.find yωPz        y∈s∩t : y ∈ s ∩ t        y∈s∩t = x∈p∩q⁺ ( y∈s , y∈t )        i = Any.index s∈ℓparts        j = Any.index t∈ℓparts        ¬d : ¬ (disj s t)        ¬d e = e ( y , y∈s∩t )         disj-sym : Symmetric disj        disj-sym {x} {y} disjxy = subst Empty (∩-comm x y) disjxy        helper : i ≡ j → x ωP z        helper i≡j =           let s≡t : s ≡ t              s≡t = begin                 s ≡⟨ helper₁ s∈ℓparts ⟩                 Any.lookup s∈ℓparts ≡⟨ cong (List.lookup parts) i≡j ⟩                Any.lookup t∈ℓparts ≡˘⟨ helper₁ t∈ℓparts ⟩                 t ∎              x∈t : x ∈ t              x∈t = subst₂ _∈_ refl s≡t x∈s          in Membership.lose t∈ℓparts ( x∈t , z∈t )                  in case i ≟ j of λ {          (yes i≡j) → helper i≡j ;          (no i≢j) → ⊥-elim (¬d (elim-sym disj-sym disjoint s∈ℓparts t∈ℓparts i≢j))        } where            helper₁ : ∀ {A : Set} {x : A} {l : List A} →                       (ξ : x ∈ℓ l) → x ≡ Any.lookup ξ            helper₁ (here px) = px            helper₁ (there χ) = helper₁ χ        isEq : IsEquivalence _ωP_    isEq = record {            refl = ωRefl ;            sym = ωSym ;             trans = ωTrans }                -- decidability    ≟P : Decidable ( _ωP_ )    ≟P = λ x y → Any.any (λ t → dec-× (x ∈? t) (y ∈? t)) parts    isDecEq : IsDecEquivalence _ωP_    isDecEq = record { isEquivalence = isEq ; _≟_ = ≟P }                         Subset Lemmas
module FDS.Fin.Subset.Lemmas whereopen import FDS.Subset.Helpersopen import Level hiding (suc)open import Relation.Nullary.Negation using (contradiction)open import Relation.Unary hiding (_∈_;_⊆_;_∩_;_∪_;⋃;∁;Empty;_⇒_)open import Relation.Binaryopen import Data.Bool.Properties hiding (≤-antisym)open import Data.Nat.Base as ℕ using (ℕ ;_+_; zero; suc; _<_; z≤n; s≤s)open import Data.Nat.Properties using (+-comm)open import Data.Fin   renaming (zero to fzero; suc to fsuc)  hiding (fold; _+_; _<_)open import Data.Fin.Propertiesopen import Data.Fin.Subsetopen import Data.Fin.Subset.Propertiesopen import Functionopen import Data.Productopen import Data.Sum renaming ([_,_] to _⊞_)open import Data.Vec as Vecopen import Data.Vec.Propertiesopen import Data.List as Listopen import Data.List.Relation.Unary.Any as Any hiding (index)open import Data.List.Relation.Unary.All as Allopen import Data.List.Relation.Unary.AllPairsopen import Data.List.Membership.Propositional renaming (_∈_ to _∈ℓ_)import Algebra.Properties.BooleanAlgebraopen import Relation.Binary.PropositionalEquality as Eqopen Eq.≡-ReasoningWe'll work with the following equivalent notions of disjointness
Disj : ∀ {n} → Subset n → Subset n → SetDisj S T = S ∩ T ≡ ⊥disj : ∀ {n} → Subset n → Subset n → Setdisj S T = Empty (S ∩ T)Disj-sym : ∀ {n} → Symmetric (Disj {n = n})Disj-sym {n} {S} {T} S∩T≡⊥ = begin T ∩ S ≡⟨ ∩-comm T S ⟩ S ∩ T ≡⟨ S∩T≡⊥ ⟩ ⊥ ∎Empty⊥ : ∀ {n} → Empty { n } ⊥Empty⊥ (i , n) = let                    x : Vec.lookup ⊥ i ≡ outside                    x = lookup-replicate i outside                    y : Vec.lookup ⊥ i ≡ inside                    y = []=⇒lookup n                    z = trans (sym x) y                  in contradiction z (not-¬ refl)                  disj⇒Disj : ∀ {n} → ∀ {s t : Subset n} → disj s t → Disj s tdisj⇒Disj d = Empty-unique dDisj⇒disj : ∀ {n} → ∀ {s t : Subset n} → Disj s t → disj s tDisj⇒disj D = subst Empty (sym D) (Empty⊥)We borrow the following proof from standard library, where it's related to monoids in general and is proved for tables while we need it for unions of lists
⋃-remove : ∀ {n} {L : List (Subset n)} → ∀ s → (s∈L : s ∈ℓ L) →           ⋃ L ≡ s ∪ ⋃ (L Any.─ s∈L)⋃-remove {n} {List.[]} _ = λ ()⋃-remove {n} {x ∷ xs} s (here px) =  begin    ⋃ (x ∷ xs) ≡⟨ refl ⟩    x ∪ (⋃ xs) ≡˘⟨ cong (_∪ _) px ⟩    s ∪ (⋃ xs) ≡⟨ cong (_ ∪_) refl ⟩    s ∪ ⋃ ((x ∷ xs) Any.─ (here {P = P} px)) ∎    where    P : Pred (Subset n) Level.zero    P = s ≡_⋃-remove {n} {x ∷ xs} s (there s∈L) =  begin    x ∪ (⋃ xs) ≡⟨ cong (x ∪_) (⋃-remove {L = xs} s s∈L) ⟩    x ∪ (s ∪ ⋃ (xs Any.─ s∈L)) ≡˘⟨ ∪-assoc _ _ _ ⟩ -- TODO: -- use solver    (x ∪ s) ∪ ⋃ (xs Any.─ s∈L) ≡⟨ cong (_∪ _) (∪-comm x s) ⟩    (s ∪ x) ∪ ⋃ (xs Any.─ s∈L) ≡⟨ ∪-assoc _ _ _ ⟩    s ∪ (x ∪ ⋃ (xs Any.─ s∈L)) ≡⟨ refl ⟩    s ∪ ⋃ ((x ∷ xs) Any.─ (there s∈L)) ∎We'll also make use of the following covering lemma: if a family of sets covers the whole carrier then its union is the whole set
cover-⊤ : ∀ {n} → (L : List (Subset n)) →          (∀ (x : Fin n) → Any (x ∈_) L ) →          ⊤ {n} ≡ ⋃ Lcover-⊤ {n} L ∃lx∈l = ⊆-antisym ⊤⊆⋃L (⊆-max (⋃ L)) where  ⊤⊆⋃L : ⊤ ⊆ (⋃ L)  ⊤⊆⋃L {x} _ = let                  (l , l∈ℓL , x∈l) = find {P = x ∈_} (∃lx∈l x)                  l⊆l∪* = p⊆p∪q {p = l} (⋃ (L Any.─ l∈ℓL))                  x∈l∪* = l⊆l∪* {x} x∈l                in subst₂ _∈_ refl (sym (⋃-remove l l∈ℓL)) x∈l∪*and the following additive property of cardinality on disjoint sets
∣p⊍q∣≡∣p∣+∣q∣ : ∀ {n} → ∀ (p q : Subset n) →                 Disj p q → ∣ p ∪ q ∣ ≡ ∣ p ∣ + ∣ q ∣      ∣p⊍q∣≡∣p∣+∣q∣ {zero} [] [] d = refl∣p⊍q∣≡∣p∣+∣q∣ {suc n} (outside ∷ p) (outside ∷ q) d =   begin    ∣ p ∪ q ∣ ≡⟨ ∣p⊍q∣≡∣p∣+∣q∣ {_} p q (drop-disj {n} {outside} {outside} d) ⟩    ∣ (outside ∷ p) ∣ + ∣ (outside ∷ q) ∣ ∎∣p⊍q∣≡∣p∣+∣q∣ {suc n} (inside ∷ p) (outside ∷ q) d =   begin    ∣ (inside ∷ p) ∪ (outside ∷ q) ∣ ≡⟨ refl ⟩    suc ∣ p ∪ q ∣       ≡⟨ cong suc (∣p⊍q∣≡∣p∣+∣q∣ {_} p q (drop-disj {n} {inside} {outside} d)) ⟩    suc (∣ p ∣ + ∣ q ∣) ≡⟨⟩    (suc ∣ p ∣) + ∣ q ∣ ≡⟨⟩    ∣ (inside ∷ p) ∣ + ∣ (outside ∷ q) ∣ ∎∣p⊍q∣≡∣p∣+∣q∣ {suc n} (outside ∷ p) (inside ∷ q) d =   begin    ∣ (outside ∷ p) ∪ (inside ∷ q) ∣ ≡⟨ refl ⟩    suc ∣ p ∪ q ∣       ≡⟨ cong suc (∣p⊍q∣≡∣p∣+∣q∣ {_} p q (drop-disj {n} {outside} {inside} d)) ⟩    suc (∣ p ∣ + ∣ q ∣) ≡⟨ cong suc (+-comm ∣ p ∣ ∣ q ∣) ⟩    suc (∣ q ∣ + ∣ p ∣) ≡⟨ refl ⟩    (suc ∣ q ∣) + ∣ p ∣ ≡⟨⟩    ∣ (inside ∷ q) ∣ + ∣ (outside ∷ p) ∣       ≡⟨ +-comm ∣ (inside ∷ q) ∣ ∣ (outside ∷ p) ∣  ⟩    ∣ (outside ∷ p) ∣ + ∣ (inside ∷ q) ∣ ∎            which also hold for lists of subsets
∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ : ∀ {n} → (C : List (Subset n)) → AllPairs Disj C →                ∣ ⋃ C ∣ ≡ List.sum (List.map ∣_∣ C)∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ {n} List.[] Δℓ =  begin    ∣ ⋃ {n} List.[] ∣ ≡⟨⟩    ∣ ⊥ {n} ∣ ≡⟨ ∣⊥∣≡0 n ⟩    0 ≡⟨⟩    List.foldr _+_ 0 List.[] ∎∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ {n} (s ∷ C) (h ∷ t) =   begin    ∣ ⋃ (s ∷ C) ∣ ≡⟨⟩    ∣ s ∪ (⋃ C) ∣ ≡⟨ ∣p⊍q∣≡∣p∣+∣q∣ s (⋃ C) (Disj-sym DisjUCs) ⟩    ∣ s ∣ + ∣ ⋃ C ∣ ≡⟨ cong (∣ s ∣ +_) (∣⋃ᵢpᵢ∣≡Σᵢ∣pᵢ∣ {n} C t ) ⟩    ∣ s ∣ + (List.sum (List.map ∣_∣ C)) ≡⟨⟩    List.sum (List.map ∣_∣ (s ∷ C)) ∎    where    a : All (λ x → x ⊆ ∁ s) C    a = All.map (disj⇒⊆∁ ∘ Disj-sym) h    c : ⋃ C ⊆ ∁ s    c = pᵢ⊆q⇒⋃pᵢ⊆q (∁ s) C a    DisjUCs : Disj (⋃ C) s    DisjUCs = ⊆∁⇒disj cwe conclude with a proof of existence for a finite choice function
fsuc∈ : ∀ {n} → {p : Subset n} → {x : Fin n} → {s : Side} →         (x ∈ p) → (fsuc x) ∈ (s ∷ p)fsuc∈ here = there herefsuc∈ (there x∈p) = there (fsuc∈ x∈p)Since finite sets carry a natural order we can always find the smallest element in a non empty subset. Note how Agda type checker eliminates obvious non matching patterns given we're dealing with non-empty sets only.
index* : ∀ {n} → (s : Subset n) → Nonempty s →         ∃ (λ x → ( x ∈ s × (∀ {y} → (y ∈ s) → x ≤ y )))index* {suc n} (inside ∷ rest) _ = (fzero , here , λ _ → z≤n )index* {suc n} (outside ∷ rest) ne with (∃-toSum ne)... | inj₂ b = let w = drop-there (proj₂ b)                   z = (proj₁ b , w)                   ( a , bb , c ) = (index* rest z)               in (fsuc a , fsuc∈ bb , λ { (there y∈s) → s≤s (c y∈s) } )index : ∀ {n} → (s : Subset n) → Nonempty s → Fin nindex s ne = proj₁ (index* s ne)index-unique : ∀ {n} → ∀ (s t : Subset n) → (ns : Nonempty s) →             (nt : Nonempty t) →             s ≡ t → (index s ns) ≡ (index t nt)index-unique s t ns nt s≡t = let fs , fs∈s , fs≤ = index* s ns                                 ft , ft∈t , ft≤ = index* t nt                                 ft∈s : ft ∈ s                                 ft∈s = subst _ (sym s≡t) ft∈t                                 fs∈t : fs ∈ t                                 fs∈t = subst _ s≡t fs∈s                              in ≤-antisym (fs≤ ft∈s) (ft≤ fs∈t)List-Relations Properties
missing from the standard library
module FDS.List.AllPairs.Lemmas whereopen import Relation.Nullaryopen import Relation.Nullary.Decidableopen import Relation.Unary hiding (_∈_)open import Relation.Binary hiding (Decidable)open import Relation.Binary.PropositionalEqualityopen import Data.Natopen import Data.Fin using (toℕ; suc; zero) renaming (_<_ to _<f_)open import Data.Fin.Propertiesopen import Data.Emptyopen import Data.Productopen import Data.Listopen import Data.List.Membership.Propositionalopen import Data.List.Relation.Unary.Any as Anyopen import Data.List.Relation.Unary.All as Allopen import Data.List.Relation.Unary.All.Properties hiding (drop⁺)open import Data.List.Relation.Unary.AllPairs as AllPairsopen import Data.List.Relation.Unary.AllPairs.Properties hiding (filter⁺)let's hide here a pair of tedious helpers drop∷ and drop-suc used in the next lemma, that can be stated as evidence of membership implies a list is non empty.  
module _ {a} {A : Set a} where  drop∷ : ∀ (xs : List A) → ∀ {x} → (x∈ : x ∈ xs) →          let i = Any.index x∈          in drop (toℕ i) xs ≡ x ∷ drop (toℕ (suc i)) xs  drop∷ _ (Any.here px) = cong (_∷ _) (sym px)  drop∷ (x ∷ xs) (Any.there x∈) = drop∷ xs x∈  drop-suc : ∀ (xs : List A) → ∀ {x y} →             (x∈ : x ∈ xs) → (y∈ : y ∈ xs) →             (Any.index x∈) <f (Any.index y∈) →             y ∈ drop (toℕ (suc (Any.index x∈))) xs  drop-suc _ (Any.here px) (Any.there y∈) i<j = y∈  drop-suc (x ∷ xs) (Any.there x∈) (Any.there y∈) (s≤s i<j) =     drop-suc xs x∈ y∈ i<jthis is an all-pairs elimination, which surprisingly is not in the standard library unless I've overseen something crucial
module _ {a ℓ} {A : Set a} {R : Rel A ℓ } where  elim : ∀ {xs x y} → AllPairs R xs →            (x∈ : x ∈ xs) → (y∈ : y ∈ xs) →           (Any.index x∈) <f (Any.index y∈) → R x y  elim {xs} {x} {y} ap x∈ y∈ i<j =    let  i = (Any.index x∈)          j = (Any.index y∈)         xs' = drop (toℕ i) xs         ¬∅ : xs' ≡ x ∷ _         ¬∅ = drop∷ xs x∈         d : AllPairs R xs'         d = drop⁺ (toℕ i) ap         d' = subst (AllPairs R) ¬∅ d         h = AllPairs.head d'    in All.lookup h (drop-suc xs x∈ y∈ i<j)in case of symmetric relations the order of indices is actually irrelevant
module _ {a ℓ} {A : Set a} {R : Rel A ℓ } (S : Symmetric R) where  elim-sym : ∀ {xs x y} → AllPairs R xs →           (x∈ : x ∈ xs) → (y∈ : y ∈ xs) →           (Any.index x∈) ≢ (Any.index y∈) → R x y  elim-sym ap x∈ y∈ i≢j with <-cmp (Any.index x∈) (Any.index y∈)  ...| tri< i<j _ _ = elim ap x∈ y∈ i<j  ...| tri> _ _ j<i = S (elim ap y∈ x∈ j<i)  ...| tri≈ _ i≈j _ = ⊥-elim (i≢j i≈j)to conclude this section we'll show an alternative form of filter introduction rule for the AllPairs predicate, namely
module _ {a ℓ ℓ₁ ℓ₂} {A : Set a}         {R : Rel A ℓ₁} {S : Rel A ℓ₂}         {P : Pred A ℓ} (P? : Decidable P) where  filter⁺₃ : ∀ {xs} → (∀ {x y} → P x → P y → R x y → S x y) →            AllPairs R xs → AllPairs S (filter P? xs)  filter⁺₃ {[]} _ _ = []  filter⁺₃ {x ∷ xs} Δ (h ∷ t) with (P? x)  ... | no  ¬p = filter⁺₃ {xs} Δ t  ... | yes p = let                  hf : All (R x) (filter P? xs)                  hf = filter⁺ P? h                  ap : All P (filter P? xs)                  ap = all-filter P? xs                  w : All (P ∩ R x) (filter P? xs)                  w = All.zip ( ap , hf )                  y : P ∩ R x ⊆ S x                  y = λ z → Δ p (proj₁ z) (proj₂ z)                  z : All (S x) (filter P? xs)                  z =  All.map y w                in z ∷ filter⁺₃ {xs} Δ tReferences
Appendix: Subset Helpers
module FDS.Subset.Helpers where-- open import Level-- open import Data.Nat-- open import Relation.Nullary.Decidable-- open import Data.Vec.Propertiesopen import Data.Nat.Base as ℕ using (ℕ; zero)open import Data.Productopen import Data.Sum renaming ([_,_] to _⊞_)open import Data.Vec as V using (_∷_; [])open import Data.List renaming ([] to <>)open import Data.List.Relation.Unary.Allopen import Data.Fin.Subsetopen import Data.Fin.Subset.Propertiesopen import Relation.Binary.PropositionalEquality as Eqopen Eq.≡-Reasoningimport Algebra.Properties.BooleanAlgebra∩-⊆-stable : ∀ {n} → ∀ {p q} → (r : Subset n) → p ⊆ q → (p ∩ r) ⊆ (q ∩ r)∩-⊆-stable {_} {p} {q} r p⊆q x∈p∩r =   let    x∈p = proj₁ (x∈p∩q⁻ p r x∈p∩r)    x∈r = proj₂ (x∈p∩q⁻ p r x∈p∩r)  in x∈p∩q⁺ ( p⊆q x∈p , x∈r )idʳ⇒⊆ : ∀ {n} → (S T : Subset n) → (S ∪ T) ≡ T → S ⊆ Tidʳ⇒⊆ {n} S T sut≡t = subst₂ _⊆_ refl sut≡t (p⊆p∪q T)p∩∁p≡⊥ : ∀ {n} → (p : Subset n) → p ∩ ∁ p ≡ ⊥p∩∁p≡⊥ [] = reflp∩∁p≡⊥ (outside ∷ p) = cong (outside ∷_) (p∩∁p≡⊥ p)p∩∁p≡⊥ (inside ∷ p) = cong (outside ∷_) (p∩∁p≡⊥ p)p⊆q⇒p∩∁q≡⊥ : ∀ {n} → {S T : Subset n} → S ⊆ T → S ∩ (∁ T) ≡ ⊥p⊆q⇒p∩∁q≡⊥ {_} {S} {T} s⊆t = let                               a = p∩∁p≡⊥ _                               b : S ∩ (∁ T) ⊆ T ∩ (∁ T)                               b = ∩-⊆-stable (∁ T) s⊆t                               c = subst₂ _⊆_ refl a b                               in ⊆-antisym c (⊆-min _)disj⇒⊆∁ : ∀ {n} → {S T : Subset n} → S ∩ T ≡ ⊥ → S ⊆ (∁ T)disj⇒⊆∁ {n} {S} {T} dst =   let ct≡suct = begin                ∁ T ≡˘⟨ ∪-identityˡ _ ⟩                ⊥ ∪ (∁ T) ≡˘⟨ cong (_∪ (∁ T)) dst ⟩                (S ∩ T) ∪ (∁ T) ≡⟨ ∪-distribʳ-∩ _ _ _ ⟩                (S ∪ (∁ T)) ∩ (T ∪ (∁ T)) ≡⟨ cong ((S ∪ (∁ T)) ∩_) (p∪∁p≡⊤ T) ⟩                (S ∪ (∁ T)) ∩ ⊤ ≡⟨ ∩-identityʳ _ ⟩                S ∪ (∁ T) ∎  in idʳ⇒⊆ S (∁ T) (sym ct≡suct)⊆∁⇒disj : ∀ {n} → {S T : Subset n} → S ⊆ (∁ T) → S ∩ T ≡ ⊥⊆∁⇒disj {n} {S} {T} s⊆∁t =   begin  S ∩ T ≡˘⟨ cong (S ∩_) (¬-involutive T ) ⟩  S ∩ (∁ (∁ T)) ≡⟨ p⊆q⇒p∩∁q≡⊥ s⊆∁t ⟩  ⊥ ∎  where open Algebra.Properties.BooleanAlgebra (∪-∩-booleanAlgebra n)           p⊆r×q⊆r⇒p∪q⊆r : ∀ {n} → { p q r : Subset n} → (p ⊆ r) × (q ⊆ r) → (p ∪ q) ⊆ rp⊆r×q⊆r⇒p∪q⊆r {n} {p} {q} {r} (p⊆r , q⊆r) x∈p∪q =   let y = x∈p∪q⁻ {n} p q x∈p∪q  in (p⊆r ⊞ q⊆r) ypᵢ⊆q⇒⋃pᵢ⊆q : ∀ {n} → ∀ S → ∀ (L : List (Subset n)) →             All (_⊆ S) L → (⋃ L) ⊆ Spᵢ⊆q⇒⋃pᵢ⊆q S <> _ = ⊆-min Spᵢ⊆q⇒⋃pᵢ⊆q S (x ∷ xs) (h ∷ t) =   subst₂ _⊆_ refl refl (p⊆r×q⊆r⇒p∪q⊆r ( h , pᵢ⊆q⇒⋃pᵢ⊆q S xs t )) drop-disj : ∀ {n} → {x y : Side} → {p q : Subset n} →             (x ∷ p) ∩ (y ∷ q) ≡ ⊥ → p ∩ q ≡ ⊥drop-disj {zero} {_} {_} {[]} {[]} _ = refldrop-disj d = cong V.tail d