open import 1Lab.Path.IdentitySystem
open import 1Lab.HLevel.Closure
open import 1Lab.Univalence
open import 1Lab.HLevel
open import 1Lab.Equiv
open import 1Lab.Path
open import 1Lab.Type

open import Data.Dec.Base
open import Data.Sum.Base

open is-equiv
open is-contr
open is-iso

module Data.Bool where


# The booleansπ

open import Data.Bool.Base public


Pattern matching on only the first argument might seem like a somewhat inpractical choice due to its asymmetry - however, it shortens a lot of proofs since we get a lot of judgemental equalities for free. For example, see the following statements:

and-associative : (x y z : Bool) β and x (and y z) β‘ and (and x y) z
and-associative false y z = refl
and-associative true y z = refl

or-associative : (x y z : Bool) β or x (or y z) β‘ or (or x y) z
or-associative false y z = refl
or-associative true y z = refl

and-commutative : (x y : Bool) β and x y β‘ and y x
and-commutative false false = refl
and-commutative false true = refl
and-commutative true false = refl
and-commutative true true = refl

or-commutative : (x y : Bool) β or x y β‘ or y x
or-commutative false false = refl
or-commutative false true = refl
or-commutative true false = refl
or-commutative true true = refl

and-truer : (x : Bool) β and x true β‘ x
and-truer false = refl
and-truer true = refl

and-falser : (x : Bool) β and x false β‘ false
and-falser false = refl
and-falser true = refl

and-truel : (x : Bool) β and true x β‘ x
and-truel x = refl

or-falser : (x : Bool) β or x false β‘ x
or-falser false = refl
or-falser true = refl

or-truer : (x : Bool) β or x true β‘ true
or-truer false = refl
or-truer true = refl

or-falsel : (x : Bool) β or false x β‘ x
or-falsel x = refl

and-absorbs-orr : (x y : Bool) β and x (or x y) β‘ x
and-absorbs-orr false y = refl
and-absorbs-orr true y = refl

or-absorbs-andr : (x y : Bool) β or x (and x y) β‘ x
or-absorbs-andr false y = refl
or-absorbs-andr true y = refl

and-distrib-orl : (x y z : Bool) β and x (or y z) β‘ or (and x y) (and x z)
and-distrib-orl false y z = refl
and-distrib-orl true y z = refl

or-distrib-andl : (x y z : Bool) β or x (and y z) β‘ and (or x y) (or x z)
or-distrib-andl false y z = refl
or-distrib-andl true y z = refl

and-idempotent : (x : Bool) β and x x β‘ x
and-idempotent false = refl
and-idempotent true = refl

or-idempotent : (x : Bool) β or x x β‘ x
or-idempotent false = refl
or-idempotent true = refl

and-distrib-orr : (x y z : Bool) β and (or x y) z β‘ or (and x z) (and y z)
and-distrib-orr true y z =
sym (or-absorbs-andr z y) β ap (or z) (and-commutative z y)
and-distrib-orr false y z = refl

or-distrib-andr : (x y z : Bool) β or (and x y) z β‘ and (or x z) (or y z)
or-distrib-andr true y z = refl
or-distrib-andr false y z =
sym (and-absorbs-orr z y) β ap (and z) (or-commutative z y)

and-reflect-true-l : β {x y} β and x y β‘ true β x β‘ true
and-reflect-true-l {x = true} p = refl
and-reflect-true-l {x = false} p = p

and-reflect-true-r : β {x y} β and x y β‘ true β y β‘ true
and-reflect-true-r {x = true} {y = true} p = refl
and-reflect-true-r {x = false} {y = true} p = refl
and-reflect-true-r {x = true} {y = false} p = p
and-reflect-true-r {x = false} {y = false} p = p

or-reflect-true : β {x y} β or x y β‘ true β x β‘ true β y β‘ true
or-reflect-true {x = true} {y = y} p = inl refl
or-reflect-true {x = false} {y = true} p = inr refl
or-reflect-true {x = false} {y = false} p = absurd (trueβ false (sym p))

or-reflect-false-l : β {x y} β or x y β‘ false β x β‘ false
or-reflect-false-l {x = true} p = absurd (trueβ false p)
or-reflect-false-l {x = false} p = refl

or-reflect-false-r : β {x y} β or x y β‘ false β y β‘ false
or-reflect-false-r {x = true} {y = true} p = absurd (trueβ false p)
or-reflect-false-r {x = true} {y = false} p = refl
or-reflect-false-r {x = false} {y = true} p = absurd (trueβ false p)
or-reflect-false-r {x = false} {y = false} p = refl

