chore: bool and prop lemmas for Mathlib compatibility and improved confluence (#3508)

This adds a number of lemmas for simplification of `Bool` and `Prop`
terms. It pulls lemmas from Mathlib and adds additional lemmas where
confluence or consistency suggested they are needed.

It has been tested against Mathlib using some automated test
infrastructure.

That testing module is not yet included in this PR, but will be included
as part of this.

Note. There are currently some comments saying the origin of the simp
rule. These will be removed prior to merging, but are added to clarify
where the rule came from during review.

---------

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>
This commit is contained in:
Joe Hendrix 2024-03-04 15:56:30 -08:00 committed by GitHub
parent 37450d47e2
commit 01104cc81e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 2017 additions and 137 deletions

View file

@ -277,14 +277,13 @@ theorem BinTree.find_insert (b : BinTree β) (k : Nat) (v : β)
. by_cases' key < k
cases h; apply ihr; assumption
theorem BinTree.find_insert_of_ne (b : BinTree β) (h : k ≠ k') (v : β)
theorem BinTree.find_insert_of_ne (b : BinTree β) (ne : k ≠ k') (v : β)
: (b.insert k v).find? k' = b.find? k' := by
let ⟨t, h⟩ := b; simp
induction t with simp
| leaf =>
intros
have_eq k k'
contradiction
intros le
exact Nat.lt_of_le_of_ne le ne
| node left key value right ihl ihr =>
let .node hl hr bl br := h
specialize ihl bl

View file

@ -37,15 +37,6 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
f (ite P x y) = ite P (f x) (f y) :=
apply_dite f P (fun _ => x) (fun _ => y)
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp] theorem dite_not (P : Prop) {_ : Decidable P} (x : ¬P → α) (y : ¬¬P → α) :
dite (¬P) x y = dite P (fun h => y (not_not_intro h)) x := by
by_cases h : P <;> simp [h]
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp] theorem ite_not (P : Prop) {_ : Decidable P} (x y : α) : ite (¬P) x y = ite P y x :=
dite_not P (fun _ => x) (fun _ => y)
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P → α} :
dite P (fun _ => a) B = a ↔ ∀ h, B h = a := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]

View file

@ -125,16 +125,15 @@ theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
/-- The Double Negation Theorem: `¬¬P` is equivalent to `P`.
The left-to-right direction, double negation elimination (DNE),
is classically true but not constructively. -/
@[scoped simp] theorem not_not : ¬¬a ↔ a := Decidable.not_not
@[simp] theorem not_not : ¬¬a ↔ a := Decidable.not_not
@[simp] theorem not_forall {p : α → Prop} : (¬∀ x, p x) ↔ ∃ x, ¬p x := Decidable.not_forall
@[simp low] theorem not_forall {p : α → Prop} : (¬∀ x, p x) ↔ ∃ x, ¬p x := Decidable.not_forall
theorem not_forall_not {p : α → Prop} : (¬∀ x, ¬p x) ↔ ∃ x, p x := Decidable.not_forall_not
theorem not_exists_not {p : α → Prop} : (¬∃ x, ¬p x) ↔ ∀ x, p x := Decidable.not_exists_not
theorem forall_or_exists_not (P : α → Prop) : (∀ a, P a) ∃ a, ¬ P a := by
rw [← not_forall]; exact em _
theorem exists_or_forall_not (P : α → Prop) : (∃ a, P a) ∀ a, ¬ P a := by
rw [← not_exists]; exact em _
@ -147,8 +146,22 @@ theorem not_and_iff_or_not_not : ¬(a ∧ b) ↔ ¬a ¬b := Decidable.not_an
theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
@[simp] theorem imp_iff_left_iff : (b ↔ a → b) ↔ a b := Decidable.imp_iff_left_iff
@[simp] theorem imp_iff_right_iff : (a → b ↔ b) ↔ a b := Decidable.imp_iff_right_iff
@[simp] theorem and_or_imp : a ∧ b (a → c) ↔ a → b c := Decidable.and_or_imp
@[simp] theorem not_imp : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff_and_not
@[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p → q) ∧ (¬p → q) ↔ q :=
Iff.intro (fun (a : _ ∧ _) => (Classical.em p).rec a.left a.right)
(fun a => And.intro (fun _ => a) (fun _ => a))
end Classical
/- Export for Mathlib compat. -/
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Extract an element from a existential statement, using `Classical.choose`. -/
-- This enables projection notation.
@[reducible] noncomputable def Exists.choose {p : α → Prop} (P : ∃ a, p a) : α := Classical.choose P

View file

@ -677,7 +677,7 @@ You can prove theorems about the resulting element by induction on `h`, since
theorem Eq.substr {α : Sort u} {p : α → Prop} {a b : α} (h₁ : b = a) (h₂ : p a) : p b :=
h₁ ▸ h₂
theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
@[simp] theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
rfl
/--
@ -1403,9 +1403,9 @@ theorem false_imp_iff (a : Prop) : (False → a) ↔ True := iff_true_intro Fals
theorem true_imp_iff (α : Prop) : (True → α) ↔ α := imp_iff_right True.intro
@[simp] theorem imp_self : (a → a) ↔ True := iff_true_intro id
@[simp high] theorem imp_self : (a → a) ↔ True := iff_true_intro id
theorem imp_false : (a → False) ↔ ¬a := Iff.rfl
@[simp] theorem imp_false : (a → False) ↔ ¬a := Iff.rfl
theorem imp.swap : (a → b → c) ↔ (b → a → c) := Iff.intro flip flip

View file

