import Mathlib.Logic.Basic
import Mathlib.Data.Bool.Basic
import Mathlib.Algebra.Order.Lattice.Basic
import Mathlib.Tactic.Basic
import Mathlib.Tactic.Ring
import Mathlib.Logic.Equiv.Basic
Boolean algebra is not just a collection of logical operations—it forms a rich algebraic structure governed by
precise laws. These laws enable systematic reasoning, circuit optimization, and logical simplification. In type theory,
we can prove these laws both computationally (for Bool
) and propositionally (for Prop
),
demonstrating the deep connections between logic and algebra.
A Boolean algebra consists of:
Bool
or propositions in Prop
)-- Define a general Boolean algebra structure
class BooleanAlgebra (α : Type*) extends Lattice α where
-- Complement operation
compl : α → α
-- Special elements
top : α -- True element
bot : α -- False element
-- Complement laws
compl_sup_eq : ∀ x : α, x ⊔ compl x = top
compl_inf_eq : ∀ x : α, x ⊓ compl x = bot
-- Identity laws
sup_bot_eq : ∀ x : α, x ⊔ bot = x
inf_top_eq : ∀ x : α, x ⊓ top = x
-- Bool is a Boolean algebra
instance : BooleanAlgebra Bool where
compl := not
top := true
bot := false
compl_sup_eq := fun x => by cases x <;> simp [Sup.sup, Bot.bot]
compl_inf_eq := fun x => by cases x <;> simp [Inf.inf, Bot.bot]
sup_bot_eq := fun x => by cases x <;> simp [Sup.sup, Bot.bot]
inf_top_eq := fun x => by cases x <;> simp [Inf.inf, Top.top]
The identity laws establish True and False as the identity elements:
-- Boolean identity laws
theorem bool_or_false (b : Bool) : b || false = b := by cases b <;> rfl
theorem bool_or_true (b : Bool) : b || true = true := by cases b <;> rfl
theorem bool_and_false (b : Bool) : b && false = false := by cases b <;> rfl
theorem bool_and_true (b : Bool) : b && true = b := by cases b <;> rfl
-- Propositional identity laws
theorem prop_or_false (P : Prop) : P ∨ False ↔ P := ⟨Or.resolve_right, Or.inl⟩
theorem prop_or_true (P : Prop) : P ∨ True ↔ True := ⟨fun _ => trivial, fun _ => Or.inr trivial⟩
theorem prop_and_false (P : Prop) : P ∧ False ↔ False := ⟨And.right, False.elim⟩
theorem prop_and_true (P : Prop) : P ∧ True ↔ P := ⟨And.left, fun h => ⟨h, trivial⟩⟩
Every element has a unique complement:
-- Boolean complement laws
theorem bool_or_not (b : Bool) : b || not b = true := by cases b <;> rfl
theorem bool_and_not (b : Bool) : b && not b = false := by cases b <;> rfl
-- Propositional complement laws (require classical logic for full strength)
open Classical
theorem prop_or_not (P : Prop) : P ∨ ¬P := em P
theorem prop_and_not (P : Prop) : ¬(P ∧ ¬P) := fun ⟨h, nh⟩ => nh h
-- Double negation
theorem bool_not_not (b : Bool) : not (not b) = b := by cases b <;> rfl
theorem prop_not_not (P : Prop) : ¬¬P → P := not_not
Operations with the same element twice simplify:
-- Boolean idempotency
theorem bool_or_self (b : Bool) : b || b = b := by cases b <;> rfl
theorem bool_and_self (b : Bool) : b && b = b := by cases b <;> rfl
-- Propositional idempotency
theorem prop_or_self (P : Prop) : P ∨ P ↔ P := ⟨Or.resolve_left id, Or.inl⟩
theorem prop_and_self (P : Prop) : P ∧ P ↔ P := ⟨And.left, fun h => ⟨h, h⟩⟩
Order of operands doesn’t matter:
-- Boolean commutativity
theorem bool_or_comm (b c : Bool) : b || c = c || b := by cases b <;> cases c <;> rfl
theorem bool_and_comm (b c : Bool) : b && c = c && b := by cases b <;> cases c <;> rfl
-- Propositional commutativity
theorem prop_or_comm (P Q : Prop) : P ∨ Q ↔ Q ∨ P := Or.comm
theorem prop_and_comm (P Q : Prop) : P ∧ Q ↔ Q ∧ P := And.comm
Grouping doesn’t matter:
-- Boolean associativity
theorem bool_or_assoc (a b c : Bool) : a || (b || c) = (a || b) || c := by
cases a <;> cases b <;> cases c <;> rfl
theorem bool_and_assoc (a b c : Bool) : a && (b && c) = (a && b) && c := by
cases a <;> cases b <;> cases c <;> rfl
-- Propositional associativity
theorem prop_or_assoc (P Q R : Prop) : P ∨ (Q ∨ R) ↔ (P ∨ Q) ∨ R := Or.assoc
theorem prop_and_assoc (P Q R : Prop) : P ∧ (Q ∧ R) ↔ (P ∧ Q) ∧ R := And.assoc
Operations distribute over each other:
-- Boolean distributivity
theorem bool_and_or_distrib (a b c : Bool) :
a && (b || c) = (a && b) || (a && c) := by
cases a <;> cases b <;> cases c <;> rfl
theorem bool_or_and_distrib (a b c : Bool) :
a || (b && c) = (a || b) && (a || c) := by
cases a <;> cases b <;> cases c <;> rfl
-- Propositional distributivity
theorem prop_and_or_distrib (P Q R : Prop) :
P ∧ (Q ∨ R) ↔ (P ∧ Q) ∨ (P ∧ R) := by
constructor
· intro ⟨hp, hqr⟩
cases hqr with
| inl hq => exact Or.inl ⟨hp, hq⟩
| inr hr => exact Or.inr ⟨hp, hr⟩
· intro h
cases h with
| inl ⟨hp, hq⟩ => exact ⟨hp, Or.inl hq⟩
| inr ⟨hp, hr⟩ => exact ⟨hp, Or.inr hr⟩
theorem prop_or_and_distrib (P Q R : Prop) :
P ∨ (Q ∧ R) ↔ (P ∨ Q) ∧ (P ∨ R) := by
constructor
· intro h
cases h with
| inl hp => exact ⟨Or.inl hp, Or.inl hp⟩
| inr ⟨hq, hr⟩ => exact ⟨Or.inr hq, Or.inr hr⟩
· intro ⟨hpq, hpr⟩
cases hpq with
| inl hp => exact Or.inl hp
| inr hq =>
cases hpr with
| inl hp => exact Or.inl hp
| inr hr => exact Or.inr ⟨hq, hr⟩
Fundamental relationship between conjunction, disjunction, and negation:
-- Boolean De Morgan's laws
theorem bool_not_and (b c : Bool) : not (b && c) = (not b) || (not c) := by
cases b <;> cases c <;> rfl
theorem bool_not_or (b c : Bool) : not (b || c) = (not b) && (not c) := by
cases b <;> cases c <;> rfl
-- Propositional De Morgan's laws
theorem prop_not_and (P Q : Prop) : ¬(P ∧ Q) ↔ ¬P ∨ ¬Q := by
constructor
· intro h
by_cases hp : P
· by_cases hq : Q
· exact False.elim (h ⟨hp, hq⟩)
· exact Or.inr hq
· exact Or.inl hp
· intro h ⟨hp, hq⟩
cases h with
| inl hnp => exact hnp hp
| inr hnq => exact hnq hq
theorem prop_not_or (P Q : Prop) : ¬(P ∨ Q) ↔ ¬P ∧ ¬Q := by
constructor
· intro h
constructor
· intro hp; exact h (Or.inl hp)
· intro hq; exact h (Or.inr hq)
· intro ⟨hnp, hnq⟩ h
cases h with
| inl hp => exact hnp hp
| inr hq => exact hnq hq
These fundamental laws show how operations can “absorb” each other:
-- Boolean absorption laws
theorem bool_and_or_absorb (b c : Bool) : b && (b || c) = b := by
cases b <;> cases c <;> rfl
theorem bool_or_and_absorb (b c : Bool) : b || (b && c) = b := by
cases b <;> cases c <;> rfl
-- Propositional absorption laws
theorem prop_and_or_absorb (P Q : Prop) : P ∧ (P ∨ Q) ↔ P := by
constructor
· intro ⟨hp, _⟩; exact hp
· intro hp; exact ⟨hp, Or.inl hp⟩
theorem prop_or_and_absorb (P Q : Prop) : P ∨ (P ∧ Q) ↔ P := by
constructor
· intro h
cases h with
| inl hp => exact hp
| inr ⟨hp, _⟩ => exact hp
· intro hp; exact Or.inl hp
These show how certain expressions can be simplified:
-- Consensus theorem for Boolean algebra
theorem bool_consensus (a b c : Bool) :
(a && b) || (not a && c) || (b && c) = (a && b) || (not a && c) := by
cases a <;> cases b <;> cases c <;> rfl
-- The third term (b && c) is redundant when the first two are present
theorem prop_consensus (P Q R : Prop) :
(P ∧ Q) ∨ (¬P ∧ R) ∨ (Q ∧ R) ↔ (P ∧ Q) ∨ (¬P ∧ R) := by
constructor
· intro h
cases h with
| inl hpq => exact Or.inl hpq
| inr h' =>
cases h' with
| inl hnpr => exact Or.inr hnpr
| inr hqr =>
by_cases hp : P
· exact Or.inl ⟨hp, hqr.1⟩
· exact Or.inr ⟨hp, hqr.2⟩
· intro h; cases h <;> [exact Or.inl ‹_›; exact Or.inr (Or.inl ‹_›)]
Show when certain terms become unnecessary:
-- If P implies Q, then P ∨ Q = Q and P ∧ Q = P
theorem redundancy_or (P Q : Prop) (h : P → Q) : P ∨ Q ↔ Q := by
constructor
· intro hpq
cases hpq with
| inl hp => exact h hp
| inr hq => exact hq
· intro hq; exact Or.inr hq
theorem redundancy_and (P Q : Prop) (h : P → Q) : P ∧ Q ↔ P := by
constructor
· intro ⟨hp, _⟩; exact hp
· intro hp; exact ⟨hp, h hp⟩
For boolean expressions, exhaustive case analysis:
-- Example: proving a complex identity by cases
theorem bool_complex_identity (a b c : Bool) :
(a || b) && (a || c) && (b || c) = (a || b) && (a || c) := by
cases a <;> cases b <;> cases c <;> rfl
Using previously proven laws to derive new results:
-- Example: algebraic proof of absorption
theorem absorption_algebraic_proof (P Q : Prop) : P ∨ (P ∧ Q) ↔ P := by
calc P ∨ (P ∧ Q)
↔ P ∨ (P ∧ Q) ∨ False := by rw [prop_or_false]
_ ↔ P ∨ (P ∧ Q) ∨ (P ∧ ¬P) := by rw [← prop_and_not]
_ ↔ P ∨ (P ∧ (Q ∨ ¬P)) := by rw [← prop_and_or_distrib]
_ ↔ P ∨ (P ∧ (Q ∨ ¬P)) := by rfl
_ ↔ P ∨ (P ∧ True) := by rw [← prop_or_not]
_ ↔ P ∨ P := by rw [prop_and_true]
_ ↔ P := by rw [prop_or_self]
Direct construction without case analysis:
-- Constructive proof showing the essence of the law
theorem constructive_absorption (P Q : Prop) : P ∨ (P ∧ Q) → P := by
intro h
cases h with
| inl hp => exact hp
| inr ⟨hp, _⟩ => exact hp
Boolean algebra forms a special kind of lattice—a complemented distributive lattice:
-- Boolean algebra is a bounded distributive lattice with complements
theorem boolean_lattice_properties :
∀ (a b c : Bool),
-- Idempotent
(a || a = a) ∧
(a && a = a) ∧
-- Absorption
(a || (a && b) = a) ∧
(a && (a || b) = a) ∧
-- Distributive
(a || (b && c) = (a || b) && (a || c)) ∧
(a && (b || c) = (a && b) || (a && c)) ∧
-- Complement
(a || not a = true) ∧
(a && not a = false) := by
intro a b c
cases a <;> cases b <;> cases c <;> simp [Bool.and_assoc, Bool.or_assoc]
These laws enable systematic simplification of logical expressions:
-- Example: simplifying a complex boolean expression
def complex_expr (a b c : Bool) : Bool :=
(a && b) || (a && not b && c) || (not a && b && c) || (not a && not b)
-- Simplified form
def simplified_expr (a b c : Bool) : Bool :=
(a && b) || (not a && not b) || (not b && c)
-- Proof they're equivalent
theorem simplification_correct (a b c : Bool) :
complex_expr a b c = simplified_expr a b c := by
cases a <;> cases b <;> cases c <;> rfl
-- General simplification strategy
theorem simplification_strategy (P Q R : Prop) :
(P ∧ Q) ∨ (P ∧ ¬Q ∧ R) ∨ (¬P ∧ Q ∧ R) ∨ (¬P ∧ ¬Q) ↔
(P ∧ Q) ∨ (¬P ∧ ¬Q) ∨ (¬Q ∧ R) := by
-- This would require a longer proof, but demonstrates the principle
sorry
These laws form the theoretical foundation for:
The beauty of Boolean algebra lies in its dual nature: providing both computational tools for working with
Bool
values and logical principles for reasoning about propositions in Prop
.