and-reflect-false : β {x y} β and x y β‘ false β x β‘ false β y β‘ false
and-reflect-false {x = true} {y = y} p = inr p
and-reflect-false {x = false} {y = y} p = inl refl


All the properties above hold both in classical and constructive mathematics, even in minimal logic that fails to validate both the law of the excluded middle as well as the law of noncontradiction. However, the boolean operations satisfy both of these laws:

not-involutive : (x : Bool) β not (not x) β‘ x
not-involutive false i = false
not-involutive true i = true

not-andβ‘or-not : (x y : Bool) β not (and x y) β‘ or (not x) (not y)
not-andβ‘or-not false y = refl
not-andβ‘or-not true y = refl

not-orβ‘and-not : (x y : Bool) β not (or x y) β‘ and (not x) (not y)
not-orβ‘and-not false y = refl
not-orβ‘and-not true y = refl

or-complementl : (x : Bool) β or (not x) x β‘ true
or-complementl false = refl
or-complementl true = refl

and-complementl : (x : Bool) β and (not x) x β‘ false
and-complementl false = refl
and-complementl true = refl


Furthermore, note that not has no fixed points.

not-no-fixed : β {x} β x β‘ not x β β₯
not-no-fixed {x = true} p = absurd (trueβ false p)
not-no-fixed {x = false} p = absurd (trueβ false (sym p))


Exclusive disjunction (usually known as XOR) also yields additional structure - in particular, it can be viewed as an addition operator in a ring whose multiplication is defined by conjunction:

xor : Bool β Bool β Bool
xor false y = y
xor true y = not y

xor-associative : (x y z : Bool) β xor x (xor y z) β‘ xor (xor x y) z
xor-associative false y z = refl
xor-associative true false z = refl
xor-associative true true z = not-involutive z

xor-commutative : (x y : Bool) β xor x y β‘ xor y x
xor-commutative false false = refl
xor-commutative false true = refl
xor-commutative true false = refl
xor-commutative true true = refl

xor-falser : (x : Bool) β xor x false β‘ x
xor-falser false = refl
xor-falser true = refl

xor-truer : (x : Bool) β xor x true β‘ not x
xor-truer false = refl
xor-truer true = refl

xor-inverse-self : (x : Bool) β xor x x β‘ false
xor-inverse-self false = refl
xor-inverse-self true = refl

and-distrib-xorr : (x y z : Bool) β and (xor x y) z β‘ xor (and x z) (and y z)
and-distrib-xorr false y z = refl
and-distrib-xorr true y false = and-falser (not y) β sym (and-falser y)
and-distrib-xorr true y true = (and-truer (not y)) β ap not (sym (and-truer y))


Material implication between booleans also interacts nicely with many of the other operations:

imp : Bool β Bool β Bool
imp false y = true
imp true y = y

imp-truer : (x : Bool) β imp x true β‘ true
imp-truer false = refl
imp-truer true = refl


Furthermore, material implication is equivalent to the classical definition.

imp-not-or : β x y β or (not x) y β‘ imp x y
imp-not-or false y = refl
imp-not-or true y = refl


## The βnotβ equivalenceπ

The construction of not as an equivalence factors through showing that not is an isomorphism. In particular, not is its own inverse, so we need a proof that itβs involutive, as is proven in not-involutive. With this, we can get a proof that itβs an equivalence:

not-is-equiv : is-equiv not
not-is-equiv = is-involutiveβis-equiv not-involutive

not-inj : β {x y} β not x β‘ not y β x β‘ y
not-inj {x = true}  {y = true}  p = refl
not-inj {x = true}  {y = false} p = sym p
not-inj {x = false} {y = true}  p = sym p
not-inj {x = false} {y = false} p = refl


## Aut(Bool)π

We characterise the type Bool β‘ Bool. There are exactly two paths, and we can decide which path weβre looking at by seeing how it acts on the element true.

First, two small lemmas: we can tell whether weβre looking at the identity equivalence or the βnotβ equivalence by seeing how it acts on the constructors.

private
idLemma : (p : Bool β Bool)
β p .fst true β‘ true
β p .fst false β‘ false
β p β‘ (_ , id-equiv)
idLemma p p1 p2 = Ξ£-path (funext lemma) (is-equiv-is-prop _ _ _) where
lemma : (x : Bool) β _
lemma false = p2
lemma true = p1