@ -29,6 +29,8 @@ instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∃ x, p x) :
| _, isTrue hf => isTrue ⟨_, hf⟩
| isFalse ht, isFalse hf => isFalse fun | ⟨true, h⟩ => absurd h ht | ⟨false, h⟩ => absurd h hf
@[simp] theorem default_bool : default = false := rfl
instance : LE Bool := ⟨(. → .)⟩
instance : LT Bool := ⟨(!. && .)⟩
@ -48,85 +50,202 @@ theorem ne_false_iff : {b : Bool} → b ≠ false ↔ b = true := by decide
theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} : decide (false = b) = !b := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
/-! ### and -/
@[simp] theorem not_and_self : ∀ (x : Bool), (!x && x) = false := by decide
@[simp] theorem and_self_left : ∀(a b : Bool), (a && (a && b)) = (a && b) := by decide
@[simp] theorem and_self_right : ∀(a b : Bool), ((a && b) && b) = (a && b) := by decide
@[simp] theorem not_and_self : ∀ (x : Bool), (!x && x) = false := by decide
@[simp] theorem and_not_self : ∀ (x : Bool), (x && !x) = false := by decide
/-
Added for confluence with `not_and_self` `and_not_self` on term
`(b && !b) = true` due to reductions:
1. `(b = true !b = true)` via `Bool.and_eq_true`
2. `false = true` via `Bool.and_not_self`
-/
@[simp] theorem eq_true_and_eq_false_self : ∀(b : Bool), (b = true ∧ b = false) ↔ False := by decide
@[simp] theorem eq_false_and_eq_true_self : ∀(b : Bool), (b = false ∧ b = true) ↔ False := by decide
theorem and_comm : ∀ (x y : Bool), (x && y) = (y && x) := by decide
theorem and_left_comm : ∀ (x y z : Bool), (x && (y && z)) = (y && (x && z)) := by decide
theorem and_right_comm : ∀ (x y z : Bool), ((x && y) && z) = ((x && z) && y) := by decide
theorem and_or_distrib_left : ∀ (x y z : Bool), (x && (y || z)) = ((x && y) || (x && z)) := by
decide
/-
Bool version `and_iff_left_iff_imp`.
theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = ((x && z) || (y && z)) := by
decide
theorem and_xor_distrib_left : ∀ (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_right : ∀ (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by
decide
/-- De Morgan's law for boolean and -/
theorem not_and : ∀ (x y : Bool), (!(x && y)) = (!x || !y) := by decide
theorem and_eq_true_iff : ∀ (x y : Bool), (x && y) = true ↔ x = true ∧ y = true := by decide
theorem and_eq_false_iff : ∀ (x y : Bool), (x && y) = false ↔ x = false y = false := by decide
Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` via
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem and_iff_left_iff_imp : ∀(a b : Bool), ((a && b) = a) ↔ (a → b) := by decide
@[simp] theorem and_iff_right_iff_imp : ∀(a b : Bool), ((a && b) = b) ↔ (b → a) := by decide
@[simp] theorem iff_self_and : ∀(a b : Bool), (a = (a && b)) ↔ (a → b) := by decide
@[simp] theorem iff_and_self : ∀(a b : Bool), (b = (a && b)) ↔ (b → a) := by decide
/-! ### or -/
@[simp] theorem not_or_self : ∀ (x : Bool), (!x || x) = true := by decide
@[simp] theorem or_self_left : ∀(a b : Bool), (a || (a || b)) = (a || b) := by decide
@[simp] theorem or_self_right : ∀(a b : Bool), ((a || b) || b) = (a || b) := by decide
@[simp] theorem not_or_self : ∀ (x : Bool), (!x || x) = true := by decide
@[simp] theorem or_not_self : ∀ (x : Bool), (x || !x) = true := by decide
/-
Added for confluence with `not_or_self` `or_not_self` on term
`(b || !b) = true` due to reductions:
1. `(b = true !b = true)` via `Bool.or_eq_true`
2. `true = true` via `Bool.or_not_self`
-/
@[simp] theorem eq_true_or_eq_false_self : ∀(b : Bool), (b = true b = false) ↔ True := by decide
@[simp] theorem eq_false_or_eq_true_self : ∀(b : Bool), (b = false b = true) ↔ True := by decide
/-
Bool version `or_iff_left_iff_imp`.
Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` via
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem or_iff_left_iff_imp : ∀(a b : Bool), ((a || b) = a) ↔ (b → a) := by decide
@[simp] theorem or_iff_right_iff_imp : ∀(a b : Bool), ((a || b) = b) ↔ (a → b) := by decide
@[simp] theorem iff_self_or : ∀(a b : Bool), (a = (a || b)) ↔ (b → a) := by decide
@[simp] theorem iff_or_self : ∀(a b : Bool), (b = (a || b)) ↔ (a → b) := by decide
theorem or_comm : ∀ (x y : Bool), (x || y) = (y || x) := by decide
theorem or_left_comm : ∀ (x y z : Bool), (x || (y || z)) = (y || (x || z)) := by decide
theorem or_right_comm : ∀ (x y z : Bool), ((x || y) || z) = ((x || z) || y) := by decide
theorem or_and_distrib_left : ∀ (x y z : Bool), (x || (y && z)) = ((x || y) && (x || z)) := by
decide
/-! ### distributivity -/
theorem or_and_distrib_right : ∀ (x y z : Bool), ((x && y) || z) = ((x || z) && (y || z)) := by
decide
theorem and_or_distrib_left : ∀ (x y z : Bool), (x && (y || z)) = (x && y || x && z) := by decide
theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z || y && z) := by decide
theorem or_and_distrib_left : ∀ (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
theorem or_and_distrib_right : ∀ (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
/-- De Morgan's law for boolean and -/
@[simp] theorem not_and : ∀ (x y : Bool), (!(x && y)) = (!x || !y) := by decide
/-- De Morgan's law for boolean or -/
theorem not_or : ∀ (x y : Bool), (!(x || y)) = (!x && !y) := by decide
@[simp] theorem not_or : ∀ (x y : Bool), (!(x || y)) = (!x && !y) := by decide
theorem or_eq_true_iff : ∀ (x y : Bool), (x || y) = true ↔ x = true y = true := by decide
theorem and_eq_true_iff (x y : Bool) : (x && y) = true ↔ x = true ∧ y = true :=
Iff.of_eq (and_eq_true x y)
theorem or_eq_false_iff : ∀ (x y : Bool), (x || y) = false ↔ x = false ∧ y = false := by decide
theorem and_eq_false_iff : ∀ (x y : Bool), (x && y) = false ↔ x = false y = false := by decide
/-
New simp rule that replaces `Bool.and_eq_false_eq_eq_false_or_eq_false` in
Mathlib due to confluence:
Consider the term: `¬((b && c) = true)`:
1. Reduces to `((b && c) = false)` via `Bool.not_eq_true`
2. Reduces to `¬(b = true ∧ c = true)` via `Bool.and_eq_true`.
1. Further reduces to `b = false c = false` via `Bool.and_eq_false_eq_eq_false_or_eq_false`.
2. Further reduces to `b = true → c = false` via `not_and` and `Bool.not_eq_true`.
-/
@[simp] theorem and_eq_false_imp : ∀ (x y : Bool), (x && y) = false ↔ (x = true → y = false) := by decide
@[simp] theorem or_eq_true_iff : ∀ (x y : Bool), (x || y) = true ↔ x = true y = true := by decide
@[simp] theorem or_eq_false_iff : ∀ (x y : Bool), (x || y) = false ↔ x = false ∧ y = false := by decide
/-! ### eq/beq/bne -/
/--
These two rules follow trivially by simp, but are needed to avoid non-termination
in false_eq and true_eq.
-/
@[simp] theorem false_eq_true : (false = true) = False := by simp
@[simp] theorem true_eq_false : (true = false) = False := by simp
-- The two lemmas below normalize terms with a constant to the
-- right-hand side but risk non-termination if `false_eq_true` and
-- `true_eq_false` are disabled.
@[simp low] theorem false_eq (b : Bool) : (false = b) = (b = false) := by
cases b <;> simp
@[simp low] theorem true_eq (b : Bool) : (true = b) = (b = true) := by
cases b <;> simp
@[simp] theorem true_beq : ∀b, (true == b) = b := by decide
@[simp] theorem false_beq : ∀b, (false == b) = !b := by decide
@[simp] theorem beq_true : ∀b, (b == true) = b := by decide
@[simp] theorem beq_false : ∀b, (b == false) = !b := by decide
@[simp] theorem true_bne : ∀(b : Bool), (true != b) = !b := by decide
@[simp] theorem false_bne : ∀(b : Bool), (false != b) = b := by decide
@[simp] theorem bne_true : ∀(b : Bool), (b != true) = !b := by decide
@[simp] theorem bne_false : ∀(b : Bool), (b != false) = b := by decide
@[simp] theorem not_beq_self : ∀ (x : Bool), ((!x) == x) = false := by decide
@[simp] theorem beq_not_self : ∀ (x : Bool), (x == !x) = false := by decide
@[simp] theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
@[simp] theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
/-
Added for equivalence with `Bool.not_beq_self` and needed for confluence
due to `beq_iff_eq`.
-/
@[simp] theorem not_eq_self : ∀(b : Bool), ((!b) = b) ↔ False := by decide
@[simp] theorem eq_not_self : ∀(b : Bool), (b = (!b)) ↔ False := by decide
@[simp] theorem beq_self_left : ∀(a b : Bool), (a == (a == b)) = b := by decide
@[simp] theorem beq_self_right : ∀(a b : Bool), ((a == b) == b) = a := by decide
@[simp] theorem bne_self_left : ∀(a b : Bool), (a != (a != b)) = b := by decide
@[simp] theorem bne_self_right : ∀(a b : Bool), ((a != b) != b) = a := by decide
@[simp] theorem not_bne_not : ∀ (x y : Bool), ((!x) != (!y)) = (x != y) := by decide
@[simp] theorem bne_assoc : ∀ (x y z : Bool), ((x != y) != z) = (x != (y != z)) := by decide
@[simp] theorem bne_left_inj : ∀ (x y z : Bool), (x != y) = (x != z) ↔ y = z := by decide
@[simp] theorem bne_right_inj : ∀ (x y z : Bool), (x != z) = (y != z) ↔ x = y := by decide
/-! ### coercision related normal forms -/
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
@[simp] theorem not_not_eq : ∀ {a b : Bool}, ¬(!a) = b ↔ a = b := by decide
@[simp] theorem coe_iff_coe : ∀(a b : Bool), (a ↔ b) ↔ a = b := by decide
@[simp] theorem coe_true_iff_false : ∀(a b : Bool), (a ↔ b = false) ↔ a = (!b) := by decide
@[simp] theorem coe_false_iff_true : ∀(a b : Bool), (a = false ↔ b) ↔ (!a) = b := by decide
@[simp] theorem coe_false_iff_false : ∀(a b : Bool), (a = false ↔ b = false) ↔ (!a) = (!b) := by decide
/-! ### xor -/
@[simp] theorem false_xor : ∀ (x : Bool), xor false x = x := by decide
theorem false_xor : ∀ (x : Bool), xor false x = x := false_bne
@[simp] theorem xor_false : ∀ (x : Bool), xor x false = x := by decide
theorem xor_false : ∀ (x : Bool), xor x false = x := bne_false
@[simp] theorem true_xor : ∀ (x : Bool), xor true x = !x := by decide
theorem true_xor : ∀ (x : Bool), xor true x = !x := true_bne
@[simp] theorem xor_true : ∀ (x : Bool), xor x true = !x := by decide
theorem xor_true : ∀ (x : Bool), xor x true = !x := bne_true
@[simp] theorem not_xor_self : ∀ (x : Bool), xor (!x) x = true := by decide
theorem not_xor_self : ∀ (x : Bool), xor (!x) x = true := not_bne_self
@[simp] theorem xor_not_self : ∀ (x : Bool), xor x (!x) = true := by decide
theorem xor_not_self : ∀ (x : Bool), xor x (!x) = true := bne_not_self
theorem not_xor : ∀ (x y : Bool), xor (!x) y = !(xor x y) := by decide
theorem xor_not : ∀ (x y : Bool), xor x (!y) = !(xor x y) := by decide
@[simp] theorem not_xor_not : ∀ (x y : Bool), xor (!x) (!y) = (xor x y) := by decide
theorem not_xor_not : ∀ (x y : Bool), xor (!x) (!y) = (xor x y) := not_bne_not
theorem xor_self : ∀ (x : Bool), xor x x = false := by decide
@ -136,13 +255,11 @@ theorem xor_left_comm : ∀ (x y z : Bool), xor x (xor y z) = xor y (xor x z) :=
theorem xor_right_comm : ∀ (x y z : Bool), xor (xor x y) z = xor (xor x z) y := by decide
theorem xor_assoc : ∀ (x y z : Bool), xor (xor x y) z = xor x (xor y z) := by decide
theorem xor_assoc : ∀ (x y z : Bool), xor (xor x y) z = xor x (xor y z) := bne_assoc
@[simp]
theorem xor_left_inj : ∀ (x y z : Bool), xor x y = xor x z ↔ y = z := by decide
theorem xor_left_inj : ∀ (x y z : Bool), xor x y = xor x z ↔ y = z := bne_left_inj
@[simp]
theorem xor_right_inj : ∀ (x y z : Bool), xor x z = xor y z ↔ x = y := by decide
theorem xor_right_inj : ∀ (x y z : Bool), xor x z = xor y z ↔ x = y := bne_right_inj
/-! ### le/lt -/
@ -227,16 +344,147 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 ↔ b = false := by
cases b <;> simp
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 ↔ b = true := by
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 ↔ b = true := by
cases b <;> simp
end Bool
/-! ### ite -/
@[simp] theorem if_true_left (p : Prop) [h : Decidable p] (f : Bool) :
(ite p true f) = (p || f) := by cases h with | _ p => simp [p]
@[simp] theorem if_false_left (p : Prop) [h : Decidable p] (f : Bool) :
(ite p false f) = (!p && f) := by cases h with | _ p => simp [p]
@[simp] theorem if_true_right (p : Prop) [h : Decidable p] (t : Bool) :
(ite p t true) = (!(p : Bool) || t) := by cases h with | _ p => simp [p]
@[simp] theorem if_false_right (p : Prop) [h : Decidable p] (t : Bool) :
(ite p t false) = (p && t) := by cases h with | _ p => simp [p]
@[simp] theorem ite_eq_true_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
(ite p t f = true) = ite p (t = true) (f = true) := by
cases h with | _ p => simp [p]
@[simp] theorem ite_eq_false_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
(ite p t f = false) = ite p (t = false) (f = false) := by
cases h with | _ p => simp [p]
/-
`not_ite_eq_true_eq_true` and related theorems below are added for
non-confluence. A motivating example is
`¬((if u then b else c) = true)`.
This reduces to:
1. `¬((if u then (b = true) else (c = true))` via `ite_eq_true_distrib`
2. `(if u then b c) = false)` via `Bool.not_eq_true`.
Similar logic holds for `¬((if u then b else c) = false)` and related
lemmas.
-/
@[simp]
theorem not_ite_eq_true_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = true) (c = true)) ↔ (ite p (b = false) (c = false)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = false) (c = false)) ↔ (ite p (b = true) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_true_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = true) (c = false)) ↔ (ite p (b = false) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
¬(ite p (b = false) (c = true)) ↔ (ite p (b = true) (c = false)) := by
cases h with | _ p => simp [p]
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`
-/
@[simp] theorem eq_false_imp_eq_true : ∀(b:Bool), (b = false → b = true) ↔ (b = true) := by decide
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`
-/
@[simp] theorem eq_true_imp_eq_false : ∀(b:Bool), (b = true → b = false) ↔ (b = false) := by decide
/-! ### cond -/
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := by
theorem cond_eq_ite {α} (b : Bool) (t e : α) : cond b t e = if b then t else e := by
cases b <;> simp
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite b x y
@[simp] theorem cond_not (b : Bool) (t e : α) : cond (!b) t e = cond b e t := by
cases b <;> rfl
@[simp] theorem cond_self (c : Bool) (t : α) : cond c t t = t := by cases c <;> rfl
/-
This is a simp rule in Mathlib, but results in non-confluence that is
difficult to fix as decide distributes over propositions.
A possible fix would be to completely simplify away `cond`, but that
is not taken since it could result in major rewriting of code that is
otherwise purely about `Bool`.
-/
theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
cond (decide p) t e = if p then t else e := by
simp [cond_eq_ite]
@[simp] theorem cond_eq_ite_iff (a : Bool) (p : Prop) [h : Decidable p] (x y u v : α) :
(cond a x y = ite p u v) ↔ ite a x y = ite p u v := by
simp [Bool.cond_eq_ite]
@[simp] theorem ite_eq_cond_iff (p : Prop) [h : Decidable p] (a : Bool) (x y u v : α) :
(ite p x y = cond a u v) ↔ ite p x y = ite a u v := by
simp [Bool.cond_eq_ite]
@[simp] theorem cond_eq_true_distrib : ∀(c t f : Bool),
(cond c t f = true) = ite (c = true) (t = true) (f = true) := by
decide
@[simp] theorem cond_eq_false_distrib : ∀(c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
@[simp] theorem cond_true_left : ∀(c f : Bool), cond c true f = ( c || f) := by decide
@[simp] theorem cond_false_left : ∀(c f : Bool), cond c false f = (!c && f) := by decide
@[simp] theorem cond_true_right : ∀(c t : Bool), cond c t true = (!c || t) := by decide
@[simp] theorem cond_false_right : ∀(c t : Bool), cond c t false = ( c && t) := by decide
@[simp] theorem cond_true_same : ∀(c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_false_same : ∀(c b : Bool), cond c b c = (c && b) := by decide
/-# decidability -/
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
@[simp] theorem decide_and (p q : Prop) [dpq : Decidable (p ∧ q)] [dp : Decidable p] [dq : Decidable q] :
decide (p ∧ q) = (p && q) := by
cases dp with | _ p => simp [p]
@[simp] theorem decide_or (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
decide (p q) = (p || q) := by
cases dp with | _ p => simp [p]
@[simp] theorem decide_iff_dist (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
decide (p ↔ q) = (decide p == decide q) := by
cases dp with | _ p => simp [p]
end Bool
export Bool (cond_eq_if)
/-! ### decide -/
@[simp] theorem false_eq_decide_iff {p : Prop} [h : Decidable p] : false = decide p ↔ ¬p := by

View file

@ -687,7 +687,7 @@ decreasing_by decreasing_with
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) → Sort _} {zero succ} :
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction]; simp; rfl
rw [reverseInduction]; simp
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) → Sort _} {zero succ}
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =

View file

@ -69,7 +69,7 @@ theorem mem_cons_self (a : α) (l : List α) : a ∈ a :: l := .head ..
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a ∈ l → a ∈ y :: l := .tail _
theorem eq_nil_iff_forall_not_mem {l : List α} : l = [] ↔ ∀ a, a ∉ l := by
cases l <;> simp
cases l <;> simp [-not_or]
/-! ### append -/
@ -451,9 +451,9 @@ theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
induction as with
| nil => simp [filter]
| cons a as ih =>
by_cases h : p a <;> simp [*, or_and_right]
· exact or_congr_left (and_iff_left_of_imp fun | rfl => h).symm
· exact (or_iff_right fun ⟨rfl, h'⟩ => h h').symm
by_cases h : p a
· simp_all [or_and_left]
· simp_all [or_and_right]
theorem filter_eq_nil {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]

View file

@ -256,25 +256,24 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
theorem testBit_one_zero : testBit 1 0 = true := by trivial
theorem not_decide_mod_two_eq_one (x : Nat)
: (!decide (x % 2 = 1)) = decide (x % 2 = 0) := by
cases Nat.mod_two_eq_zero_or_one x <;> (rename_i p; simp [p])
theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
testBit (2^n - (x + 1)) i = (decide (i < n) && ! testBit x i) := by
induction i generalizing n x with
| zero =>
simp only [testBit_zero, zero_eq, Bool.and_eq_true, decide_eq_true_eq,
Bool.not_eq_true']
match n with
| 0 => simp
| n+1 =>
-- just logic + omega:
simp only [zero_lt_succ, decide_True, Bool.true_and]
rw [← decide_not, decide_eq_decide]
simp [not_decide_mod_two_eq_one]
omega
| succ i ih =>
simp only [testBit_succ]
match n with
| 0 =>
simp only [Nat.pow_zero, succ_sub_succ_eq_sub, Nat.zero_sub, Nat.zero_div, zero_testBit]
rw [decide_eq_false] <;> simp
simp [decide_eq_false]
| n+1 =>
rw [Nat.two_pow_succ_sub_succ_div_two, ih]
· simp [Nat.succ_lt_succ_iff]

View file

@ -11,6 +11,18 @@ import Init.Core
import Init.NotationExtra
set_option linter.missingDocs true -- keep it documented
/-! ## cast and equality -/
@[simp] theorem eq_mp_eq_cast (h : α = β) : Eq.mp h = cast h := rfl
@[simp] theorem eq_mpr_eq_cast (h : α = β) : Eq.mpr h = cast h.symm := rfl
@[simp] theorem cast_cast : ∀ (ha : α = β) (hb : β = γ) (a : α),
cast hb (cast ha a) = cast (ha.trans hb) a
| rfl, rfl, _ => rfl
@[simp] theorem eq_true_eq_id : Eq True = id := by
funext _; simp only [true_iff, id.def, eq_iff_iff]
/-! ## not -/
theorem not_not_em (a : Prop) : ¬¬(a ¬a) := fun h => h (.inr (h ∘ .inl))
@ -104,10 +116,62 @@ theorem and_or_right : (a ∧ b) c ↔ (a c) ∧ (b c) := by rw [@or
theorem or_imp : (a b → c) ↔ (a → c) ∧ (b → c) :=
Iff.intro (fun h => ⟨h ∘ .inl, h ∘ .inr⟩) (fun ⟨ha, hb⟩ => Or.rec ha hb)
theorem not_or : ¬(p q) ↔ ¬p ∧ ¬q := or_imp
/-
`not_or` is made simp for confluence with `¬((b || c) = true)`:
Critical pair:
1. `¬(b = true c = true)` via `Bool.or_eq_true`.
2. `(b || c = false)` via `Bool.not_eq_true` which then
reduces to `b = false ∧ c = false` via Mathlib simp lemma
`Bool.or_eq_false_eq_eq_false_and_eq_false`.
Both reduce to `b = false ∧ c = false` via `not_or`.
-/
@[simp] theorem not_or : ¬(p q) ↔ ¬p ∧ ¬q := or_imp
theorem not_and_of_not_or_not (h : ¬a ¬b) : ¬(a ∧ b) := h.elim (mt (·.1)) (mt (·.2))
/-! ## Ite -/
@[simp]
theorem if_false_left [h : Decidable p] :
ite p False q ↔ ¬p ∧ q := by cases h <;> (rename_i g; simp [g])
@[simp]
theorem if_false_right [h : Decidable p] :
ite p q False ↔ p ∧ q := by cases h <;> (rename_i g; simp [g])
/-
`if_true_left` and `if_true_right` are lower priority because
they introduce disjunctions and we prefer `if_false_left` and
`if_false_right` if they overlap.
-/
@[simp low]
theorem if_true_left [h : Decidable p] :
ite p True q ↔ ¬p → q := by cases h <;> (rename_i g; simp [g])
@[simp low]
theorem if_true_right [h : Decidable p] :
ite p q True ↔ p → q := by cases h <;> (rename_i g; simp [g])
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp] theorem dite_not [hn : Decidable (¬p)] [h : Decidable p] (x : ¬p → α) (y : ¬¬p → α) :
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
cases h <;> (rename_i g; simp [g])
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp] theorem ite_not (p : Prop) [Decidable p] (x y : α) : ite (¬p) x y = ite p y x :=
dite_not (fun _ => x) (fun _ => y)
@[simp] theorem ite_true_same (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p → q) := by
cases h <;> (rename_i g; simp [g])
@[simp] theorem ite_false_same (p q : Prop) [h : Decidable p] : (if p then q else p) = (p ∧ q) := by
cases h <;> (rename_i g; simp [g])
/-! ## exists and forall -/
section quantifiers
@ -268,7 +332,14 @@ end quantifiers
/-! ## decidable -/
theorem Decidable.not_not [Decidable p] : ¬¬p ↔ p := ⟨of_not_not, not_not_intro⟩
@[simp] theorem Decidable.not_not [Decidable p] : ¬¬p ↔ p := ⟨of_not_not, not_not_intro⟩
/-- Excluded middle. Added as alias for Decidable.em -/
abbrev Decidable.or_not_self := em
/-- Excluded middle commuted. Added as alias for Decidable.em -/
theorem Decidable.not_or_self (p : Prop) [h : Decidable p] : ¬p p := by
cases h <;> simp [*]
theorem Decidable.by_contra [Decidable p] : (¬p → False) → p := of_not_not
@ -310,7 +381,7 @@ theorem Decidable.not_imp_symm [Decidable a] (h : ¬a → b) (hb : ¬b) : a :=
theorem Decidable.not_imp_comm [Decidable a] [Decidable b] : (¬a → b) ↔ (¬b → a) :=
⟨not_imp_symm, not_imp_symm⟩
theorem Decidable.not_imp_self [Decidable a] : (¬a → a) ↔ a := by
@[simp] theorem Decidable.not_imp_self [Decidable a] : (¬a → a) ↔ a := by
have := @imp_not_self (¬a); rwa [not_not] at this
theorem Decidable.or_iff_not_imp_left [Decidable a] : a b ↔ (¬a → b) :=
@ -389,8 +460,12 @@ theorem Decidable.and_iff_not_or_not [Decidable a] [Decidable b] : a ∧ b ↔
rw [← not_and_iff_or_not_not, not_not]
theorem Decidable.imp_iff_right_iff [Decidable a] : (a → b ↔ b) ↔ a b :=
⟨fun H => (Decidable.em a).imp_right fun ha' => H.1 fun ha => (ha' ha).elim,
fun H => H.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb⟩
Iff.intro
(fun h => (Decidable.em a).imp_right fun ha' => h.mp fun ha => (ha' ha).elim)
(fun ab => ab.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb)
theorem Decidable.imp_iff_left_iff [Decidable a] : (b ↔ a → b) ↔ a b :=
propext (@Iff.comm (a → b) b) ▸ (@Decidable.imp_iff_right_iff a b _)
theorem Decidable.and_or_imp [Decidable a] : a ∧ b (a → c) ↔ a → b c :=
if ha : a then by simp only [ha, true_and, true_imp_iff]
@ -435,3 +510,53 @@ protected theorem Decidable.not_forall_not {p : α → Prop} [Decidable (∃ x,
protected theorem Decidable.not_exists_not {p : α → Prop} [∀ x, Decidable (p x)] :
(¬∃ x, ¬p x) ↔ ∀ x, p x := by
simp only [not_exists, Decidable.not_not]
export Decidable (not_imp_self)
/-
`decide_implies` simp justification.
We have a critical pair from `decide (¬(p ∧ q))`:
1. `decide (p → ¬q)` via `not_and`
2. `!decide (p ∧ q)` via `decide_not` This further refines to
`!(decide p) || !(decide q)` via `Bool.decide_and` (in Mathlib) and
`Bool.not_and` (made simp in Mathlib).
We introduce `decide_implies` below and then both normalize to
`!(decide p) || !(decide q)`.
-/
@[simp]
theorem decide_implies (u v : Prop)
[duv : Decidable (u → v)] [du : Decidable u] {dv : u → Decidable v}
: decide (u → v) = dite u (fun h => @decide v (dv h)) (fun _ => true) :=
if h : u then by
simp [h]
else by
simp [h]
/-
`decide_ite` is needed to resolve critical pair with
We have a critical pair from `decide (ite p b c = true)`:
1. `ite p b c` via `decide_coe`
2. `decide (ite p (b = true) (c = true))` via `Bool.ite_eq_true_distrib`.
We introduce `decide_ite` so both normalize to `ite p b c`.
-/
@[simp]
theorem decide_ite (u : Prop) [du : Decidable u] (p q : Prop)
[dpq : Decidable (ite u p q)] [dp : Decidable p] [dq : Decidable q] :
decide (ite u p q) = ite u (decide p) (decide q) := by
cases du <;> simp [*]
/- Confluence for `ite_true_same` and `decide_ite`. -/
@[simp] theorem ite_true_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then decide p else b) = (decide p || b) := by
cases h <;> (rename_i pt; simp [pt])
/- Confluence for `ite_false_same` and `decide_ite`. -/
@[simp] theorem ite_false_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then b else decide p) = (decide p && b) := by
cases h <;> (rename_i pt; simp [pt])

View file

@ -15,12 +15,15 @@ theorem of_eq_false (h : p = False) : ¬ p := fun hp => False.elim (h.mp hp)
theorem eq_true (h : p) : p = True :=
propext ⟨fun _ => trivial, fun _ => h⟩
-- Adding this attribute needs `eq_true`.
attribute [simp] cast_heq
theorem eq_false (h : ¬ p) : p = False :=
propext ⟨fun h' => absurd h' h, fun h' => False.elim h'⟩
theorem eq_false' (h : p → False) : p = False := eq_false h
theorem eq_true_of_decide {p : Prop} {_ : Decidable p} (h : decide p = true) : p = True :=
theorem eq_true_of_decide {p : Prop} [Decidable p] (h : decide p = true) : p = True :=
eq_true (of_decide_eq_true h)
theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) : p = False :=
@ -124,6 +127,7 @@ end SimprocHelperLemmas
@[simp] theorem not_true_eq_false : (¬ True) = False := by decide
@[simp] theorem not_iff_self : ¬(¬a ↔ a) | H => iff_not_self H.symm
attribute [simp] iff_not_self
/-! ## and -/
@ -173,6 +177,11 @@ theorem or_iff_left_of_imp (hb : b → a) : (a b) ↔ a := Iff.intro (Or.r
@[simp] theorem or_iff_left_iff_imp : (a b ↔ a) ↔ (b → a) := Iff.intro (·.mp ∘ Or.inr) or_iff_left_of_imp
@[simp] theorem or_iff_right_iff_imp : (a b ↔ b) ↔ (a → b) := by rw [or_comm, or_iff_left_iff_imp]
@[simp] theorem iff_self_or (a b : Prop) : (a ↔ a b) ↔ (b → a) :=
propext (@Iff.comm _ a) ▸ @or_iff_left_iff_imp a b
@[simp] theorem iff_or_self (a b : Prop) : (b ↔ a b) ↔ (a → b) :=
propext (@Iff.comm _ b) ▸ @or_iff_right_iff_imp a b
/-# Bool -/
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
@ -199,9 +208,9 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
@[simp] theorem Bool.not_true : (!true) = false := by decide
@[simp] theorem Bool.not_false : (!false) = true := by decide
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_false' (b : Bool) : ((!b) = false) = (b = true) := by cases b <;> simp
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
@ -212,11 +221,14 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
@[simp] theorem decide_eq_true_eq {_ : Decidable p} : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not {h : Decidable p} : decide (¬ p) = !decide p := by cases h <;> rfl
@[simp] theorem not_decide_eq_true {h : Decidable p} : ((!decide p) = true) = ¬ p := by cases h <;> simp [decide, *]
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p :=
propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
cases g <;> (rename_i gp; simp [gp]; rfl)
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by
cases h <;> (rename_i hp; simp [decide, hp])
@[simp] theorem heq_eq_eq {α : Sort u} (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@[simp] theorem cond_true (a b : α) : cond true a b = a := rfl
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
@ -228,11 +240,29 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
@[simp] theorem decide_False : decide False = false := rfl
@[simp] theorem decide_True : decide True = true := rfl
@[simp] theorem decide_True : decide True = true := rfl
@[simp] theorem bne_iff_ne [BEq α] [LawfulBEq α] (a b : α) : a != b ↔ a ≠ b := by
simp [bne]; rw [← beq_iff_eq a b]; simp [-beq_iff_eq]
/-
Added for critical pair for `¬((a != b) = true)`
1. `(a != b) = false` via `Bool.not_eq_true`
2. `¬(a ≠ b)` via `bne_iff_ne`
These will both normalize to `a = b` with the first via `bne_eq_false_iff_eq`.
-/
@[simp] theorem beq_eq_false_iff_ne [BEq α] [LawfulBEq α]
(a b : α) : (a == b) = false ↔ a ≠ b := by
rw [ne_eq, ← beq_iff_eq a b]
cases a == b <;> decide
@[simp] theorem bne_eq_false_iff_eq [BEq α] [LawfulBEq α] (a b : α) :
(a != b) = false ↔ a = b := by
rw [bne, ← beq_iff_eq a b]
cases a == b <;> decide
/-# Nat -/
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a ≤ 0) = (a = 0) :=

View file

@ -7,6 +7,7 @@ prelude
import Lean.Elab.Tactic.ElabTerm
import Lean.Elab.Command
import Lean.Elab.Tactic.Meta
import Lean.Meta.CheckTactic
/-!
Commands to validate tactic results.
@ -18,15 +19,6 @@ open Lean.Meta CheckTactic
open Lean.Elab.Tactic
open Lean.Elab.Command
private def matchCheckGoalType (stx : Syntax) (goalType : Expr) : MetaM (Expr × Expr × Level) := do
let u ← mkFreshLevelMVar
let type ← mkFreshExprMVar (some (.sort u))
let val ← mkFreshExprMVar (some type)
let extType := mkAppN (.const ``CheckGoalType [u]) #[type, val]
if !(← isDefEq goalType extType) then
throwErrorAt stx "Goal{indentExpr goalType}\nis expected to match {indentExpr extType}"
pure (val, type, u)
@[builtin_command_elab Lean.Parser.checkTactic]
def elabCheckTactic : CommandElab := fun stx => do
let `(#check_tactic $t ~> $result by $tac) := stx | throwUnsupportedSyntax
@ -34,11 +26,10 @@ def elabCheckTactic : CommandElab := fun stx => do
runTermElabM $ fun _vars => do
let u ← Lean.Elab.Term.elabTerm t none
let type ← inferType u
let lvl ← mkFreshLevelMVar
let checkGoalType : Expr := mkApp2 (mkConst ``CheckGoalType [lvl]) type u
let checkGoalType ← mkCheckGoalType u type
let mvar ← mkFreshExprMVar (.some checkGoalType)
let (goals, _) ← Lean.Elab.runTactic mvar.mvarId! tac.raw
let expTerm ← Lean.Elab.Term.elabTerm result (.some type)
let (goals, _) ← Lean.Elab.runTactic mvar.mvarId! tac.raw
match goals with
| [] =>
throwErrorAt stx
@ -51,7 +42,6 @@ def elabCheckTactic : CommandElab := fun stx => do
| _ => do
throwErrorAt stx
m!"{tac} produced multiple goals, but is expected to reduce to {indentExpr expTerm}."
pure ()
@[builtin_command_elab Lean.Parser.checkTacticFailure]
def elabCheckTacticFailure : CommandElab := fun stx => do
@ -60,8 +50,7 @@ def elabCheckTacticFailure : CommandElab := fun stx => do
runTermElabM $ fun _vars => do
let val ← Lean.Elab.Term.elabTerm t none
let type ← inferType val
let lvl ← mkFreshLevelMVar
let checkGoalType : Expr := mkApp2 (mkConst ``CheckGoalType [lvl]) type val
let checkGoalType ← mkCheckGoalType val type
let mvar ← mkFreshExprMVar (.some checkGoalType)
let act := Lean.Elab.runTactic mvar.mvarId! tactic.raw
match ← try (Term.withoutErrToSorry (some <$> act)) catch _ => pure none with
@ -73,12 +62,12 @@ def elabCheckTacticFailure : CommandElab := fun stx => do
pure m!"{indentExpr val}"
let msg ←
match gls with
| [] => pure m!"{tactic} expected to fail on {val}, but closed goal."
| [] => pure m!"{tactic} expected to fail on {t}, but closed goal."
| [g] =>
pure <| m!"{tactic} expected to fail on {val}, but returned: {←ppGoal g}"
pure <| m!"{tactic} expected to fail on {t}, but returned: {←ppGoal g}"
| gls =>
let app m g := do pure <| m ++ (←ppGoal g)
let init := m!"{tactic} expected to fail on {val}, but returned goals:"
let init := m!"{tactic} expected to fail on {t}, but returned goals:"
gls.foldlM (init := init) app
throwErrorAt stx msg

View file

@ -47,3 +47,4 @@ import Lean.Meta.CoeAttr
import Lean.Meta.Iterator
import Lean.Meta.LazyDiscrTree
import Lean.Meta.LitValues
import Lean.Meta.CheckTactic

View file

@ -0,0 +1,24 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
prelude
import Lean.Meta.Basic
namespace Lean.Meta.CheckTactic
def mkCheckGoalType (val type : Expr) : MetaM Expr := do
let lvl ← mkFreshLevelMVar
pure <| mkApp2 (mkConst ``CheckGoalType [lvl]) type val
def matchCheckGoalType (stx : Syntax) (goalType : Expr) : MetaM (Expr × Expr × Level) := do
let u ← mkFreshLevelMVar
let type ← mkFreshExprMVar (some (.sort u))
let val ← mkFreshExprMVar (some type)
let extType := mkAppN (.const ``CheckGoalType [u]) #[type, val]
if !(← isDefEq goalType extType) then
throwErrorAt stx "Goal{indentExpr goalType}\nis expected to match {indentExpr extType}"
pure (val, type, u)
end Lean.Meta.CheckTactic

View file

@ -5,32 +5,4 @@
?a = ?a
with
Ordering.eq = Ordering.lt
[Meta.Tactic.simp.unify] @forall_exists_index:1000, failed to unify
∀ (h : ∃ x, ?p x), ?q h
with
False → False
[Meta.Tactic.simp.unify] @forall_eq:1000, failed to unify
∀ (a : ?α), a = ?a' → ?p a
with
False → False
[Meta.Tactic.simp.unify] @forall_eq':1000, failed to unify
∀ (a : ?α), ?a' = a → ?p a
with
False → False
[Meta.Tactic.simp.unify] @forall_eq_or_imp:1000, failed to unify
∀ (a : ?α), a = ?a' ?q a → ?p a
with
False → False
[Meta.Tactic.simp.unify] @forall_apply_eq_imp_iff:1000, failed to unify
∀ (b : ?β) (a : ?α), ?f a = b → ?p b
with
False → False
[Meta.Tactic.simp.unify] @forall_eq_apply_imp_iff:1000, failed to unify
∀ (b : ?β) (a : ?α), b = ?f a → ?p b
with
False → False
[Meta.Tactic.simp.unify] @forall_apply_eq_imp_iff₂:1000, failed to unify
∀ (b : ?β) (a : ?α), ?p a → ?f a = b → ?q b
with
False → False
[Meta.Tactic.simp.rewrite] @imp_self:1000, False → False ==> True
[Meta.Tactic.simp.rewrite] @imp_self:10000, False → False ==> True

389
tests/lean/bool_simp.lean Normal file
View file

@ -0,0 +1,389 @@
variable (p q : Prop)
variable (b c d : Bool)
variable (u v w : Prop) [Decidable u] [Decidable v] [Decidable w]
-- Specific regressions found when introducing Boolean normalization
#check_simp (b != !c) = false ~> b = !c
#check_simp ¬(u → v w) ~> u ∧ ¬v ∧ ¬w
#check_simp decide (u ∧ (v → False)) ~> decide u && !decide v
#check_simp decide (cond true b c = true) ~> b
#check_simp decide (ite u b c = true) ~> ite u b c
#check_simp true ≠ (b || c) ~> b = false ∧ c = false
#check_simp ¬((!b = false) ∧ (c = false)) ~> b = true → c = true
#check_simp (((!b) && c) ≠ false) ~> b = false ∧ c = true
#check_simp (cond b false c ≠ false) ~> b = false ∧ c
#check_simp (b && c) = false ~> b → c = false
#check_simp (b && c) ≠ false ~> b ∧ c
#check_simp decide (u → False) ~> !decide u
#check_simp decide (¬u) ~> !decide u
#check_simp (b = true) ≠ (c = false) ~> b = c
#check_simp (b != c) != (false != d) ~> b != (c != d)
#check_simp (b == false) ≠ (c != d) ~> b = (c != d)
#check_simp (b = true) ≠ (c = false) ~> b = c
#check_simp ¬b = !c ~> b = c
#check_simp (b == c) = false ~> ¬(b = c)
#check_simp (true ≠ if u then b else c) ~> (if u then b = false else c = false)
#check_simp (u ∧ v → False) ~> u → v → False
#check_simp (u = (v ≠ w)) ~> (u ↔ ¬(v ↔ w))
#check_simp ((b = false) = (c = false)) ~> (!b) = (!c)
#check_simp True ≠ (c = false) ~> c = true
#check_simp u ∧ u ∧ v ~> u ∧ v
#check_simp b || (b || c) ~> b || c
#check_simp ((b ≠ c) : Bool) ~> !(decide (b = c))
#check_simp ((u ≠ v) : Bool) ~> !((u : Bool) == (v : Bool))
#check_simp decide (u → False) ~> !(decide u)
#check_simp decide (¬u) ~> !u
-- Specific regressions done
-- Round trip isomorphisms
#check_simp (decide (b : Prop)) ~> b
#check_simp ((u : Bool) : Prop) ~> u
/- # not -/
variable [Decidable u]
-- Ground
#check_simp (¬True) ~> False
#check_simp (¬true) ~> False
#check_simp (!True) ~> false
#check_simp (!true) ~> false
#check_simp (¬False) ~> True
#check_simp (!False) ~> true
#check_simp (¬false) ~> True
#check_simp (!false) ~> true
/- # Coercions and not -/
#check_simp ¬p !~>
#check_simp !b !~>
#check_simp (¬u : Prop) !~>
#check_simp (¬u : Bool) ~> !u
#check_simp (!u : Prop) ~> ¬u
#check_simp (!u : Bool) !~>
#check_simp (¬b : Prop) ~> b = false
#check_simp (¬b : Bool) ~> !b
#check_simp (!b : Prop) ~> b = false
#check_simp (!b : Bool) !~>
#check_simp (¬¬u) ~> u
/- # and -/
-- Validate coercions
#check_simp (u ∧ v : Prop) !~>
#check_simp (u ∧ v : Bool) ~> u && v
#check_simp (u && v : Prop) ~> u ∧ v
#check_simp (u && v : Bool) !~>
#check_simp (b ∧ c : Prop) !~>
#check_simp (b ∧ c : Bool) ~> b && c
#check_simp (b && c : Prop) ~> b ∧ c
#check_simp (b && c : Bool) !~>
-- Partial evaluation
#check_simp (True ∧ v : Prop) ~> v
#check_simp (True ∧ v : Bool) ~> (v : Bool)
#check_simp (True && v : Prop) ~> v
#check_simp (True && v : Bool) ~> (v : Bool)
#check_simp (true ∧ c : Prop) ~> (c : Prop)
#check_simp (true ∧ c : Bool) ~> c
#check_simp (true && c : Prop) ~> (c : Prop)
#check_simp (true && c : Bool) ~> c
#check_simp (u ∧ True : Prop) ~> u
#check_simp (u ∧ True : Bool) ~> (u : Bool)
#check_simp (u && True : Prop) ~> u
#check_simp (u && True : Bool) ~> (u : Bool)
#check_simp (b ∧ true : Prop) ~> (b : Prop)
#check_simp (b ∧ true : Bool) ~> b
#check_simp (b && true : Prop) ~> (b : Prop)
#check_simp (b && true : Bool) ~> b
#check_simp (False ∧ v : Prop) ~> False
#check_simp (False ∧ v : Bool) ~> false
#check_simp (False && v : Prop) ~> False
#check_simp (False && v : Bool) ~> false
#check_simp (false ∧ c : Prop) ~> False
#check_simp (false ∧ c : Bool) ~> false
#check_simp (false && c : Prop) ~> False
#check_simp (false && c : Bool) ~> false
#check_simp (u ∧ False : Prop) ~> False
#check_simp (u ∧ False : Bool) ~> false
#check_simp (u && False : Prop) ~> False
#check_simp (u && False : Bool) ~> false
#check_simp (b ∧ false : Prop) ~> False
#check_simp (b ∧ false : Bool) ~> false
#check_simp (b && false : Prop) ~> False
#check_simp (b && false : Bool) ~> false
-- Idempotence
#check_simp (u ∧ u) ~> u
#check_simp (u && u) ~> (u : Bool)
#check_simp (b ∧ b) ~> (b : Prop)
#check_simp (b && b) ~> b
-- Cancelation
#check_simp (u ∧ ¬u) ~> False
#check_simp (¬u ∧ u) ~> False
#check_simp (b && ¬b) ~> false
#check_simp (¬b && b) ~> false
-- Check we swap operators, but do apply deMorgan etc
#check_simp ¬(u ∧ v) ~> u → ¬v
#check_simp decide (¬(u ∧ v)) ~> !u || !v
#check_simp !(u ∧ v) ~> !u || !v
#check_simp ¬(b ∧ c) ~> b → c = false
#check_simp !(b ∧ c) ~> !b || !c
#check_simp ¬(u && v) ~> u → ¬ v
#check_simp ¬(b && c) ~> b = true → c = false
#check_simp !(u && v) ~> !u || !v
#check_simp !(b && c) ~> !b || !c
#check_simp ¬u ∧ ¬v !~>
#check_simp ¬b ∧ ¬c ~> ((b = false) ∧ (c = false))
#check_simp ¬u && ¬v ~> (!u && !v)
#check_simp ¬b && ¬c ~> (!b && !c)
-- Some ternary test cases
#check_simp (u ∧ (v ∧ w) : Prop) !~>
#check_simp (u ∧ (v ∧ w) : Bool) ~> (u && (v && w))
#check_simp ((u ∧ v) ∧ w : Prop) !~>
#check_simp ((u ∧ v) ∧ w : Bool) ~> ((u && v) && w)
#check_simp (b && (c && d) : Prop) ~> (b ∧ c ∧ d)
#check_simp (b && (c && d) : Bool) !~>
#check_simp ((b && c) && d : Prop) ~> ((b ∧ c) ∧ d)
#check_simp ((b && c) && d : Bool) !~>
/- # or -/
-- Validate coercions
#check_simp p q !~>
#check_simp q p !~>
#check_simp (u v : Prop) !~>
#check_simp (u v : Bool) ~> u || v
#check_simp (u || v : Prop) ~> u v
#check_simp (u || v : Bool) !~>
#check_simp (b c : Prop) !~>
#check_simp (b c : Bool) ~> b || c
#check_simp (b || c : Prop) ~> b c
#check_simp (b || c : Bool) !~>
-- Partial evaluation
#check_simp (True v : Prop) ~> True
#check_simp (True v : Bool) ~> true
#check_simp (True || v : Prop) ~> True
#check_simp (True || v : Bool) ~> true
#check_simp (true c : Prop) ~> True
#check_simp (true c : Bool) ~> true
#check_simp (true || c : Prop) ~> True
#check_simp (true || c : Bool) ~> true
#check_simp (u True : Prop) ~> True
#check_simp (u True : Bool) ~> true
#check_simp (u || True : Prop) ~> True
#check_simp (u || True : Bool) ~> true
#check_simp (b true : Prop) ~> True
#check_simp (b true : Bool) ~> true
#check_simp (b || true : Prop) ~> True
#check_simp (b || true : Bool) ~> true
#check_simp (False v : Prop) ~> v
#check_simp (False v : Bool) ~> (v : Bool)
#check_simp (False || v : Prop) ~> v
#check_simp (False || v : Bool) ~> (v : Bool)
#check_simp (false c : Prop) ~> (c : Prop)
#check_simp (false c : Bool) ~> c
#check_simp (false || c : Prop) ~> (c : Prop)
#check_simp (false || c : Bool) ~> c
#check_simp (u False : Prop) ~> u
#check_simp (u False : Bool) ~> (u : Bool)
#check_simp (u || False : Prop) ~> u
#check_simp (u || False : Bool) ~> (u : Bool)
#check_simp (b false : Prop) ~> (b : Prop)
#check_simp (b false : Bool) ~> b
#check_simp (b || false : Prop) ~> (b : Prop)
#check_simp (b || false : Bool) ~> b
-- Idempotence
#check_simp (u u) ~> u
#check_simp (u || u) ~> (u : Bool)
#check_simp (b b) ~> (b : Prop)
#check_simp (b || b) ~> b
-- Complement
-- Note. We may want to revisit this.
-- Decidable excluded middle currently does not simplify.
#check_simp ( u ¬u) !~>
#check_simp (¬u u) !~>
#check_simp ( b || ¬b) ~> true
#check_simp (¬b || b) ~> true
-- Check we swap operators, but do apply deMorgan etc
#check_simp ¬(u v) ~> ¬u ∧ ¬v
#check_simp !(u v) ~> !u && !v
#check_simp ¬(b c) ~> b = false ∧ c =false
#check_simp !(b c) ~> !b && !c
#check_simp ¬(u || v) ~> ¬u ∧ ¬v
#check_simp ¬(b || c) ~> b = false ∧ c = false
#check_simp !(u || v) ~> !u && !v
#check_simp !(b || c) ~> !b && !c
#check_simp ¬u ¬v !~>
#check_simp (¬b) (¬c) ~> b = false c = false
#check_simp ¬u || ¬v ~> (!u || !v)
#check_simp ¬b || ¬c ~> (!b || !c)
-- Some ternary test cases
#check_simp (u (v w) : Prop) !~>
#check_simp (u (v w) : Bool) ~> (u || (v || w))
#check_simp ((u v) w : Prop) !~>
#check_simp ((u v) w : Bool) ~> ((u || v) || w)
#check_simp (b || (c || d) : Prop) ~> (b c d)
#check_simp (b || (c || d) : Bool) !~>
#check_simp ((b || c) || d : Prop) ~> ((b c) d)
#check_simp ((b || c) || d : Bool) !~>
/- # and/or -/
-- We don't currently do automatic simplification across and/or/not
-- This tests for non-unexpected reductions.
#check_simp p ∧ (p q) !~>
#check_simp (p q) ∧ p !~>
#check_simp u ∧ (v w) !~>
#check_simp u (v ∧ w) !~>
#check_simp (v w) ∧ u !~>
#check_simp (v ∧ w) u !~>
#check_simp b && (c || d) !~>
#check_simp b || (c && d) !~>
#check_simp (c || d) && b !~>
#check_simp (c && d) || b !~>
/- # implication -/
#check_simp (b → c) !~>
#check_simp (u → v) !~>
#check_simp p → q !~>
#check_simp decide (u → ¬v) ~> !u || !v
/- # iff -/
#check_simp (u = v : Prop) ~> u ↔ v
#check_simp (u = v : Bool) ~> u == v
#check_simp (u ↔ v : Prop) !~>
#check_simp (u ↔ v : Bool) ~> u == v
#check_simp (u == v : Prop) ~> u ↔ v
#check_simp (u == v : Bool) !~>
#check_simp (b = c : Prop) !~>
#check_simp (b = c : Bool) !~>
#check_simp (b ↔ c : Prop) ~> b = c
#check_simp (b ↔ c : Bool) ~> decide (b = c)
#check_simp (b == c : Prop) ~> b = c
#check_simp (b == c : Bool) !~>
-- Partial evaluation
#check_simp (True = v : Prop) ~> v
#check_simp (True = v : Bool) ~> (v : Bool)
#check_simp (True ↔ v : Prop) ~> v
#check_simp (True ↔ v : Bool) ~> (v : Bool)
#check_simp (True == v : Prop) ~> v
#check_simp (True == v : Bool) ~> (v : Bool)
#check_simp (true = c : Prop) ~> c = true
#check_simp (true = c : Bool) ~> c
#check_simp (true ↔ c : Prop) ~> c = true
#check_simp (true ↔ c : Bool) ~> c
#check_simp (true == c : Prop) ~> (c : Prop)
#check_simp (true == c : Bool) ~> c
#check_simp (v = True : Prop) ~> v
#check_simp (v = True : Bool) ~> (v : Bool)
#check_simp (v ↔ True : Prop) ~> v
#check_simp (v ↔ True : Bool) ~> (v : Bool)
#check_simp (v == True : Prop) ~> v
#check_simp (v == True : Bool) ~> (v : Bool)
#check_simp (c = true : Prop) !~>
#check_simp (c = true : Bool) ~> c
#check_simp (c ↔ true : Prop) ~> c = true
#check_simp (c ↔ true : Bool) ~> c
#check_simp (c == true : Prop) ~> c = true
#check_simp (c == true : Bool) ~> c
#check_simp (True = v : Prop) ~> v
#check_simp (True = v : Bool) ~> (v : Bool)
#check_simp (True ↔ v : Prop) ~> v
#check_simp (True ↔ v : Bool) ~> (v : Bool)
#check_simp (True == v : Prop) ~> v
#check_simp (True == v : Bool) ~> (v : Bool)
#check_simp (true = c : Prop) ~> c = true
#check_simp (true = c : Bool) ~> c
#check_simp (true ↔ c : Prop) ~> c = true
#check_simp (true ↔ c : Bool) ~> c
#check_simp (true == c : Prop) ~> (c : Prop)
#check_simp (true == c : Bool) ~> c
#check_simp (v = False : Prop) ~> ¬v
#check_simp (v = False : Bool) ~> !v
#check_simp (v ↔ False : Prop) ~> ¬v
#check_simp (v ↔ False : Bool) ~> !v
#check_simp (v == False : Prop) ~> ¬v
#check_simp (v == False : Bool) ~> !v
#check_simp (c = false : Prop) !~>
#check_simp (c = false : Bool) ~> !c
#check_simp (c ↔ false : Prop) ~> c = false
#check_simp (c ↔ false : Bool) ~> !c
#check_simp (c == false : Prop) ~> c = false
#check_simp (c == false : Bool) ~> !c
#check_simp (False = v : Prop) ~> ¬v
#check_simp (False = v : Bool) ~> !v
#check_simp (False ↔ v : Prop) ~> ¬v
#check_simp (False ↔ v : Bool) ~> !v
#check_simp (False == v : Prop) ~> ¬v
#check_simp (False == v : Bool) ~> !v
#check_simp (false = c : Prop) ~> c = false
#check_simp (false = c : Bool) ~> !c
#check_simp (false ↔ c : Prop) ~> c = false
#check_simp (false ↔ c : Bool) ~> !c
#check_simp (false == c : Prop) ~> c = false
#check_simp (false == c : Bool) ~> !c
-- Ternary (expand these)
#check_simp (u == (v = w)) ~> u == (v == w)
#check_simp (u == (v == w)) !~>
/- # bne -/
#check_simp p ≠ q ~> ¬(p ↔ q)
#check_simp (b != c : Bool) !~>
#check_simp ¬(p = q) ~> ¬(p ↔ q)
#check_simp b ≠ c ~> b ≠ c
#check_simp ¬(b = c) !~>
#check_simp ¬(b ↔ c) ~> ¬(b = c)
#check_simp (b != c : Prop) ~> b ≠ c
#check_simp u ≠ v ~> ¬(u ↔ v)
#check_simp ¬(u = v) ~> ¬(u ↔ v)
#check_simp ¬(u ↔ v) !~>
#check_simp ((u:Bool) != v : Bool) !~>
#check_simp ((u:Bool) != v : Prop) ~> ¬(u ↔ v)
/- # equality and and/or interactions -/
#check_simp (u == (v w)) ~> u == (v || w)
#check_simp (u == (v || w)) !~>
#check_simp ((u ∧ v) == w) ~> (u && v) == w
/- # ite/cond -/
#check_simp if b then c else d !~>
#check_simp if b then p else q !~>
#check_simp if u then p else q !~>
#check_simp if u then b else c !~>
#check_simp if u then u else q ~> ¬u → q
#check_simp if u then q else u ~> u ∧ q
#check_simp if u then q else q ~> q
#check_simp cond b c d !~>

View file

File diff suppressed because it is too large Load diff