If it quacks like the identity equivalence, then it must be. Otherwise weβre looking at the not equivalence.

  notLemma : (p : Bool β Bool)
β p .fst true β‘ false
β p .fst false β‘ true
β p β‘ (not , not-is-equiv)
notLemma p p1 p2 = Ξ£-path (funext lemma) (is-equiv-is-prop _ _ _) where
lemma : (x : Bool) β _
lemma false = p2
lemma true = p1


With these two lemmas, we can proceed to classify the automorphisms of Bool. For this, weβll need another lemma: If a function Bool β Bool doesnβt map f x β‘ x, then it maps f x β‘ not x.

Bool-autβ‘2 : (Bool β‘ Bool) β‘ Lift _ Bool
Bool-autβ‘2 = IsoβPath the-iso where
lemma : (f : Bool β Bool) {x : Bool} β Β¬ f x β‘ x β f x β‘ not x
lemma f {false} x = caseα΅ (f false β‘ true) of Ξ» where
(yes p) β p
(no Β¬p) β absurd (Β¬p (xβ falseβxβ‘true _ x))
lemma f {true} x = caseα΅ (f true β‘ false) of Ξ» where
(yes p) β p
(no Β¬p) β absurd (Β¬p (xβ trueβxβ‘false _ x))


This lemma is slightly annoying to prove, but itβs not too complicated. Itβs essentially two case splits: first on the boolean, and second on whether weβre looking at f x β‘ not x. If we are, then itβs fine (those are the yes p = p cases) - otherwise that contradicts what weβve been told.

  the-iso : Iso (Bool β‘ Bool) (Lift _ Bool)

fst the-iso path = caseα΅ (transport path true β‘ true) of Ξ» where
(yes path) β lift false
(no Β¬path) β lift true


Now we classify the isomorphism by looking at what it does to true. We arbitrarily map refl to false and not to true.

  the-iso .snd .is-iso.inv (lift false) = refl
the-iso .snd .is-iso.inv (lift true)  = ua (not , not-is-equiv)


The inverse is determined by the same rule, but backwards. Thatβs why itβs an inverse! Everything computes in a way that lines up to this function being a right inverse on the nose.

  the-iso .snd .is-iso.rinv (lift false) = refl
the-iso .snd .is-iso.rinv (lift true)  = refl


The left inverse is a lot more complicated to prove. We examine how the path acts on both true and false. There are four cases:

  the-iso .snd .is-iso.linv path with transport path true  β‘? true
| transport path false β‘? false
... | yes trueβtrue | yes falseβfalse =
refl                  β‘β¨ sym (PathβEquiv .snd .linv _) β©β‘
ua (pathβequiv refl) β‘β¨ ap ua pathβequiv-refl β©β‘
ua (_ , id-equiv)      β‘β¨ ap ua (sym (idLemma _ trueβtrue falseβfalse)) β©β‘
ua (pathβequiv path) β‘β¨ PathβEquiv .snd .linv _ β©β‘
path                  β


In the case where the path quacks like reflexivity, we use the univalence axiom to show that we must be looking at the reflexivity path. For this, we use idLemma to show that pathβequiv path must be the identity equivalence.

  ... | yes trueβtrue | no falseβtrue' =
let
falseβtrue = lemma (transport path) falseβtrue'
fibres = is-contrβis-prop (pathβequiv path .snd .is-eqv true)
(true , trueβtrue) (false , falseβtrue)
in absurd (trueβ false (ap fst fibres))


The second case is when both booleans map to true. This is a contradiction - transport along a path is an equivalence, and equivalences have contractible fibres; Since we have two fibres over true, that means we must have true β‘ false.

  ... | no trueβfalse' | yes falseβfalse =
let
trueβfalse = lemma (transport path) trueβfalse'
fibres = is-contrβis-prop (pathβequiv path .snd .is-eqv false)
(true , trueβfalse) (false , falseβfalse)
in absurd (trueβ false (ap fst fibres))


The other case is analogous.

  ... | no trueβfalse' | no falseβtrue' =
ua (not , not-is-equiv)
β‘β¨ ap ua (sym (notLemma _
(lemma (transport path) trueβfalse')
(lemma (transport path) falseβtrue')))

The last case is when the path quacks like ua (not, _) - in that case, we use the notLemma to show it must be ua (not, _), and the univalence axiom finishes the job.