mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-31 17:24:08 +00:00
Compare commits
39 Commits
array_any2
...
Rat_bug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da40cc194f | ||
|
|
cfc20cbfad | ||
|
|
3eb07cac44 | ||
|
|
58034bf237 | ||
|
|
7ba7ea4e16 | ||
|
|
4877e84031 | ||
|
|
9c47f395c8 | ||
|
|
3f98b4835c | ||
|
|
a86145b6bb | ||
|
|
c4d3a74f32 | ||
|
|
c74865fbe2 | ||
|
|
93a908469c | ||
|
|
903fe29863 | ||
|
|
84da113355 | ||
|
|
75df4c0b52 | ||
|
|
ad5a746cdd | ||
|
|
2bd3ce5463 | ||
|
|
2b752ec245 | ||
|
|
909ee719aa | ||
|
|
7dd5e957da | ||
|
|
d67e0eea47 | ||
|
|
10bfeba2d9 | ||
|
|
4285f8ba05 | ||
|
|
d8be3ef7a8 | ||
|
|
c924768879 | ||
|
|
c1e76e8976 | ||
|
|
60a9f8e492 | ||
|
|
604133d189 | ||
|
|
d3781bb787 | ||
|
|
87e8da5230 | ||
|
|
727c696d9f | ||
|
|
cf2b7f4c1b | ||
|
|
cd4383b6f3 | ||
|
|
0d9859370a | ||
|
|
c292ae2e0e | ||
|
|
3113847806 | ||
|
|
d275455674 | ||
|
|
a4d10742d3 | ||
|
|
777fba495a |
2
.github/workflows/pr-release.yml
vendored
2
.github/workflows/pr-release.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v8 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
|
||||
@@ -65,20 +65,21 @@ def format_markdown_description(pr_number, description):
|
||||
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
|
||||
return f"{link} {description}"
|
||||
|
||||
def commit_types():
|
||||
# see doc/dev/commit_convention.md
|
||||
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
|
||||
|
||||
def count_commit_types(commits):
|
||||
counts = {
|
||||
'total': len(commits),
|
||||
'feat': 0,
|
||||
'fix': 0,
|
||||
'refactor': 0,
|
||||
'doc': 0,
|
||||
'chore': 0
|
||||
}
|
||||
for commit_type in commit_types():
|
||||
counts[commit_type] = 0
|
||||
|
||||
for _, first_line, _ in commits:
|
||||
for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
|
||||
if first_line.startswith(commit_type):
|
||||
counts[commit_type.rstrip(':')] += 1
|
||||
for commit_type in commit_types():
|
||||
if first_line.startswith(f'{commit_type}:'):
|
||||
counts[commit_type] += 1
|
||||
break
|
||||
|
||||
return counts
|
||||
@@ -158,8 +159,9 @@ def main():
|
||||
counts = count_commit_types(commits)
|
||||
print(f"For this release, {counts['total']} changes landed. "
|
||||
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements "
|
||||
f"and {counts['chore']} chores.\n")
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
|
||||
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
|
||||
f"and {counts['style'] + counts['chore']} other changes.\n")
|
||||
|
||||
section_order = sort_sections_order()
|
||||
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
|
||||
|
||||
@@ -555,6 +555,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Array.map_push]
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Array { x // p x }} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem size_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.unattach.size = xs.size := by
|
||||
unfold unattach
|
||||
@@ -676,6 +680,20 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [List.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.all f 0 stop = xs.unattach.all g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.any f 0 stop = xs.unattach.any g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {xs : Array { x // p x }}
|
||||
|
||||
@@ -144,6 +144,8 @@ end List
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem size_eq_length_toList (xs : Array α) : xs.size = xs.toList.length := rfl
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
|
||||
|
||||
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
|
||||
|
||||
@@ -23,6 +23,18 @@ section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem _root_.List.countP_toArray (l : List α) : countP p l.toArray = l.countP p := by
|
||||
simp [countP]
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons hd tl ih =>
|
||||
simp only [List.foldr_cons, ih, List.countP_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem countP_toList (xs : Array α) : xs.toList.countP p = countP p xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos (xs) (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
|
||||
@@ -150,6 +162,13 @@ section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem _root_.List.count_toArray (l : List α) (a : α) : count a l.toArray = l.count a := by
|
||||
simp [count, List.count_eq_countP]
|
||||
|
||||
@[simp] theorem count_toList (xs : Array α) (a : α) : xs.toList.count a = xs.count a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
|
||||
|
||||
theorem count_push (a b : α) (xs : Array α) :
|
||||
|
||||
@@ -282,6 +282,10 @@ end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
|
||||
simp [eraseIdxIfInBounds, h]
|
||||
|
||||
theorem eraseIdx_eq_take_drop_succ (xs : Array α) (i : Nat) (h) : xs.eraseIdx i = xs.take i ++ xs.drop (i + 1) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.size_toArray] at h
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
|
||||
@@ -23,6 +23,9 @@ open Nat
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
@[simp] theorem mapM_id {xs : Array α} {f : α → Id β} : xs.mapM f = xs.map f := by
|
||||
induction xs; simp_all
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {xs ys : Array α} :
|
||||
(xs ++ ys).mapM f = (return (← xs.mapM f) ++ (← ys.mapM f)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
|
||||
@@ -16,6 +16,25 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem ofFn_zero (f : Fin 0 → α) : ofFn f = #[] := rfl
|
||||
|
||||
theorem ofFn_succ (f : Fin (n+1) → α) :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split <;> rename_i h₃
|
||||
· rfl
|
||||
· congr
|
||||
simp at h₁ h₂
|
||||
omega
|
||||
|
||||
@[simp] theorem _rooy_.List.toArray_ofFn (f : Fin n → α) : (List.ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem toList_ofFn (f : Fin n → α) : (Array.ofFn f).toList = List.ofFn f := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
|
||||
rw [← Array.toList_inj]
|
||||
|
||||
@@ -13,6 +13,7 @@ import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Nat.Mod
|
||||
import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.Pow
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -569,6 +570,11 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||||
have p : 0 ≤ i % (2^n : Nat) := by omega
|
||||
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
|
||||
|
||||
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
|
||||
(h : -2 ^ (w - 1) ≤ n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
|
||||
have hw : w = (w - 1) + 1 := by omega
|
||||
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
|
||||
|
||||
@[simp] theorem ofInt_natCast (w n : Nat) :
|
||||
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
|
||||
|
||||
@@ -2693,6 +2699,9 @@ theorem toInt_neg {x : BitVec w} :
|
||||
rw [← BitVec.zero_sub, toInt_sub]
|
||||
simp [BitVec.toInt_ofNat]
|
||||
|
||||
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
|
||||
eq_of_toInt_eq (by simp [toInt_neg])
|
||||
|
||||
@[simp] theorem toFin_neg (x : BitVec n) :
|
||||
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
|
||||
rfl
|
||||
@@ -4109,9 +4118,7 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
|
||||
|
||||
theorem msb_eq_toInt {x : BitVec w}:
|
||||
x.msb = decide (x.toInt < 0) := by
|
||||
by_cases h : x.msb <;>
|
||||
· simp [h, toInt_eq_msb_cond]
|
||||
omega
|
||||
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
|
||||
|
||||
theorem msb_eq_toNat {x : BitVec w}:
|
||||
x.msb = decide (x.toNat ≥ 2 ^ (w - 1)) := by
|
||||
|
||||
@@ -45,6 +45,7 @@ theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
|
||||
theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩ :=
|
||||
⟨fun h i hi => h ⟨i, hi⟩, fun h ⟨i, hi⟩ => h i hi⟩
|
||||
|
||||
/-- Restatement of `Fin.mk.injEq` as an `iff`. -/
|
||||
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := Fin.ext_iff
|
||||
|
||||
@@ -55,6 +56,14 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@[simp] theorem mk_eq_zero {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
(⟨a, ha⟩ : Fin n) = 0 ↔ a = 0 :=
|
||||
mk.inj_iff
|
||||
|
||||
@[simp] theorem zero_eq_mk {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
0 = (⟨a, ha⟩ : Fin n) ↔ a = 0 := by
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
|
||||
(Fin.ofNat' n a).val = a % n := rfl
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@ open Nat
|
||||
This file defines the `Int` type as well as
|
||||
|
||||
* coercions, conversions, and compatibility with numeric literals,
|
||||
* basic arithmetic operations add/sub/mul/div/mod/pow,
|
||||
* basic arithmetic operations add/sub/mul/pow,
|
||||
* a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
|
||||
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
|
||||
* decidability of equality, relations and `NonNeg`.
|
||||
|
||||
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
|
||||
-/
|
||||
|
||||
/--
|
||||
|
||||
@@ -227,33 +227,4 @@ theorem cooper_resolution_dvd_right
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_le_of_neg_le lower
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_mul _ _ ▸ dvd
|
||||
|
||||
/--
|
||||
Left Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_left
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < a ∧ b * k + b * p ≤ a * q ∧ a ∣ k + p) := by
|
||||
have h := cooper_resolution_dvd_left
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt a_pos), Int.one_dvd, and_true,
|
||||
and_self] at h
|
||||
exact h
|
||||
|
||||
/--
|
||||
Right Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_right
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < b ∧ a * k + b * p ≤ a * q ∧ b ∣ k - q) := by
|
||||
have h := cooper_resolution_dvd_right
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
have : ∀ k : Int, (b ∣ -k + q) ↔ (b ∣ k - q) := by
|
||||
intro k
|
||||
rw [← Int.dvd_neg, Int.neg_add, Int.neg_neg, Int.sub_eq_add_neg]
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.one_dvd, and_true,
|
||||
and_self, ← Int.neg_eq_neg_one_mul, this] at h
|
||||
exact h
|
||||
end Int
|
||||
|
||||
@@ -21,25 +21,25 @@ and satisfy `x / 0 = 0` and `x % 0 = x`.
|
||||
In early versions of Lean, the typeclasses provided by `/` and `%`
|
||||
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
|
||||
|
||||
However we decided it was better to use `ediv` and `emod`,
|
||||
However we decided it was better to use `ediv` and `emod` for the default typeclass instances,
|
||||
as they are consistent with the conventions used in SMTLib, and Mathlib,
|
||||
and often mathematical reasoning is easier with these conventions.
|
||||
|
||||
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
|
||||
|
||||
In September 2024, we decided to do this rename (with deprecations in place),
|
||||
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
|
||||
ever need to use these functions and their associated lemmas.
|
||||
|
||||
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
|
||||
In December 2024, we removed `div` and `mod`, but have not yet renamed `ediv` and `emod`.
|
||||
-/
|
||||
|
||||
/-! ### E-rounding division
|
||||
This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
|
||||
This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of `Int.div` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
|
||||
Integer division. This version of integer division uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
This is the function powering the `/` notation on integers.
|
||||
@@ -71,7 +71,7 @@ def ediv : (@& Int) → (@& Int) → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the E-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
@@ -229,7 +229,7 @@ def fdiv : Int → Int → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ m / succ n)
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the F-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
|
||||
@@ -268,11 +268,14 @@ Balanced mod (and balanced div) are a division and modulus pair such
|
||||
that `b * (Int.bdiv a b) + Int.bmod a b = a` and
|
||||
`-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
|
||||
|
||||
This is used in Omega as well as signed bitvectors.
|
||||
Note that unlike `emod`, `fmod`, and `tmod`,
|
||||
`bmod` takes a natural number as the second argument, rather than an integer.
|
||||
|
||||
This function is used in `omega` as well as signed bitvectors.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced modulus. This version of Integer modulus uses the
|
||||
Balanced modulus. This version of integer modulus uses the
|
||||
balanced rounding convention, which guarantees that
|
||||
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
|
||||
@@ -18,7 +18,7 @@ open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
|
||||
-- /-! ### dvd -/
|
||||
/-! ### dvd -/
|
||||
|
||||
protected theorem dvd_def (a b : Int) : (a ∣ b) = Exists (fun c => b = a * c) := rfl
|
||||
|
||||
@@ -67,7 +67,7 @@ protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natAbs := by
|
||||
rw [← natAbs_dvd_natAbs, natAbs_ofNat]
|
||||
|
||||
/-! ### *div zero -/
|
||||
/-! ### ediv zero -/
|
||||
|
||||
@[simp] theorem zero_ediv : ∀ b : Int, 0 / b = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
@@ -77,7 +77,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
/-! ### mod zero -/
|
||||
/-! ### emod zero -/
|
||||
|
||||
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl
|
||||
|
||||
@@ -89,7 +89,6 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
@@ -106,12 +105,17 @@ where
|
||||
← Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
|
||||
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
|
||||
|
||||
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
|
||||
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
|
||||
rw [Int.mul_comm]; exact emod_add_ediv ..
|
||||
|
||||
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
|
||||
rw [Int.add_comm]; exact emod_add_ediv ..
|
||||
|
||||
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
|
||||
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
rw [Int.mul_comm]; exact ediv_add_emod ..
|
||||
|
||||
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
|
||||
rw [← Int.add_sub_cancel (a % b), emod_add_ediv]
|
||||
|
||||
@@ -170,7 +174,7 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c +
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
| Int.ofNat (b+1), _ =>
|
||||
@@ -178,6 +182,9 @@ theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0
|
||||
norm_cast
|
||||
simp
|
||||
|
||||
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
|
||||
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
theorem emod_nonneg : ∀ (a : Int) {b : Int}, b ≠ 0 → 0 ≤ a % b
|
||||
|
||||
@@ -94,6 +94,14 @@ theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
|
||||
protected theorem mul_dvd_mul_iff_left {a b c : Int} (h : a ≠ 0) : (a * b) ∣ (a * c) ↔ b ∣ c :=
|
||||
⟨by rintro ⟨d, h'⟩; exact ⟨d, by rw [Int.mul_assoc] at h'; exact (mul_eq_mul_left_iff h).mp h'⟩,
|
||||
by rintro ⟨d, rfl⟩; exact ⟨d, by simp [Int.mul_assoc]⟩⟩
|
||||
|
||||
protected theorem mul_dvd_mul_iff_right {a b c : Int} (h : a ≠ 0) : (b * a) ∣ (c * a) ↔ b ∣ c := by
|
||||
rw [Int.mul_comm b a, Int.mul_comm c a]
|
||||
exact Int.mul_dvd_mul_iff_left h
|
||||
|
||||
/-! ### *div zero -/
|
||||
|
||||
@[simp] protected theorem zero_tdiv : ∀ b : Int, tdiv 0 b = 0
|
||||
@@ -234,6 +242,13 @@ theorem tdiv_eq_fdiv {a b : Int} :
|
||||
rw [fdiv_eq_tdiv]
|
||||
omega
|
||||
|
||||
|
||||
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.tdiv b = a / b := by
|
||||
simp [tdiv_eq_ediv, h]
|
||||
|
||||
theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.fdiv b = a / b := by
|
||||
simp [fdiv_eq_ediv, h]
|
||||
|
||||
/-! ### mod zero -/
|
||||
|
||||
@[simp] theorem zero_tmod (b : Int) : tmod 0 b = 0 := by cases b <;> simp [tmod]
|
||||
@@ -251,9 +266,6 @@ theorem tdiv_eq_fdiv {a b : Int} :
|
||||
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
rw [Int.mul_comm]; exact ediv_add_emod ..
|
||||
|
||||
theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
|
||||
| ofNat m, -[n+1] => by
|
||||
@@ -274,9 +286,11 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
|
||||
rw [Int.add_comm]; apply tmod_add_tdiv ..
|
||||
|
||||
/-- Variant of `tmod_add_tdiv` with the multiplication written the other way around. -/
|
||||
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
|
||||
rw [Int.mul_comm]; apply tmod_add_tdiv
|
||||
|
||||
/-- Variant of `tdiv_add_tmod` with the multiplication written the other way around. -/
|
||||
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
|
||||
rw [Int.mul_comm]; apply tdiv_add_tmod
|
||||
|
||||
@@ -300,9 +314,17 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
|
||||
show -(↑(succ m % succ n) : Int) + -↑(succ n * (succ m / succ n)) = -↑(succ m)
|
||||
rw [← Int.neg_add]; exact congrArg (-ofNat ·) <| Nat.mod_add_div ..
|
||||
|
||||
/-- Variant of `fmod_add_fdiv` with the multiplication written the other way around. -/
|
||||
theorem fmod_add_fdiv' (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
|
||||
rw [Int.mul_comm]; exact fmod_add_fdiv ..
|
||||
|
||||
theorem fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
|
||||
rw [Int.add_comm]; exact fmod_add_fdiv ..
|
||||
|
||||
/-- Variant of `fdiv_add_fmod` with the multiplication written the other way around. -/
|
||||
theorem fdiv_add_fmod' (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
|
||||
rw [Int.mul_comm]; exact fdiv_add_fmod ..
|
||||
|
||||
theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
|
||||
rw [← Int.add_sub_cancel (a.fmod b), fmod_add_fdiv]
|
||||
|
||||
@@ -396,6 +418,11 @@ theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
|
||||
rw [Int.div_def, ediv]
|
||||
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
|
||||
|
||||
theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b := by
|
||||
rw [Int.div_def]
|
||||
match a, b, ha, hb with
|
||||
| .negSucc a, .negSucc b, _, _ => apply ofNat_succ_pos
|
||||
|
||||
theorem ediv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a / b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. ▸ Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
@@ -446,6 +473,10 @@ protected theorem ediv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a / b = c :=
|
||||
Int.ediv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a * b = c) : a = c / b :=
|
||||
(Int.ediv_eq_of_eq_mul_left H1 H2.symm).symm
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
theorem mod_def' (m n : Int) : m % n = emod m n := rfl
|
||||
@@ -715,16 +746,100 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
|
||||
|
||||
/-! ### tdiv -/
|
||||
|
||||
@[simp] protected theorem tdiv_one : ∀ a : Int, a.tdiv 1 = a
|
||||
| (n:Nat) => congrArg ofNat (Nat.div_one _)
|
||||
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_neg : ∀ a b : Int, a.tdiv (-b) = -(a.tdiv b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
|
||||
/-!
|
||||
We don't give `tdiv` versions of
|
||||
* `add_mul_ediv_right : c ≠ 0 → (a + b * c) / c = a / c + b`
|
||||
* `add_mul_ediv_left : b ≠ 0 → (a + b * c) / b = a / b + c`
|
||||
* `add_ediv_of_dvd_right : c ∣ b → (a + b) / c = a / c + b / c`
|
||||
* `add_ediv_of_dvd_left : c ∣ a → (a + b) / c = a / c + b / c`
|
||||
because they all involve awkward off-by-one corrections.
|
||||
-/
|
||||
|
||||
@[simp] theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).tdiv b = a := by
|
||||
rw [tdiv_eq_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ H]
|
||||
|
||||
@[simp] theorem mul_tdiv_cancel_left (b : Int) (H : a ≠ 0) : (a * b).tdiv a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_tdiv_cancel _ H
|
||||
|
||||
-- There's no good analogues of `ediv_nonneg_iff_of_pos`, `ediv_neg'`, or `negSucc_ediv`
|
||||
-- for `tdiv`.
|
||||
|
||||
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.tdiv b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0) : 0 ≤ a.tdiv b := by
|
||||
rw [tdiv_eq_ediv]
|
||||
split <;> rename_i h
|
||||
· simpa using ediv_nonneg_of_nonpos_of_nonpos Ha Hb
|
||||
· simp at h
|
||||
by_cases h' : b = 0
|
||||
· subst h'
|
||||
simp
|
||||
· replace h' : b < 0 := by omega
|
||||
rw [sign_eq_neg_one_of_neg h']
|
||||
have : 0 < a / b := by
|
||||
by_cases h'' : a = 0
|
||||
· subst h''
|
||||
simp at h
|
||||
· replace h'' : a < 0 := by omega
|
||||
exact ediv_pos_of_neg_of_neg h'' h'
|
||||
omega
|
||||
|
||||
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.tdiv b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. ▸ Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.tdiv b = 0 :=
|
||||
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
|
||||
|
||||
@[simp] theorem mul_tdiv_mul_of_pos {a : Int}
|
||||
(b c : Int) (H : 0 < a) : (a * b).tdiv (a * c) = b.tdiv c := by
|
||||
rw [tdiv_eq_ediv, mul_ediv_mul_of_pos _ _ H, tdiv_eq_ediv]
|
||||
simp only [sign_mul]
|
||||
by_cases h : 0 ≤ b
|
||||
· rw [if_pos, if_pos (.inl h)]
|
||||
left
|
||||
exact Int.mul_nonneg (Int.le_of_lt H) h
|
||||
· have H' : a ≠ 0 := by omega
|
||||
simp only [Int.mul_dvd_mul_iff_left H']
|
||||
by_cases h' : c ∣ b
|
||||
· simp [h']
|
||||
· rw [if_neg, if_neg]
|
||||
· simp [sign_eq_one_of_pos H]
|
||||
· simp [h']; omega
|
||||
· simp_all only [Int.not_le, ne_eq, or_false]
|
||||
exact Int.mul_neg_of_pos_of_neg H h
|
||||
|
||||
@[simp] theorem mul_tdiv_mul_of_pos_left
|
||||
(a : Int) {b : Int} (c : Int) (H : 0 < b) : (a * b).tdiv (c * b) = a.tdiv c := by
|
||||
rw [Int.mul_comm, Int.mul_comm c, mul_tdiv_mul_of_pos _ _ H]
|
||||
|
||||
@[simp] protected theorem tdiv_one : ∀ a : Int, a.tdiv 1 = a
|
||||
| (n:Nat) => congrArg ofNat (Nat.div_one _)
|
||||
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.tdiv a :=
|
||||
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.tdiv b = c :=
|
||||
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a * b = c) : a = c.tdiv b :=
|
||||
(Int.tdiv_eq_of_eq_mul_left H1 H2.symm).symm
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem neg_tdiv : ∀ a b : Int, (-a).tdiv b = -(a.tdiv b)
|
||||
| 0, n => by simp [Int.neg_zero]
|
||||
@@ -734,33 +849,6 @@ unseal Nat.div in
|
||||
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
|
||||
|
||||
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.tdiv b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.tdiv b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. ▸ Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.tdiv b = 0 :=
|
||||
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).tdiv b = a :=
|
||||
have : ∀ {a b : Nat}, (b : Int) ≠ 0 → (tdiv (a * b) b : Int) = a := fun H => by
|
||||
rw [← ofNat_mul, ← ofNat_tdiv,
|
||||
Nat.mul_div_cancel _ <| Nat.pos_of_ne_zero <| Int.ofNat_ne_zero.1 H]
|
||||
match a, b, a.eq_nat_or_neg, b.eq_nat_or_neg with
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inl rfl⟩ => this H
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.mul_neg, Int.neg_tdiv, Int.tdiv_neg, Int.neg_neg,
|
||||
this (Int.neg_ne_zero.1 H)]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inl rfl⟩ => by rw [Int.neg_mul, Int.neg_tdiv, this H]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.neg_mul_neg, Int.tdiv_neg, this (Int.neg_ne_zero.1 H)]
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel_left (b : Int) (H : a ≠ 0) : (a * b).tdiv a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_tdiv_cancel _ H
|
||||
|
||||
@[simp] protected theorem tdiv_self {a : Int} (H : a ≠ 0) : a.tdiv a = 1 := by
|
||||
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
@@ -796,14 +884,7 @@ theorem tdiv_dvd_tdiv : ∀ {a b c : Int}, a ∣ b → b ∣ c → b.tdiv a ∣
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inl rfl⟩ => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.tdiv a :=
|
||||
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
|
||||
/-! ### (t-)mod -/
|
||||
/-! ### tmod -/
|
||||
|
||||
theorem ofNat_tmod (m n : Nat) : (↑(m % n) : Int) = tmod m n := rfl
|
||||
|
||||
@@ -878,9 +959,6 @@ protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.tdiv b = c) : a = c * b := by
|
||||
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.tdiv b = c :=
|
||||
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d ∣ n) (H : n.tdiv d = 0) : n = 0 := by
|
||||
rw [← Int.mul_tdiv_cancel' h, H, Int.mul_zero]
|
||||
@@ -968,19 +1046,6 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
|
||||
@[simp] theorem fmod_self {a : Int} : a.fmod a = 0 := by
|
||||
have := mul_fmod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
/-! ### Theorems crossing div/mod versions -/
|
||||
|
||||
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.tdiv b = a / b := by
|
||||
by_cases b0 : b = 0
|
||||
· simp [b0]
|
||||
· rw [Int.tdiv_eq_iff_eq_mul_left b0 h, ← Int.ediv_eq_iff_eq_mul_left b0 h]
|
||||
|
||||
theorem fdiv_eq_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → a.fdiv b = a / b
|
||||
| _, b, ⟨c, rfl⟩ => by
|
||||
by_cases bz : b = 0
|
||||
· simp [bz]
|
||||
· rw [mul_fdiv_cancel_left _ bz, mul_ediv_cancel_left _ bz]
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -46,4 +46,23 @@ theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
@[simp] theorem toNat_eq_zero : ∀ {n : Int}, n.toNat = 0 ↔ n ≤ 0 := by omega
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
obtain ⟨a, rfl⟩ := h
|
||||
rw [natAbs_mul] at h₁
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
have := le_bmod (x := n) (m := m) (by omega)
|
||||
have := bmod_lt (x := n) (m := m) (by omega)
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
end Int
|
||||
|
||||
@@ -9,6 +9,7 @@ import Init.Data.Prod
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.DivMod.Bootstrap
|
||||
import Init.Data.Int.Cooper
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.RArray
|
||||
import Init.Data.AC
|
||||
@@ -531,8 +532,9 @@ def Poly.isValidLe (p : Poly) : Bool :=
|
||||
| .num k => k ≤ 0
|
||||
| _ => false
|
||||
|
||||
attribute [-simp] Int.not_le in
|
||||
theorem le_eq_false (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isUnsatLe → (lhs.denote ctx ≤ rhs.denote ctx) = False := by
|
||||
simp [Poly.isUnsatLe] <;> split <;> simp
|
||||
simp only [Poly.isUnsatLe] <;> split <;> simp
|
||||
next p k h =>
|
||||
intro h'
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
@@ -820,7 +822,7 @@ def le_neg_cert (p₁ p₂ : Poly) : Bool :=
|
||||
theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ → ¬ p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 := by
|
||||
simp [le_neg_cert]
|
||||
intro; subst p₂; simp; intro h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg <| Int.lt_of_not_ge h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg h
|
||||
simp at h
|
||||
exact h
|
||||
|
||||
@@ -846,9 +848,6 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
|
||||
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe → p.denote' ctx ≤ 0 → False := by
|
||||
simp [Poly.isUnsatLe]; split <;> simp
|
||||
intro h₁ h₂
|
||||
have := Int.lt_of_le_of_lt h₂ h₁
|
||||
simp at this
|
||||
|
||||
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 → p₂.denote' ctx = 0 := by
|
||||
simp at h
|
||||
@@ -1021,6 +1020,9 @@ theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p'
|
||||
simp [eq_coeff_cert]
|
||||
intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) → p.denote' ctx ≠ 0 → p'.denote' ctx ≠ 0 := by
|
||||
simp; intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq → p.denote' ctx ≠ 0 → False := by
|
||||
simp [Poly.isUnsatDiseq] <;> split <;> simp
|
||||
|
||||
@@ -1043,6 +1045,434 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
intro h; rw [← Int.sub_eq_zero] at h
|
||||
rw [←Int.sub_eq_add_neg]; assumption
|
||||
|
||||
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
|
||||
p₂ == p₁.mul (-1)
|
||||
|
||||
theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
|
||||
: eq_of_le_ge_cert p₁ p₂ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₁.denote' ctx = 0 := by
|
||||
simp [eq_of_le_ge_cert]
|
||||
intro; subst p₂; simp
|
||||
intro h₁ h₂
|
||||
replace h₂ := Int.neg_le_of_neg_le h₂; simp at h₂
|
||||
simp [Int.eq_iff_le_and_ge, *]
|
||||
|
||||
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
|
||||
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
|
||||
p₃ == p₁.addConst 1
|
||||
|
||||
theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: le_of_le_diseq_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp [le_of_le_diseq_cert]
|
||||
have (a : Int) : a ≤ 0 → ¬ a = 0 → 1 + a ≤ 0 := by
|
||||
intro h₁ h₂; cases (Int.lt_or_gt_of_ne h₂)
|
||||
next => apply Int.le_of_lt_add_one; rw [Int.add_comm, Int.add_lt_add_iff_right]; assumption
|
||||
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
|
||||
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
|
||||
|
||||
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
|
||||
p₂ == p₁.addConst 1 &&
|
||||
p₃ == (p₁.mul (-1)).addConst 1
|
||||
|
||||
theorem diseq_split (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → p₂.denote' ctx ≤ 0 ∨ p₃.denote' ctx ≤ 0 := by
|
||||
simp [diseq_split_cert]
|
||||
intro _ _; subst p₂ p₃; simp
|
||||
generalize p₁.denote ctx = p
|
||||
intro h; cases Int.lt_or_gt_of_ne h
|
||||
next h => have := Int.add_one_le_of_lt h; rw [Int.add_comm]; simp [*]
|
||||
next h => have := Int.add_one_le_of_lt (Int.neg_lt_neg h); simp at this; simp [*]
|
||||
|
||||
theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → ¬p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
intro h₁ h₂ h₃
|
||||
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
|
||||
|
||||
def OrOver (n : Nat) (p : Nat → Prop) : Prop :=
|
||||
match n with
|
||||
| 0 => False
|
||||
| n+1 => p n ∨ OrOver n p
|
||||
|
||||
theorem orOver_unsat {p} : ¬ OrOver 0 p := by simp [OrOver]
|
||||
|
||||
theorem orOver_resolve {n p} : OrOver (n+1) p → ¬ p n → OrOver n p := by
|
||||
intro h₁ h₂
|
||||
rw [OrOver] at h₁
|
||||
cases h₁
|
||||
· contradiction
|
||||
· assumption
|
||||
|
||||
private theorem orOver_of_p {i n p} (h₁ : i < n) (h₂ : p i) : OrOver n p := by
|
||||
induction n
|
||||
next => simp at h₁
|
||||
next n ih =>
|
||||
simp [OrOver]
|
||||
cases Nat.eq_or_lt_of_le <| Nat.le_of_lt_add_one h₁
|
||||
next h => subst i; exact Or.inl h₂
|
||||
next h => exact Or.inr (ih h)
|
||||
|
||||
private theorem orOver_of_exists {n p} : (∃ k, k < n ∧ p k) → OrOver n p := by
|
||||
intro ⟨k, h₁, h₂⟩
|
||||
apply orOver_of_p h₁ h₂
|
||||
|
||||
private theorem ofNat_toNat {a : Int} : a ≥ 0 → Int.ofNat a.toNat = a := by cases a <;> simp
|
||||
private theorem cast_toNat {a : Int} : a ≥ 0 → a.toNat = a := by cases a <;> simp
|
||||
private theorem ofNat_lt {a : Int} {n : Nat} : a ≥ 0 → a < Int.ofNat n → a.toNat < n := by cases a <;> simp
|
||||
@[local simp] private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem gcd_neg_left (a b : Int) : Int.gcd (-a) b = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_neg_right (a b : Int) : Int.gcd a (-b) = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
|
||||
@[local simp] private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
|
||||
|
||||
private theorem cooper_dvd_left_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm a (a * d / Int.gcd (a * d) c)) fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k ∧
|
||||
a * d ∣ c * p + (-a) * s + c * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_left a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
rw [Int.neg_mul] at h₂
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
rw [Int.neg_ediv_of_dvd Int.gcd_dvd_left] at h₂
|
||||
simp only [lcm_neg_right] at h₂
|
||||
have : c * k + c * p + -(a * s) = c * p + -(a * s) + c * k := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
rw [← ofNat_toNat h₁] at h₃ h₄ h₅
|
||||
rw [Int.add_comm] at h₄
|
||||
have := ofNat_lt h₁ h₂
|
||||
apply orOver_of_exists
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : b * Int.ofNat k.toNat + b * p + -(a * q) = b * p + -(a * q) + b * Int.ofNat k.toNat := by ac_rfl
|
||||
rw [this] at h₃
|
||||
exists k.toNat
|
||||
|
||||
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
|
||||
|
||||
def Poly.tail (p : Poly) : Poly :=
|
||||
match p with
|
||||
| .add _ _ p => p
|
||||
| _ => p
|
||||
|
||||
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
∧ a*d ∣ (p₂.addConst (c*k)).denote' ctx
|
||||
|
||||
private theorem denote'_mul_combine_mul_addConst_eq (ctx : Context) (p q : Poly) (a b c : Int)
|
||||
: ((p.mul b |>.combine (q.mul a)).addConst c).denote' ctx = b*p.denote ctx + a*q.denote ctx + c := by
|
||||
simp
|
||||
|
||||
private theorem denote'_addConst_eq (ctx : Context) (p : Poly) (a : Int)
|
||||
: (p.addConst a).denote' ctx = p.denote ctx + a := by
|
||||
simp
|
||||
|
||||
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_left_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_left_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd1_cert p₁ p' a k → a ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let p := p₁.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
d' == a*d && p' == p₂.addConst (c*k)
|
||||
|
||||
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' → d' ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_left_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver a.natAbs fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true] at h
|
||||
assumption
|
||||
|
||||
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
|
||||
n == a.natAbs
|
||||
|
||||
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_left_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_left_split ctx p₁ p₂) := by
|
||||
unfold cooper_left_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_left_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
assumption
|
||||
|
||||
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_ineq_cert p₁ p₂ k b p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_left_split_ineq_cert, cooper_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_dvd_cert p₁ p' a k → a ∣ p'.denote ctx := by
|
||||
simp [cooper_left_split_dvd_cert, cooper_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
private theorem cooper_dvd_right_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm b (b * d / Int.gcd (b * d) c)) fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k ∧
|
||||
b * d ∣ (-c) * q + b * s + (-c) * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
apply orOver_of_exists
|
||||
have hlt := ofNat_lt h₁ h₂
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : -(a * k) + b * p + -(a * q) = b * p + -(a * q) + -(a * k) := by ac_rfl
|
||||
rw [this] at h₃; clear this
|
||||
rw [Int.sub_neg, Int.add_comm] at h₄
|
||||
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
exists k.toNat
|
||||
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
|
||||
|
||||
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
|
||||
|
||||
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
∧ b*d ∣ (p₂.addConst ((-c)*k)).denote' ctx
|
||||
|
||||
theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_right_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_right_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_right_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd1_cert p₂ p' b k → b ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
d' == b*d && p' == p₂.addConst ((-c)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' → d' ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_right_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver b.natAbs fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true, Int.neg_zero] at h
|
||||
assumption
|
||||
|
||||
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
|
||||
|
||||
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_right_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_right_split ctx p₁ p₂) := by
|
||||
unfold cooper_right_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_right_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
assumption
|
||||
|
||||
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_ineq_cert p₁ p₂ k a p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_right_split_ineq_cert, cooper_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_dvd_cert p₂ p' b k → b ∣ p'.denote ctx := by
|
||||
simp [cooper_right_split_dvd_cert, cooper_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
end Int.Linear
|
||||
|
||||
theorem Int.not_le_eq (a b : Int) : (¬a ≤ b) = (b + 1 ≤ a) := by
|
||||
|
||||
@@ -133,10 +133,10 @@ protected theorem lt_of_not_ge {a b : Int} (h : ¬a ≤ b) : b < a :=
|
||||
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a ≤ b :=
|
||||
(Int.lt_iff_le_not_le.mp h).right
|
||||
|
||||
protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
@[simp] protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
|
||||
|
||||
protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
@[simp] protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
by rw [← Int.not_le, Decidable.not_not]
|
||||
|
||||
protected theorem lt_trichotomy (a b : Int) : a < b ∨ a = b ∨ b < a :=
|
||||
|
||||
@@ -662,6 +662,10 @@ def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α :
|
||||
@[simp] theorem unattach_cons {p : α → Prop} {a : { x // p x }} {l : List { x // p x }} :
|
||||
(a :: l).unattach = a.val :: l.unattach := rfl
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {l : List { x // p x }} {a} :
|
||||
a ∈ l.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ l := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem length_unattach {p : α → Prop} {l : List { x // p x }} :
|
||||
l.unattach.length = l.length := by
|
||||
unfold unattach
|
||||
@@ -766,6 +770,16 @@ and simplifies these to the function directly taking the value.
|
||||
simp [hf, find?_cons]
|
||||
split <;> simp [ih]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.all f = l.unattach.all g := by
|
||||
simp [all_eq, hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.any f = l.unattach.any g := by
|
||||
simp [any_eq, hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : List { x // p x }}
|
||||
|
||||
@@ -212,6 +212,7 @@ def mapMono (as : List α) (f : α → α) : List α :=
|
||||
|
||||
/-! ## Additional lemmas required for bootstrapping `Array`. -/
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
|
||||
(as ++ bs)[i] = as[i] := by
|
||||
induction as generalizing i with
|
||||
@@ -221,6 +222,7 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as
|
||||
| zero => rfl
|
||||
| succ i => apply ih
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i) {h₂} :
|
||||
(as ++ bs)[i]'h₂ =
|
||||
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
|
||||
|
||||
@@ -101,6 +101,12 @@ This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
|
||||
| some x, some y => r x y
|
||||
| _, _ => False
|
||||
|
||||
@[inline] protected def le (r : α → β → Prop) : Option α → Option β → Prop
|
||||
| none, some _ => True
|
||||
| none, none => True
|
||||
| some _, none => False
|
||||
| some x, some y => r x y
|
||||
|
||||
instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
|
||||
| none, some _ => isTrue trivial
|
||||
| some x, some y => s x y
|
||||
@@ -217,18 +223,24 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
@[simp] theorem any_none : Option.any p none = false := rfl
|
||||
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
|
||||
|
||||
/-- The minimum of two optional values. -/
|
||||
/--
|
||||
The minimum of two optional values.
|
||||
|
||||
Note this treats `none` as the least element,
|
||||
so `min none x = min x none = none` for all `x : Option α`.
|
||||
Prior to nightly-2025-02-27, we instead had `min none (some x) = min (some x) none = some x`.
|
||||
-/
|
||||
protected def min [Min α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Min.min x y)
|
||||
| some x, none => some x
|
||||
| none, some y => some y
|
||||
| some _, none => none
|
||||
| none, some _ => none
|
||||
| none, none => none
|
||||
|
||||
instance [Min α] : Min (Option α) where min := Option.min
|
||||
|
||||
@[simp] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = some a := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = some b := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = none := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = none := rfl
|
||||
@[simp] theorem min_none_none [Min α] : min (none : Option α) none = none := rfl
|
||||
|
||||
/-- The maximum of two optional values. -/
|
||||
@@ -251,6 +263,9 @@ end Option
|
||||
instance [LT α] : LT (Option α) where
|
||||
lt := Option.lt (· < ·)
|
||||
|
||||
instance [LE α] : LE (Option α) where
|
||||
le := Option.le (· ≤ ·)
|
||||
|
||||
@[always_inline]
|
||||
instance : Functor Option where
|
||||
map := Option.map
|
||||
|
||||
@@ -673,4 +673,80 @@ theorem pmap_map (o : Option α) (f : α → β) {p : β → Prop} (g : ∀ b, p
|
||||
o.pelim g (fun a h => g' (f a (H a h))) := by
|
||||
cases o <;> simp
|
||||
|
||||
/-! ### LT and LE -/
|
||||
|
||||
@[simp] theorem not_lt_none [LT α] {a : Option α} : ¬ a < none := by cases a <;> simp [LT.lt, Option.lt]
|
||||
@[simp] theorem none_lt_some [LT α] {a : α} : none < some a := by simp [LT.lt, Option.lt]
|
||||
@[simp] theorem some_lt_some [LT α] {a b : α} : some a < some b ↔ a < b := by simp [LT.lt, Option.lt]
|
||||
|
||||
@[simp] theorem none_le [LE α] {a : Option α} : none ≤ a := by cases a <;> simp [LE.le, Option.le]
|
||||
@[simp] theorem not_some_le_none [LE α] {a : α} : ¬ some a ≤ none := by simp [LE.le, Option.le]
|
||||
@[simp] theorem some_le_some [LE α] {a b : α} : some a ≤ some b ↔ a ≤ b := by simp [LE.le, Option.le]
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
theorem min_eq_left [LE α] [Min α] (min_eq_left : ∀ x y : α, x ≤ y → min x y = x)
|
||||
{a b : Option α} (h : a ≤ b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right [LE α] [Min α] (min_eq_right : ∀ x y : α, y ≤ x → min x y = y)
|
||||
{a b : Option α} (h : b ≤ a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_left_of_lt [LT α] [Min α] (min_eq_left : ∀ x y : α, x < y → min x y = x)
|
||||
{a b : Option α} (h : a < b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right_of_lt [LT α] [Min α] (min_eq_right : ∀ x y : α, y < x → min x y = y)
|
||||
{a b : Option α} (h : b < a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_or [LE α] [Min α] (min_eq_or : ∀ x y : α, min x y = x ∨ min x y = y)
|
||||
{a b : Option α} : min a b = a ∨ min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_left [LE α] [Min α] (min_le_left : ∀ x y : α, min x y ≤ x)
|
||||
{a b : Option α} : min a b ≤ a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_right [LE α] [Min α] (min_le_right : ∀ x y : α, min x y ≤ y)
|
||||
{a b : Option α} : min a b ≤ b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem le_min [LE α] [Min α] (le_min : ∀ x y z : α, x ≤ min y z ↔ x ≤ y ∧ x ≤ z)
|
||||
{a b c : Option α} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
theorem max_eq_left [LE α] [Max α] (max_eq_left : ∀ x y : α, x ≤ y → max x y = y)
|
||||
{a b : Option α} (h : a ≤ b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right [LE α] [Max α] (max_eq_right : ∀ x y : α, y ≤ x → max x y = x)
|
||||
{a b : Option α} (h : b ≤ a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_left_of_lt [LT α] [Max α] (max_eq_left : ∀ x y : α, x < y → max x y = y)
|
||||
{a b : Option α} (h : a < b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right_of_lt [LT α] [Max α] (max_eq_right : ∀ x y : α, y < x → max x y = x)
|
||||
{a b : Option α} (h : b < a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_or [LE α] [Max α] (max_eq_or : ∀ x y : α, max x y = x ∨ max x y = y)
|
||||
{a b : Option α} : max a b = a ∨ max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem left_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (left_le_max : ∀ x y : α, x ≤ max x y)
|
||||
{a b : Option α} : a ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem right_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (right_le_max : ∀ x y : α, y ≤ max x y)
|
||||
{a b : Option α} : b ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_le [LE α] [Max α] (max_le : ∀ x y z : α, max x y ≤ z ↔ x ≤ z ∧ y ≤ z)
|
||||
{a b c : Option α} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
end Option
|
||||
|
||||
@@ -8,6 +8,7 @@ import Init.Data.SInt.Basic
|
||||
import Init.Data.SInt.Float
|
||||
import Init.Data.SInt.Float32
|
||||
import Init.Data.SInt.Lemmas
|
||||
import Init.Data.SInt.Bitwise
|
||||
|
||||
/-!
|
||||
This module contains the definitions and basic theory about signed fixed width integer types.
|
||||
|
||||
@@ -77,6 +77,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
|
||||
-/
|
||||
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
|
||||
|
||||
theorem Int8.toBitVec.inj : {x y : Int8} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
|
||||
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
|
||||
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
|
||||
@@ -110,8 +113,8 @@ instance : ReprAtom Int8 := ⟨⟩
|
||||
instance : Hashable Int8 where
|
||||
hash i := i.toUInt8.toUInt64
|
||||
|
||||
instance : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance : Neg Int8 where
|
||||
instance Int8.instOfNat : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance Int8.instNeg : Neg Int8 where
|
||||
neg := Int8.neg
|
||||
|
||||
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
|
||||
@@ -213,6 +216,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
|
||||
-/
|
||||
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
|
||||
|
||||
theorem Int16.toBitVec.inj : {x y : Int16} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
|
||||
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
|
||||
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
|
||||
@@ -250,8 +256,8 @@ instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : Hashable Int16 where
|
||||
hash i := i.toUInt16.toUInt64
|
||||
|
||||
instance : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance : Neg Int16 where
|
||||
instance Int16.instOfNat : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance Int16.instNeg : Neg Int16 where
|
||||
neg := Int16.neg
|
||||
|
||||
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
|
||||
@@ -353,6 +359,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
|
||||
-/
|
||||
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
|
||||
|
||||
theorem Int32.toBitVec.inj : {x y : Int32} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
|
||||
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
|
||||
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
|
||||
@@ -394,8 +403,8 @@ instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : Hashable Int32 where
|
||||
hash i := i.toUInt32.toUInt64
|
||||
|
||||
instance : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance : Neg Int32 where
|
||||
instance Int32.instOfNat : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance Int32.instNeg : Neg Int32 where
|
||||
neg := Int32.neg
|
||||
|
||||
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
|
||||
@@ -497,6 +506,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
|
||||
-/
|
||||
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
|
||||
|
||||
theorem Int64.toBitVec.inj : {x y : Int64} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
|
||||
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
|
||||
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
|
||||
@@ -542,8 +554,8 @@ instance : ReprAtom Int64 := ⟨⟩
|
||||
instance : Hashable Int64 where
|
||||
hash i := i.toUInt64
|
||||
|
||||
instance : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance : Neg Int64 where
|
||||
instance Int64.instOfNat : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance Int64.instNeg : Neg Int64 where
|
||||
neg := Int64.neg
|
||||
|
||||
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
|
||||
@@ -645,6 +657,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
|
||||
-/
|
||||
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
|
||||
|
||||
theorem ISize.toBitVec.inj : {x y : ISize} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
|
||||
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
|
||||
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
|
||||
@@ -700,8 +715,8 @@ instance : ReprAtom ISize := ⟨⟩
|
||||
instance : Hashable ISize where
|
||||
hash i := i.toUSize.toUInt64
|
||||
|
||||
instance : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance : Neg ISize where
|
||||
instance ISize.instOfNat : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance ISize.instNeg : Neg ISize where
|
||||
neg := ISize.neg
|
||||
|
||||
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
|
||||
|
||||
57
src/Init/Data/SInt/Bitwise.lean
Normal file
57
src/Init/Data/SInt/Bitwise.lean
Normal file
@@ -0,0 +1,57 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
macro "declare_bitwise_int_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec.sdiv b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec.srem b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_not {a : $typeName} : (~~~a).toBitVec = ~~~a.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec.sshiftRight' (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_abs (a : $typeName) : a.abs.toBitVec = a.toBitVec.abs := rfl
|
||||
|
||||
end $typeName
|
||||
)
|
||||
declare_bitwise_int_theorems Int8 8
|
||||
declare_bitwise_int_theorems Int16 16
|
||||
declare_bitwise_int_theorems Int32 32
|
||||
declare_bitwise_int_theorems Int64 64
|
||||
declare_bitwise_int_theorems ISize System.Platform.numBits
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt8 {b : Bool} : b.toInt8.toBitVec = (BitVec.ofBool b).setWidth 8 := by
|
||||
cases b <;> simp [toInt8]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt16 {b : Bool} : b.toInt16.toBitVec = (BitVec.ofBool b).setWidth 16 := by
|
||||
cases b <;> simp [toInt16]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt32 {b : Bool} : b.toInt32.toBitVec = (BitVec.ofBool b).setWidth 32 := by
|
||||
cases b <;> simp [toInt32]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt64 {b : Bool} : b.toInt64.toBitVec = (BitVec.ofBool b).setWidth 64 := by
|
||||
cases b <;> simp [toInt64]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toISize {b : Bool} :
|
||||
b.toISize.toBitVec = (BitVec.ofBool b).setWidth System.Platform.numBits := by
|
||||
cases b
|
||||
· simp [toISize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toISize]
|
||||
@@ -5,6 +5,34 @@ Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
open Lean in
|
||||
set_option hygiene false in
|
||||
macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
|
||||
let mut cmds ← Syntax.getArgs <$> `(
|
||||
namespace $typeName
|
||||
|
||||
@[int_toBitVec] theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec.sle b.toBitVec := Iff.rfl
|
||||
@[int_toBitVec] theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec.slt b.toBitVec := Iff.rfl
|
||||
theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec ↔ a = b :=
|
||||
⟨toBitVec.inj, (· ▸ rfl)⟩
|
||||
@[int_toBitVec] theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b ↔ a.toBitVec = b.toBitVec :=
|
||||
toBitVec_inj.symm
|
||||
@[int_toBitVec] theorem ne_iff_toBitVec_ne {a b : $typeName} : a ≠ b ↔ a.toBitVec ≠ b.toBitVec :=
|
||||
Decidable.not_iff_not.2 eq_iff_toBitVec_eq
|
||||
@[simp] theorem toBitVec_ofNat {n : Nat} : toBitVec (ofNat n) = BitVec.ofNat _ n := rfl
|
||||
@[simp, int_toBitVec] theorem toBitVec_ofNatOfNat {n : Nat} : toBitVec (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
|
||||
|
||||
end $typeName
|
||||
)
|
||||
return ⟨mkNullNode cmds⟩
|
||||
|
||||
declare_int_theorems Int8 8
|
||||
declare_int_theorems Int16 16
|
||||
declare_int_theorems Int32 32
|
||||
declare_int_theorems Int64 64
|
||||
declare_int_theorems ISize System.Platform.numBits
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := rfl
|
||||
@@ -23,3 +51,48 @@ import Init.Data.SInt.Basic
|
||||
@[simp] theorem UInt32.toUInt32_toInt32 (x : UInt32) : x.toInt32.toUInt32 = x := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toInt64 (x : UInt64) : x.toInt64.toUInt64 = x := rfl
|
||||
@[simp] theorem USize.toUSize_toISize (x : USize) : x.toISize.toUSize = x := rfl
|
||||
|
||||
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := rfl
|
||||
@[simp] theorem ISize.toBitVec_zero : (0 : ISize).toBitVec = 0 := rfl
|
||||
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
|
||||
|
||||
@[simp] theorem Int8.neg_zero : -(0 : Int8) = 0 := rfl
|
||||
@[simp] theorem Int16.neg_zero : -(0 : Int16) = 0 := rfl
|
||||
@[simp] theorem Int32.neg_zero : -(0 : Int32) = 0 := rfl
|
||||
@[simp] theorem Int64.neg_zero : -(0 : Int64) = 0 := rfl
|
||||
@[simp] theorem ISize.neg_zero : -(0 : ISize) = 0 := ISize.toBitVec.inj (by simp)
|
||||
|
||||
theorem ISize.toNat_toBitVec_ofNat_of_lt {n : Nat} (h : n < 2^32) :
|
||||
(ofNat n).toBitVec.toNat = n :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le h (by cases USize.size_eq <;> simp_all +decide))
|
||||
|
||||
theorem ISize.toInt_ofInt {n : Int} (hn : -2^31 ≤ n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
|
||||
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self] <;> cases System.Platform.numBits_eq
|
||||
<;> (simp_all; try omega)
|
||||
|
||||
theorem ISize.toNatClampNeg_ofInt_eq_zero {n : Int} (hn : -2^31 ≤ n) (hn' : n ≤ 0) :
|
||||
toNatClampNeg (ofInt n) = 0 := by
|
||||
rwa [toNatClampNeg, toInt_ofInt hn (by omega), Int.toNat_eq_zero]
|
||||
|
||||
theorem ISize.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
|
||||
toBitVec.inj (by simp [BitVec.ofInt_neg])
|
||||
|
||||
theorem ISize.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n :=
|
||||
toBitVec.inj (by simp)
|
||||
|
||||
theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
|
||||
rw [← neg_ofInt, ofInt_eq_ofNat]
|
||||
|
||||
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) :
|
||||
toNatClampNeg (ofNat n) = n := by
|
||||
rw [toNatClampNeg, ← ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
|
||||
|
||||
theorem ISize.toNatClampNeg_neg_ofNat_of_le {n : Nat} (h : n ≤ 2 ^ 31) :
|
||||
toNatClampNeg (-ofNat n) = 0 := by
|
||||
rw [neg_ofNat, toNatClampNeg_ofInt_eq_zero (by omega) (by omega)]
|
||||
|
||||
theorem ISize.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
|
||||
rw [← ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
|
||||
|
||||
theorem ISize.toInt_neg_ofNat_of_le {n : Nat} (h : n ≤ 2 ^ 31) : toInt (-ofNat n) = -n := by
|
||||
rw [← ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]
|
||||
|
||||
@@ -287,6 +287,8 @@ theorem UInt32.size_le_usizeSize : UInt32.size ≤ USize.size := by
|
||||
theorem USize.size_eq_two_pow : USize.size = 2 ^ System.Platform.numBits := rfl
|
||||
theorem USize.toNat_lt_two_pow_numBits (n : USize) : n.toNat < 2 ^ System.Platform.numBits := n.toFin.isLt
|
||||
@[simp] theorem USize.toNat_lt (n : USize) : n.toNat < 2 ^ 64 := Nat.lt_of_lt_of_le n.toFin.isLt size_le
|
||||
theorem USize.size_le_uint64Size : USize.size ≤ UInt64.size := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
theorem UInt8.toNat_lt_usizeSize (n : UInt8) : n.toNat < USize.size :=
|
||||
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
|
||||
@@ -373,7 +375,7 @@ theorem USize.size_dvd_uInt64Size : USize.size ∣ UInt64.size := by cases USize
|
||||
@[simp] theorem UInt32.toFin_toUSize (n : UInt32) :
|
||||
n.toUSize.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_uint64Size := rfl
|
||||
|
||||
@[simp] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
|
||||
@@ -7,8 +7,8 @@ prelude
|
||||
import Init.Data.Vector.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Vector
|
||||
|
||||
@@ -473,6 +473,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Vector { x // p x } n) : Vect
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Vector.map_push]
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Vector { x // p x } n} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem unattach_mk {p : α → Prop} {xs : Array { x // p x }} {h : xs.size = n} :
|
||||
(mk xs h).unattach = mk xs.unattach (by simpa using h) := by
|
||||
simp [unattach]
|
||||
@@ -552,6 +556,18 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [Array.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Vector { x // p x } n} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.all f = xs.unattach.all g := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Vector { x // p x } n} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.any f = xs.unattach.any g := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {xs : Vector { x // p x } n} :
|
||||
|
||||
@@ -8,6 +8,7 @@ prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.InsertIdx
|
||||
import Init.Data.Array.Range
|
||||
import Init.Data.Range
|
||||
import Init.Data.Stream
|
||||
|
||||
@@ -17,8 +18,8 @@ import Init.Data.Stream
|
||||
`Vector α n` is a thin wrapper around `Array α` for arrays of fixed size `n`.
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
/-- `Vector α n` is an `Array α` with size `n`. -/
|
||||
structure Vector (α : Type u) (n : Nat) extends Array α where
|
||||
|
||||
@@ -15,8 +15,8 @@ import Init.Data.Array.Find
|
||||
We are still missing results about `idxOf?`, `findIdx`, and `findIdx?`.
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Vector
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import Init.Data.Array.Find
|
||||
Lemmas about `Vector α n`
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
@@ -1592,9 +1592,11 @@ theorem getElem_append (xs : Vector α n) (ys : Vector α m) (i : Nat) (hi : i <
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp [Array.getElem_append, hi]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_left {xs : Vector α n} {ys : Vector α m} {i : Nat} (hi : i < n) :
|
||||
(xs ++ ys)[i] = xs[i] := by simp [getElem_append, hi]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_right {xs : Vector α n} {ys : Vector α m} {i : Nat} (h : i < n + m) (hi : n ≤ i) :
|
||||
(xs ++ ys)[i] = ys[i - n] := by
|
||||
rw [getElem_append, dif_neg (by omega)]
|
||||
@@ -2068,6 +2070,12 @@ theorem flatMap_mkArray {β} (f : α → Vector β m) : (mkVector n a).flatMap f
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
theorem getElem_eq_getElem_reverse {xs : Vector α n} {i} (h : i < n) :
|
||||
xs[i] = xs.reverse[n - 1 - i] := by
|
||||
rw [getElem_reverse]
|
||||
congr
|
||||
omega
|
||||
|
||||
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
|
||||
theorem getElem?_reverse' {xs : Vector α n} (i j) (h : i + j + 1 = n) : xs.reverse[i]? = xs[j]? := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
@@ -2474,6 +2482,14 @@ theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Vector α n} {a : α} :
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
|
||||
defeq issues in the implicit size argument.
|
||||
-/
|
||||
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
|
||||
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
|
||||
getElem_pop h
|
||||
|
||||
theorem getElem?_pop (xs : Vector α n) (i : Nat) :
|
||||
xs.pop[i]? = if i < n - 1 then xs[i]? else none := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
@@ -2585,6 +2601,161 @@ theorem replace_extract {xs : Vector α n} {i : Nat} :
|
||||
|
||||
end replace
|
||||
|
||||
/-! ## Logic -/
|
||||
|
||||
/-! ### any / all -/
|
||||
|
||||
theorem not_any_eq_all_not (xs : Vector α n) (p : α → Bool) : (!xs.any p) = xs.all fun a => !p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.not_any_eq_all_not]
|
||||
|
||||
theorem not_all_eq_any_not (xs : Vector α n) (p : α → Bool) : (!xs.all p) = xs.any fun a => !p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.not_all_eq_any_not]
|
||||
|
||||
theorem and_any_distrib_left (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(q && xs.any p) = xs.any fun a => q && p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.and_any_distrib_left]
|
||||
|
||||
theorem and_any_distrib_right (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(xs.any p && q) = xs.any fun a => p a && q := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.and_any_distrib_right]
|
||||
|
||||
theorem or_all_distrib_left (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(q || xs.all p) = xs.all fun a => q || p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.or_all_distrib_left]
|
||||
|
||||
theorem or_all_distrib_right (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(xs.all p || q) = xs.all fun a => p a || q := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.or_all_distrib_right]
|
||||
|
||||
theorem any_eq_not_all_not (xs : Vector α n) (p : α → Bool) : xs.any p = !xs.all (!p .) := by
|
||||
simp only [not_all_eq_any_not, Bool.not_not]
|
||||
|
||||
@[simp] theorem any_map {xs : Vector α n} {p : β → Bool} : (xs.map f).any p = xs.any (p ∘ f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_map {xs : Vector α n} {p : β → Bool} : (xs.map f).all p = xs.all (p ∘ f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_filter {xs : Vector α n} {p q : α → Bool} :
|
||||
(xs.filter p).any q = xs.any fun a => p a && q a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_filter {xs : Vector α n} {p q : α → Bool} :
|
||||
(xs.filter p).all q = xs.all fun a => p a → q a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_filterMap {xs : Vector α n} {f : α → Option β} {p : β → Bool} :
|
||||
(xs.filterMap f).any p = xs.any fun a => match f a with | some b => p b | none => false := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
rfl
|
||||
|
||||
@[simp] theorem all_filterMap {xs : Vector α n} {f : α → Option β} {p : β → Bool} :
|
||||
(xs.filterMap f).all p = xs.all fun a => match f a with | some b => p b | none => true := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
rfl
|
||||
|
||||
@[simp] theorem any_append {xs : Vector α n} {ys : Vector α m} :
|
||||
(xs ++ ys).any f = (xs.any f || ys.any f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_append {xs : Vector α n} {ys : Vector α m} :
|
||||
(xs ++ ys).all f = (xs.all f && ys.all f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp
|
||||
|
||||
@[congr] theorem anyM_congr [Monad m]
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
xs.anyM p = ys.anyM q := by
|
||||
have : p = q := by funext a; apply h
|
||||
subst this
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[congr] theorem any_congr
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → Bool} (h : ∀ a, p a = q a) :
|
||||
xs.any p = ys.any q := by
|
||||
unfold any
|
||||
apply anyM_congr w h
|
||||
|
||||
@[congr] theorem allM_congr [Monad m]
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
xs.allM p = ys.allM q := by
|
||||
have : p = q := by funext a; apply h
|
||||
subst this
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[congr] theorem all_congr
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → Bool} (h : ∀ a, p a = q a) :
|
||||
xs.all p = ys.all q := by
|
||||
unfold all
|
||||
apply allM_congr w h
|
||||
|
||||
@[simp] theorem any_flatten {xss : Vector (Vector α n) m} : xss.flatten.any f = xss.any (any · f) := by
|
||||
cases xss using vector₂_induction
|
||||
simp
|
||||
|
||||
@[simp] theorem all_flatten {xss : Vector (Vector α n) m} : xss.flatten.all f = xss.all (all · f) := by
|
||||
cases xss using vector₂_induction
|
||||
simp
|
||||
|
||||
@[simp] theorem any_flatMap {xs : Vector α n} {f : α → Vector β m} {p : β → Bool} :
|
||||
(xs.flatMap f).any p = xs.any fun a => (f a).any p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [flatMap_mk, any_mk, Array.size_flatMap, size_toArray, Array.any_flatMap']
|
||||
congr
|
||||
funext
|
||||
congr
|
||||
simp [Vector.size_toArray]
|
||||
|
||||
@[simp] theorem all_flatMap {xs : Vector α n} {f : α → Vector β m} {p : β → Bool} :
|
||||
(xs.flatMap f).all p = xs.all fun a => (f a).all p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [flatMap_mk, all_mk, Array.size_flatMap, size_toArray, Array.all_flatMap']
|
||||
congr
|
||||
funext
|
||||
congr
|
||||
simp [Vector.size_toArray]
|
||||
|
||||
@[simp] theorem any_reverse {xs : Vector α n} : xs.reverse.any f = xs.any f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_reverse {xs : Vector α n} : xs.reverse.all f = xs.all f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_cast {xs : Vector α n} : (xs.cast h).any f = xs.any f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_cast {xs : Vector α n} : (xs.cast h).all f = xs.all f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_mkVector {n : Nat} {a : α} :
|
||||
(mkVector n a).any f = if n = 0 then false else f a := by
|
||||
induction n <;> simp_all [mkVector_succ']
|
||||
|
||||
@[simp] theorem all_mkVector {n : Nat} {a : α} :
|
||||
(mkVector n a).all f = if n = 0 then true else f a := by
|
||||
induction n <;> simp_all +contextual [mkVector_succ']
|
||||
|
||||
/-! Content below this point has not yet been aligned with `List` and `Array`. -/
|
||||
|
||||
set_option linter.indexVariables false in
|
||||
@@ -2592,14 +2763,6 @@ set_option linter.indexVariables false in
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
|
||||
defeq issues in the implicit size argument.
|
||||
-/
|
||||
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
|
||||
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
|
||||
getElem_pop h
|
||||
|
||||
@[simp] theorem push_pop_back (xs : Vector α (n + 1)) : xs.pop.push xs.back = xs := by
|
||||
ext i
|
||||
by_cases h : i < n
|
||||
@@ -2663,11 +2826,6 @@ theorem swap_comm (xs : Vector α n) {i j : Nat} {hi hj} :
|
||||
simp only [swap_mk, mk.injEq]
|
||||
rw [Array.swap_comm]
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
|
||||
simp [Vector.range]
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
@[simp] theorem getElem_take (xs : Vector α n) (j : Nat) (hi : i < min n j) :
|
||||
|
||||
@@ -115,6 +115,9 @@ theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
|
||||
simp [Vector.range]
|
||||
|
||||
theorem range_eq_range' (n : Nat) : range n = range' 0 n := by
|
||||
simp [range, range', Array.range_eq_range']
|
||||
|
||||
|
||||
@@ -111,9 +111,7 @@ def isExact : Constraint → Bool
|
||||
|
||||
theorem not_sat_of_isImpossible (h : isImpossible c) {t} : ¬ c.sat t := by
|
||||
rcases c with ⟨_ | l, _ | u⟩ <;> simp [isImpossible, sat] at h ⊢
|
||||
intro w
|
||||
rw [Int.not_le]
|
||||
exact Int.lt_of_lt_of_le h w
|
||||
exact Int.lt_of_lt_of_le h
|
||||
|
||||
/--
|
||||
Scale a constraint by multiplying by an integer.
|
||||
@@ -139,17 +137,14 @@ theorem scale_sat {c : Constraint} (k) (w : c.sat t) : (scale k c).sat (k * t) :
|
||||
· rcases c with ⟨_ | l, _ | u⟩ <;> split <;> rename_i h <;> simp_all [sat, flip, map]
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· constructor
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.1 (Int.le_of_lt h)
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.2 (Int.le_of_lt h)
|
||||
· replace h := Int.not_lt.mp h
|
||||
constructor
|
||||
· constructor
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.2
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.1
|
||||
|
||||
@@ -181,13 +176,13 @@ theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
|
||||
|
||||
/-- The conjunction of two constraints. -/
|
||||
def combine (x y : Constraint) : Constraint where
|
||||
lowerBound := max x.lowerBound y.lowerBound
|
||||
upperBound := min x.upperBound y.upperBound
|
||||
lowerBound := Option.merge max x.lowerBound y.lowerBound
|
||||
upperBound := Option.merge min x.upperBound y.upperBound
|
||||
|
||||
theorem combine_sat : (c : Constraint) → (c' : Constraint) → (t : Int) →
|
||||
(c.combine c').sat t = (c.sat t ∧ c'.sat t) := by
|
||||
rintro ⟨_ | l₁, _ | u₁⟩ <;> rintro ⟨_ | l₂, _ | u₂⟩ t
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le] at *
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.merge] at *
|
||||
· rw [And.comm]
|
||||
· rw [← and_assoc, And.comm (a := l₂ ≤ t), and_assoc]
|
||||
· rw [and_assoc]
|
||||
@@ -210,21 +205,19 @@ theorem div_sat (c : Constraint) (t : Int) (k : Nat) (n : k ≠ 0) (h : (k : Int
|
||||
· simp_all [sat, div]
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
rw [← Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
constructor
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.1
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
rw [← Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.2
|
||||
|
||||
/--
|
||||
|
||||
@@ -514,7 +514,9 @@ def inferStep : InterpM Bool := do
|
||||
let currentVal ← getFunVal idx
|
||||
withReader (fun ctx => { ctx with currFnIdx := idx }) do
|
||||
decl.params.forM fun p => updateVarAssignment p.fvarId .top
|
||||
decl.value.forCodeM interpCode
|
||||
match decl.value with
|
||||
| .code code .. => interpCode code
|
||||
| .extern .. => updateCurrFnSummary .top
|
||||
let newVal ← getFunVal idx
|
||||
if currentVal != newVal then
|
||||
return true
|
||||
|
||||
@@ -149,8 +149,10 @@ def Decl.reduceArity (decl : Decl) : CompilerM (Array Decl) := do
|
||||
match decl.value with
|
||||
| .code code =>
|
||||
let used ← collectUsedParams decl
|
||||
if used.size == decl.params.size then
|
||||
return #[decl] -- Declarations uses all parameters
|
||||
if used.size == decl.params.size || used.size == 0 then
|
||||
-- Do nothing if all params were used, or if no params were used. In the latter case,
|
||||
-- this would promote the decl to a constant, which could execute unreachable code.
|
||||
return #[decl]
|
||||
else
|
||||
trace[Compiler.reduceArity] "{decl.name}, used params: {used.toList.map mkFVar}"
|
||||
let mask := decl.params.map fun param => used.contains param.fvarId
|
||||
|
||||
@@ -535,7 +535,9 @@ opaque compileDeclsOld (env : Environment) (opt : @& Options) (decls : @& List N
|
||||
-- `ref?` is used for error reporting if available
|
||||
partial def compileDecls (decls : List Name) (ref? : Option Declaration := none)
|
||||
(logErrors := true) : CoreM Unit := do
|
||||
if !Elab.async.get (← getOptions) then
|
||||
-- When inside `realizeConst`, do compilation synchronously so that `_cstage*` constants are found
|
||||
-- by the replay code
|
||||
if !Elab.async.get (← getOptions) || (← getEnv).isRealizing then
|
||||
doCompile
|
||||
return
|
||||
let env ← getEnv
|
||||
|
||||
@@ -190,6 +190,26 @@ where
|
||||
return (x, toExpr <| UInt64.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for UInt64 was not 64 bit but {value.w} bit"
|
||||
| Int8.toBitVec x =>
|
||||
if h : value.w = 8 then
|
||||
return (x, toExpr <| Int8.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int8 was not 8 bit but {value.w} bit"
|
||||
| Int16.toBitVec x =>
|
||||
if h : value.w = 16 then
|
||||
return (x, toExpr <| Int16.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int16 was not 16 bit but {value.w} bit"
|
||||
| Int32.toBitVec x =>
|
||||
if h : value.w = 32 then
|
||||
return (x, toExpr <| Int32.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int32 was not 32 bit but {value.w} bit"
|
||||
| Int64.toBitVec x =>
|
||||
if h : value.w = 64 then
|
||||
return (x, toExpr <| Int64.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int64 was not 64 bit but {value.w} bit"
|
||||
| _ =>
|
||||
match var with
|
||||
| .app (.const (.str p s) []) arg =>
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Attr
|
||||
import Lean.Elab.Tactic.Simp
|
||||
@@ -14,7 +15,7 @@ This module contains the implementation of the pre processing pass for reducing
|
||||
|
||||
It:
|
||||
1. runs the `int_toBitVec` simp set
|
||||
2. If `USize.toBitVec` is used anywhere looks for equations of the form
|
||||
2. If `USize.toBitVec`/`ISize.toBitVec` is used anywhere looks for equations of the form
|
||||
`System.Platform.numBits = constant` (or flipped) and uses them to convert the system back to
|
||||
fixed width.
|
||||
-/
|
||||
@@ -25,11 +26,12 @@ namespace Frontend.Normalize
|
||||
open Lean.Meta
|
||||
|
||||
/--
|
||||
Contains information for the `USize` elimination pass.
|
||||
Contains information for the `USize`/`ISize` elimination pass.
|
||||
-/
|
||||
structure USizeState where
|
||||
structure SizeState where
|
||||
/--
|
||||
Contains terms of the form `USize.toBitVec e` that we will translate to constant width `BitVec`.
|
||||
Contains terms of the form `USize.toBitVec e` and `ISize.toBitVec e` that we will translate to
|
||||
constant width `BitVec`.
|
||||
-/
|
||||
relevantTerms : Std.HashSet Expr := {}
|
||||
/--
|
||||
@@ -37,16 +39,16 @@ structure USizeState where
|
||||
-/
|
||||
relevantHyps : Std.HashSet FVarId := {}
|
||||
|
||||
private abbrev M := StateRefT USizeState MetaM
|
||||
private abbrev M := StateRefT SizeState MetaM
|
||||
|
||||
namespace M
|
||||
|
||||
@[inline]
|
||||
def addUSizeTerm (e : Expr) : M Unit := do
|
||||
def addSizeTerm (e : Expr) : M Unit := do
|
||||
modify fun s => { s with relevantTerms := s.relevantTerms.insert e }
|
||||
|
||||
@[inline]
|
||||
def addUSizeHyp (f : FVarId) : M Unit := do
|
||||
def addSizeHyp (f : FVarId) : M Unit := do
|
||||
modify fun s => { s with relevantHyps := s.relevantHyps.insert f }
|
||||
|
||||
end M
|
||||
@@ -64,30 +66,30 @@ def intToBitVecPass : Pass where
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := hyps)
|
||||
let some (_, goal) := result? | return none
|
||||
handleUSize goal |>.run' {}
|
||||
handleSize goal |>.run' {}
|
||||
where
|
||||
handleUSize (goal : MVarId) : M MVarId := do
|
||||
if ← detectUSize goal then
|
||||
replaceUSize goal
|
||||
handleSize (goal : MVarId) : M MVarId := do
|
||||
if ← detectSize goal then
|
||||
replaceSize goal
|
||||
else
|
||||
return goal
|
||||
|
||||
detectUSize (goal : MVarId) : M Bool := do
|
||||
detectSize (goal : MVarId) : M Bool := do
|
||||
goal.withContext do
|
||||
for hyp in ← getPropHyps do
|
||||
(← hyp.getType).forEachWhere
|
||||
(stopWhenVisited := true)
|
||||
(·.isAppOfArity ``USize.toBitVec 1)
|
||||
(fun e => e.isAppOfArity ``USize.toBitVec 1 || e.isAppOfArity ``ISize.toBitVec 1)
|
||||
fun e => do
|
||||
M.addUSizeTerm e
|
||||
M.addUSizeHyp hyp
|
||||
M.addSizeTerm e
|
||||
M.addSizeHyp hyp
|
||||
|
||||
return !(← get).relevantTerms.isEmpty
|
||||
|
||||
/--
|
||||
Turn `goal` into a goal containing `BitVec const` instead of `USize`.
|
||||
Turn `goal` into a goal containing `BitVec const` instead of `USize`/`ISize`.
|
||||
-/
|
||||
replaceUSize (goal : MVarId) : M MVarId := do
|
||||
replaceSize (goal : MVarId) : M MVarId := do
|
||||
if let some (numBits, numBitsEq) ← findNumBitsEq goal then
|
||||
goal.withContext do
|
||||
let relevantHyps := (← get).relevantHyps.toArray.map mkFVar
|
||||
@@ -138,13 +140,14 @@ where
|
||||
numBitsEq
|
||||
(mkMVar newGoal)
|
||||
goal.assign <| mkAppN casesOn (relevantTerms ++ abstractedHyps)
|
||||
-- remove all of the hold hypotheses about USize.toBitVec to prevent false counter examples
|
||||
-- remove all of the hold hypotheses about USize.toBitVec/ISize.toBitVec to prevent
|
||||
-- false counter examples
|
||||
(newGoal, _) ← newGoal.tryClearMany' (abstractedHyps.map Expr.fvarId!)
|
||||
-- intro both the new `BitVec const` as well as all hypotheses about them
|
||||
(_, newGoal) ← newGoal.introN (relevantTerms.size + abstractedHyps.size)
|
||||
return newGoal
|
||||
else
|
||||
logWarning m!"Detected USize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
|
||||
logWarning m!"Detected USize/ISize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
|
||||
return goal
|
||||
|
||||
/--
|
||||
|
||||
@@ -15,7 +15,7 @@ structures containing information about supported types into individual parts re
|
||||
|
||||
The implementation runs cases recursively on all "interesting" types where a type is interesting if
|
||||
it is a non recursive structure and at least one of the following conditions hold:
|
||||
- it contains something of type `BitVec`/`UIntX`/`Bool`
|
||||
- it contains something of type `BitVec`/`UIntX`/`IntX`/`Bool`
|
||||
- it is parametrized by an interesting type
|
||||
- it contains another interesting type
|
||||
Afterwards we also:
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
|
||||
|
||||
/-!
|
||||
@@ -64,6 +65,11 @@ where
|
||||
| UInt32 => return true
|
||||
| UInt64 => return true
|
||||
| USize => return true
|
||||
| Int8 => return true
|
||||
| Int16 => return true
|
||||
| Int32 => return true
|
||||
| Int64 => return true
|
||||
| ISize => return true
|
||||
| Bool => return true
|
||||
| _ =>
|
||||
let some const := expr.getAppFn.constName? | return false
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.RBMap
|
||||
import Init.Data.Nat.Fold
|
||||
import Std.Tactic.BVDecide.LRAT.Actions
|
||||
import Std.Data.HashMap
|
||||
|
||||
@@ -17,7 +17,6 @@ This module implements the LRAT trimming algorithm described in section 4 of
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace LRAT
|
||||
|
||||
open Lean (RBMap)
|
||||
open Std.Tactic.BVDecide.LRAT (IntAction)
|
||||
|
||||
namespace trim
|
||||
@@ -41,16 +40,18 @@ structure Context where
|
||||
|
||||
structure State where
|
||||
/--
|
||||
The set of used proof step ids.
|
||||
For each proof step `i` contains at index `i - initialId` `0` if `i` is unused, `1` if it is
|
||||
used.
|
||||
-/
|
||||
used : RBMap Nat Unit compare := {}
|
||||
used : ByteArray
|
||||
/--
|
||||
A mapping from old proof step ids to new ones. Used such that the proof remains a sequence without
|
||||
For each proof step `i` contains at index `i - initialId` the step that `i` maps to in the new
|
||||
proof or `0` if that step is not yet set. Used such that the proof remains a sequence without
|
||||
gaps.
|
||||
-/
|
||||
mapped : Std.HashMap Nat Nat := {}
|
||||
mapped : Array Nat
|
||||
|
||||
abbrev M : Type → Type := ReaderT Context <| ExceptT String <| StateM State
|
||||
abbrev M : Type → Type := ReaderT Context <| StateM State
|
||||
|
||||
namespace M
|
||||
|
||||
@@ -78,7 +79,9 @@ def run (proof : Array IntAction) (x : M α) : Except String α := do
|
||||
| .addEmpty id .. | .addRup id .. | .addRat id .. => acc.insert id a
|
||||
| .del .. => acc
|
||||
let proof := proof.foldl (init := {}) folder
|
||||
ReaderT.run x { proof, initialId, addEmptyId } |>.run |>.run' {}
|
||||
let used := Nat.fold proof.size (init := ByteArray.mkEmpty proof.size) (fun _ _ acc => acc.push 0)
|
||||
let mapped := Array.mkArray proof.size 0
|
||||
return ReaderT.run x { proof, initialId, addEmptyId } |>.run' { used, mapped }
|
||||
|
||||
@[inline]
|
||||
def getInitialId : M Nat := do
|
||||
@@ -90,6 +93,10 @@ def getEmptyId : M Nat := do
|
||||
let ctx ← read
|
||||
return ctx.addEmptyId
|
||||
|
||||
@[inline]
|
||||
private def idIndex (id : Nat) : M Nat := do
|
||||
return id - (← M.getInitialId)
|
||||
|
||||
@[inline]
|
||||
def getProofStep (id : Nat) : M (Option IntAction) := do
|
||||
let ctx ← read
|
||||
@@ -98,22 +105,20 @@ def getProofStep (id : Nat) : M (Option IntAction) := do
|
||||
@[inline]
|
||||
def isUsed (id : Nat) : M Bool := do
|
||||
let s ← get
|
||||
return s.used.contains id
|
||||
return s.used[← idIndex id]! == 1
|
||||
|
||||
@[inline]
|
||||
def markUsed (id : Nat) : M Unit := do
|
||||
-- If we are referring to a proof step that is not part of the proof, it is part of the CNF.
|
||||
-- We do not trim the CNF so just forget about the fact that this step was used.
|
||||
if (← getProofStep id).isSome then
|
||||
modify (fun s => { s with used := s.used.insert id () })
|
||||
if id >= (← M.getInitialId) then
|
||||
let idx ← idIndex id
|
||||
modify (fun s => { s with used := s.used.set! idx 1 })
|
||||
|
||||
@[inline]
|
||||
def getUsedSet : M (RBMap Nat Unit Ord.compare) := do
|
||||
let s ← get
|
||||
return s.used
|
||||
|
||||
def registerIdMap (oldId : Nat) (newId : Nat) : M Unit := do
|
||||
modify (fun s => { s with mapped := s.mapped.insert oldId newId })
|
||||
let idx ← idIndex oldId
|
||||
modify (fun s => { s with mapped := s.mapped.set! idx newId })
|
||||
|
||||
def mapStep (step : IntAction) : M IntAction := do
|
||||
match step with
|
||||
@@ -139,8 +144,12 @@ def mapStep (step : IntAction) : M IntAction := do
|
||||
where
|
||||
@[inline]
|
||||
mapIdent (ident : Nat) : M Nat := do
|
||||
let s ← get
|
||||
return s.mapped[ident]? |>.getD ident
|
||||
if ident < (← getInitialId) then
|
||||
return ident
|
||||
else
|
||||
let s ← get
|
||||
let newId := s.mapped[← idIndex ident]!
|
||||
return newId
|
||||
|
||||
end M
|
||||
|
||||
@@ -150,14 +159,17 @@ up with DFS.
|
||||
-/
|
||||
partial def useAnalysis : M Unit := do
|
||||
let emptyId ← M.getEmptyId
|
||||
go [emptyId]
|
||||
go #[emptyId]
|
||||
where
|
||||
go (workList : List Nat) : M Unit := do
|
||||
match workList with
|
||||
| [] => return ()
|
||||
| id :: workList =>
|
||||
go (worklist : Array Nat) : M Unit := do
|
||||
let mut worklist := worklist
|
||||
if h : worklist.size = 0 then
|
||||
return ()
|
||||
else
|
||||
let id := worklist.back
|
||||
worklist := worklist.pop
|
||||
if ← M.isUsed id then
|
||||
go workList
|
||||
go worklist
|
||||
else
|
||||
M.markUsed id
|
||||
let step? ← M.getProofStep id
|
||||
@@ -165,36 +177,37 @@ where
|
||||
| some step =>
|
||||
match step with
|
||||
| .addEmpty _ hints =>
|
||||
let workList := hints.toList ++ workList
|
||||
go workList
|
||||
worklist := worklist ++ hints
|
||||
go worklist
|
||||
| .addRup _ _ hints =>
|
||||
let workList := hints.toList ++ workList
|
||||
go workList
|
||||
worklist := worklist ++ hints
|
||||
go worklist
|
||||
| .addRat _ _ _ rupHints ratHints =>
|
||||
let folder acc a :=
|
||||
a.fst :: a.snd.toList ++ acc
|
||||
let ratHints := ratHints.foldl (init := []) folder
|
||||
let workList := rupHints.toList ++ ratHints ++ workList
|
||||
go workList
|
||||
| .del .. => go workList
|
||||
| none => go workList
|
||||
let folder acc a := acc.push a.fst ++ a.snd
|
||||
let ratHints := ratHints.foldl (init := Array.mkEmpty ratHints.size) folder
|
||||
worklist := worklist ++ ratHints ++ rupHints
|
||||
go worklist
|
||||
| .del .. => go worklist
|
||||
| none => go worklist
|
||||
|
||||
/--
|
||||
Map the set of used proof steps to a new LRAT proof that has no holes in the sequence of proof
|
||||
identifiers.
|
||||
-/
|
||||
def mapping : M (Array IntAction) := do
|
||||
let used ← M.getUsedSet
|
||||
let mut nextMapped ← M.getInitialId
|
||||
let mut newProof := Array.mkEmpty used.size
|
||||
for (id, _) in used do
|
||||
M.registerIdMap id nextMapped
|
||||
-- This should never panic as the use def analysis has already marked this step as being used
|
||||
-- so it must exist.
|
||||
let step := (← M.getProofStep id).get!
|
||||
let newStep ← M.mapStep step
|
||||
newProof := newProof.push newStep
|
||||
nextMapped := nextMapped + 1
|
||||
let emptyId ← M.getEmptyId
|
||||
let initialId ← M.getInitialId
|
||||
let mut nextMapped := initialId
|
||||
let mut newProof := #[]
|
||||
for id in [initialId:emptyId+1] do
|
||||
if ← M.isUsed id then
|
||||
M.registerIdMap id nextMapped
|
||||
-- This should never panic as the use def analysis has already marked this step as being used
|
||||
-- so it must exist.
|
||||
let step := (← M.getProofStep id).get!
|
||||
let newStep ← M.mapStep step
|
||||
newProof := newProof.push newStep
|
||||
nextMapped := nextMapped + 1
|
||||
return newProof
|
||||
|
||||
def go : M (Array IntAction) := do
|
||||
@@ -207,7 +220,7 @@ end trim
|
||||
Trim the LRAT `proof` by removing all steps that are not used in reaching the empty clause
|
||||
conclusion.
|
||||
-/
|
||||
def trim (proof : Array IntAction) : Except String (Array IntAction) :=
|
||||
def trim (proof : Array IntAction) : Except String (Array IntAction) := do
|
||||
trim.go.run proof
|
||||
|
||||
end LRAT
|
||||
|
||||
@@ -439,11 +439,14 @@ private structure AsyncConsts where
|
||||
deriving Inhabited
|
||||
|
||||
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
|
||||
{ aconsts with
|
||||
let normalizedName := privateToUserName aconst.constInfo.name
|
||||
if let some aconst' := aconsts.normalizedTrie.find? normalizedName then
|
||||
panic! s!"AsyncConsts.add: duplicate normalized declaration name {aconst.constInfo.name} vs. {aconst'.constInfo.name}"
|
||||
else { aconsts with
|
||||
size := aconsts.size + 1
|
||||
revList := aconst :: aconsts.revList
|
||||
map := aconsts.map.insert aconst.constInfo.name aconst
|
||||
normalizedTrie := aconsts.normalizedTrie.insert (privateToUserName aconst.constInfo.name) aconst
|
||||
normalizedTrie := aconsts.normalizedTrie.insert normalizedName aconst
|
||||
}
|
||||
|
||||
private def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
@@ -451,8 +454,9 @@ private def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option
|
||||
|
||||
/-- Finds the constant in the collection that is a prefix of `declName`, if any. -/
|
||||
private def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
-- as macro scopes are a strict suffix,
|
||||
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName.eraseMacroScopes)
|
||||
-- as macro scopes are a strict suffix, we do not have to remove them before calling
|
||||
-- `findLongestPrefix?`
|
||||
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName)
|
||||
|
||||
/-- Context for `realizeConst` established by `enableRealizationsForConst`. -/
|
||||
private structure RealizationContext where
|
||||
@@ -504,8 +508,8 @@ structure Environment where
|
||||
/-- Information about this asynchronous branch of the environment, if any. -/
|
||||
private asyncCtx? : Option AsyncContext := none
|
||||
/--
|
||||
Realized constants belonging to imported declarations. `none` only from `Environment.ofKernelEnv`,
|
||||
which should never leak into general elaboration.
|
||||
Realized constants belonging to imported declarations. Must be initialized by calling
|
||||
`enableRealizationsForImports`.
|
||||
-/
|
||||
private realizedImportedConsts? : Option RealizationContext
|
||||
/--
|
||||
@@ -644,6 +648,21 @@ def findConstVal? (env : Environment) (n : Name) : Option ConstantVal := do
|
||||
return asyncConst.constInfo.toConstantVal
|
||||
else env.findNoAsync n |>.map (·.toConstantVal)
|
||||
|
||||
/--
|
||||
Allows `realizeConst` calls for imported declarations in all derived environment branches.
|
||||
Realizations will run using the given environment and options to ensure deterministic results.
|
||||
This function should be called directly after `setMainModule` to ensure that all realized constants
|
||||
use consistent private prefixes.
|
||||
-/
|
||||
def enableRealizationsForImports (env : Environment) (opts : Options) : BaseIO Environment :=
|
||||
return { env with realizedImportedConsts? := some {
|
||||
-- safety: `RealizationContext` is private
|
||||
env := unsafe unsafeCast env
|
||||
opts
|
||||
constsRef := (← IO.mkRef {})
|
||||
}
|
||||
}
|
||||
|
||||
/--
|
||||
Allows `realizeConst` calls for the given declaration in all derived environment branches.
|
||||
Realizations will run using the given environment and options to ensure deterministic results. Note
|
||||
@@ -893,7 +912,10 @@ def imports (env : Environment) : Array Import :=
|
||||
def allImportedModuleNames (env : Environment) : Array Name :=
|
||||
env.header.moduleNames
|
||||
|
||||
def setMainModule (env : Environment) (m : Name) : Environment :=
|
||||
def setMainModule (env : Environment) (m : Name) : Environment := Id.run do
|
||||
if env.realizedImportedConsts?.isSome then
|
||||
panic! "Environment.setMainModule: cannot set after `enableRealizationsForImports`"
|
||||
return env
|
||||
env.modifyCheckedAsync ({ · with header.mainModule := m })
|
||||
|
||||
def mainModule (env : Environment) : Name :=
|
||||
@@ -1078,9 +1100,6 @@ def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ
|
||||
{if asyncCtx.realizing then "realization" else "async"} context '{asyncCtx.declPrefix}'"
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| .local =>
|
||||
if let some asyncCtx := env.asyncCtx?.filter (·.realizing) then
|
||||
panic! s!"Environment.modifyState: environment extension is marked as `local` but used in \
|
||||
realization context '{asyncCtx.declPrefix}'"
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| _ =>
|
||||
if ext.replay?.isNone then
|
||||
@@ -1692,14 +1711,6 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
Safety: There are no concurrent accesses to `env` at this point. -/
|
||||
env ← unsafe Runtime.markPersistent env
|
||||
env ← finalizePersistentExtensions env s.moduleData opts
|
||||
env := { env with
|
||||
realizedImportedConsts? := some {
|
||||
-- safety: `RealizationContext` is private
|
||||
env := unsafe unsafeCast env
|
||||
opts
|
||||
constsRef := (← IO.mkRef {})
|
||||
}
|
||||
}
|
||||
if leakEnv then
|
||||
/- Ensure the final environment including environment extension states is
|
||||
marked persistent as documented.
|
||||
@@ -1870,6 +1881,7 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
|
||||
-- allow realizations to recursively realize other constants for `forConst`. Do note that
|
||||
-- this allows for recursive realization of `constName` itself, which will deadlock.
|
||||
realizedLocalConsts := realizeEnv.realizedLocalConsts.insert forConst ctx
|
||||
realizedImportedConsts? := env.realizedImportedConsts?
|
||||
}
|
||||
-- ensure realized constants are nested below `forConst` and that environment extension
|
||||
-- modifications know they are in an async context
|
||||
@@ -1882,7 +1894,8 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
|
||||
|
||||
-- find new constants incl. nested realizations, add current extension state, and compute
|
||||
-- closure
|
||||
let consts := realizeEnv'.asyncConsts.revList.take (realizeEnv'.asyncConsts.size - realizeEnv.asyncConsts.size)
|
||||
let numNewConsts := realizeEnv'.asyncConsts.size - realizeEnv.asyncConsts.size
|
||||
let consts := realizeEnv'.asyncConsts.revList.take numNewConsts |>.reverse
|
||||
let consts := consts.map fun c =>
|
||||
if c.exts?.isNone then
|
||||
{ c with exts? := some <| .pure realizeEnv'.checkedWithoutAsync.extensions }
|
||||
@@ -1892,7 +1905,11 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
|
||||
prom.resolve (consts, replay, dyn)
|
||||
pure (consts, replay, dyn)
|
||||
return ({ env with
|
||||
asyncConsts := consts.foldl (·.add) env.asyncConsts
|
||||
asyncConsts := consts.foldl (init := env.asyncConsts) fun consts c =>
|
||||
if consts.find? c.constInfo.name |>.isSome then
|
||||
consts
|
||||
else
|
||||
consts.add c
|
||||
checked := env.checked.map replay
|
||||
}, dyn)
|
||||
where
|
||||
|
||||
@@ -425,6 +425,7 @@ where
|
||||
return { diagnostics, result? := none }
|
||||
|
||||
let headerEnv := headerEnv.setMainModule setup.mainModuleName
|
||||
let headerEnv ← headerEnv.enableRealizationsForImports setup.opts
|
||||
let mut traceState := default
|
||||
if trace.profiler.output.get? setup.opts |>.isSome then
|
||||
traceState := {
|
||||
|
||||
@@ -2215,16 +2215,24 @@ private partial def setAllDiagRanges (snap : Language.SnapshotTree) (pos endPos
|
||||
task := (← BaseIO.mapTask (t := task.task) (setAllDiagRanges · pos endPos)) })
|
||||
}
|
||||
|
||||
open Language
|
||||
|
||||
private structure RealizeConstantResult where
|
||||
snap : SnapshotTree
|
||||
error? : Option Exception
|
||||
deriving TypeName
|
||||
|
||||
/--
|
||||
Makes the helper constant `constName` that is derived from `forConst` available in the environment.
|
||||
`enableRealizationsForConst forConst` must have been called first on this environment branch. If
|
||||
this is the first environment branch requesting `constName` to be realized (atomically), `realize`
|
||||
is called with the environment and options at the time of calling `enableRealizationsForConst` if
|
||||
`forConst` is from the current module and the state just after importing otherwise, thus helping
|
||||
achieve deterministic results despite the non-deterministic choice of which thread is tasked with
|
||||
realization. In other words, the state after calling `realizeConst` is *as if* `realize` had been
|
||||
called immediately after `enableRealizationsForConst forConst`, though the effects of this call are
|
||||
visible only after calling `realizeConst`. See below for more details on the replayed effects.
|
||||
`forConst` is from the current module and the state just after importing (when
|
||||
`enableRealizationsForImports` should be called) otherwise, thus helping achieve deterministic
|
||||
results despite the non-deterministic choice of which thread is tasked with realization. In other
|
||||
words, the state after calling `realizeConst` is *as if* `realize` had been called immediately after
|
||||
`enableRealizationsForConst forConst`, though the effects of this call are visible only after
|
||||
calling `realizeConst`. See below for more details on the replayed effects.
|
||||
|
||||
`realizeConst` cannot check what other data is captured in the `realize` closure,
|
||||
so it is best practice to extract it into a separate function and pay close attention to the passed
|
||||
@@ -2241,20 +2249,25 @@ to add `constName` to the environment, an appropriate diagnostic is reported to
|
||||
constants are added to the environment.
|
||||
-/
|
||||
def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
|
||||
MetaM Unit := withTraceNode `Meta.realizeConst (fun _ => return constName) do
|
||||
MetaM Unit := do
|
||||
let env ← getEnv
|
||||
let coreCtx ← readThe Core.Context
|
||||
-- these fields should be invariant throughout the file
|
||||
let coreCtx := { fileName := coreCtx.fileName, fileMap := coreCtx.fileMap }
|
||||
let (env, dyn) ← env.realizeConst forConst constName (realizeAndReport coreCtx)
|
||||
if let some snap := dyn.get? Language.SnapshotTree then
|
||||
let mut snap := snap
|
||||
-- localize diagnostics
|
||||
if let some range := (← getRef).getRange? then
|
||||
let fileMap ← getFileMap
|
||||
snap ← setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
|
||||
Core.logSnapshotTask <| .finished (stx? := none) snap
|
||||
setEnv env
|
||||
if env.contains constName then
|
||||
return
|
||||
withTraceNode `Meta.realizeConst (fun _ => return constName) do
|
||||
let coreCtx ← readThe Core.Context
|
||||
-- these fields should be invariant throughout the file
|
||||
let coreCtx := { fileName := coreCtx.fileName, fileMap := coreCtx.fileMap }
|
||||
let (env, dyn) ← env.realizeConst forConst constName (realizeAndReport coreCtx)
|
||||
if let some res := dyn.get? RealizeConstantResult then
|
||||
let mut snap := res.snap
|
||||
-- localize diagnostics
|
||||
if let some range := (← getRef).getRange? then
|
||||
let fileMap ← getFileMap
|
||||
snap ← setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
|
||||
Core.logSnapshotTask <| .finished (stx? := none) snap
|
||||
if let some e := res.error? then
|
||||
throw e
|
||||
setEnv env
|
||||
where
|
||||
-- similar to `wrapAsyncAsSnapshot` but not sufficiently so to share code
|
||||
realizeAndReport (coreCtx : Core.Context) env opts := do
|
||||
@@ -2267,14 +2280,20 @@ where
|
||||
realize
|
||||
if !(← getEnv).contains constName then
|
||||
throwError "Lean.Meta.realizeConst: {constName} was not added to the environment"
|
||||
catch e : Exception =>
|
||||
logError e.toMessageData
|
||||
finally
|
||||
addTraceAsMessages
|
||||
let res? ← act |>.run' |>.run coreCtx { env } |>.toBaseIO
|
||||
match res? with
|
||||
| .ok ((output, ()), st) => pure (st.env, .mk (← Core.mkSnapshot output coreCtx st))
|
||||
| .error _e => unreachable!; pure (env, .mk ({ diagnostics := .empty : Language.SnapshotLeaf}))
|
||||
| .ok ((output, ()), st) => pure (st.env, .mk {
|
||||
snap := (← Core.mkSnapshot output coreCtx st)
|
||||
error? := none
|
||||
: RealizeConstantResult
|
||||
})
|
||||
| .error e => pure (env, .mk {
|
||||
snap := toSnapshotTree { diagnostics := .empty : Language.SnapshotLeaf}
|
||||
error? := some e
|
||||
: RealizeConstantResult
|
||||
})
|
||||
|
||||
end Meta
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Types
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
namespace Lean
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat
|
||||
builtin_initialize registerTraceClass `grind.cutsat.model
|
||||
builtin_initialize registerTraceClass `grind.cutsat.subst
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq.unsat (inherited := true)
|
||||
@@ -43,7 +46,12 @@ builtin_initialize registerTraceClass `grind.cutsat.le.upper (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assign
|
||||
builtin_initialize registerTraceClass `grind.cutsat.conflict
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq.trivial (inherited := true)
|
||||
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq.split
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.backtrack
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -17,9 +17,6 @@ private def _root_.Int.Linear.Poly.substVar (p : Poly) : GoalM (Option (Var × E
|
||||
let p := p.mul (-b) |>.combine (c.p.mul a)
|
||||
return some (x, c, p)
|
||||
|
||||
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
|
||||
def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
|
||||
let c ← if c.p.isSorted then
|
||||
pure c
|
||||
@@ -52,8 +49,27 @@ partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIn
|
||||
let c ← mkDiseqCnstr p (.subst x c₁ c)
|
||||
applySubsts c
|
||||
|
||||
/--
|
||||
Given a disequality `c`, tries to find an inequality to be refined using
|
||||
`p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
|
||||
-/
|
||||
private def DiseqCnstr.findLe (c : DiseqCnstr) : GoalM Bool := do
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
let s ← get'
|
||||
let go (atLower : Bool) : GoalM Bool := do
|
||||
let cs' := if atLower then s.lowers[x]! else s.uppers[x]!
|
||||
for c' in cs' do
|
||||
if c.p == c'.p || c.p.isNegEq c'.p then
|
||||
c'.erase
|
||||
let le ← mkLeCnstr (c'.p.addConst 1) (.ofLeDiseq c' c)
|
||||
le.assert
|
||||
return true
|
||||
return false
|
||||
go true <||> go false
|
||||
|
||||
def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm
|
||||
let c ← c.applySubsts
|
||||
if c.p.isUnsatDiseq then
|
||||
@@ -62,8 +78,16 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.diseq.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
let c ← if k == 1 then
|
||||
pure c
|
||||
else
|
||||
mkDiseqCnstr (c.p.div k) (.divCoeffs c)
|
||||
if (← c.findLe) then
|
||||
return ()
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
c.p.updateOccs
|
||||
trace[grind.cutsat.diseq] "{← c.pp}"
|
||||
modify' fun s => { s with diseqs := s.diseqs.modify x (·.push c) }
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
@@ -173,7 +197,8 @@ private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
|
||||
for y in ys do
|
||||
updateOccsAt k x c y
|
||||
|
||||
def EqCnstr.assert (c : EqCnstr) : GoalM Unit := do
|
||||
@[export lean_grind_cutsat_assert_eq]
|
||||
def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm
|
||||
|
||||
@@ -45,6 +45,57 @@ partial def LeCnstr.applySubsts (c : LeCnstr) : GoalM LeCnstr := withIncRecDepth
|
||||
let c ← c.applyEq a x c₁ b
|
||||
applySubsts c
|
||||
|
||||
def _root_.Int.Linear.Poly.isNegEq (p₁ p₂ : Poly) : Bool :=
|
||||
match p₁, p₂ with
|
||||
| .num k₁, .num k₂ => k₁ == -k₂
|
||||
| .add a₁ x p₁, .add a₂ y p₂ => a₁ == -a₂ && x == y && isNegEq p₁ p₂
|
||||
| _, _ => false
|
||||
|
||||
def LeCnstr.erase (c : LeCnstr) : GoalM Unit := do
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
if a < 0 then
|
||||
modify' fun s => { s with lowers := s.lowers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
|
||||
else
|
||||
modify' fun s => { s with uppers := s.uppers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
|
||||
|
||||
/--
|
||||
Given a lower (upper) bound constraint `c`, tries to find
|
||||
an imply equality by searching a upper (lower) bound constraint `c'` such that
|
||||
`c.p == -c'.p`
|
||||
-/
|
||||
private def findEq (c : LeCnstr) : GoalM Bool := do
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
let s ← get'
|
||||
let cs' := if a < 0 then s.uppers[x]! else s.lowers[x]!
|
||||
for c' in cs' do
|
||||
if c.p.isNegEq c'.p then
|
||||
c'.erase
|
||||
let eq ← mkEqCnstr c.p (.ofLeGe c c')
|
||||
eq.assert
|
||||
return true
|
||||
return false
|
||||
|
||||
/--
|
||||
Applies `p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
|
||||
-/
|
||||
private def refineWithDiseq (c : LeCnstr) : GoalM LeCnstr := do
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
let mut c := c
|
||||
repeat
|
||||
let some c' ← refineWithDiseqStep? x c | return c
|
||||
c := c'
|
||||
return c
|
||||
where
|
||||
refineWithDiseqStep? (x : Var) (c : LeCnstr) : GoalM (Option LeCnstr) := do
|
||||
let s ← get'
|
||||
let cs' := s.diseqs[x]!
|
||||
for c' in cs' do
|
||||
if c.p == c'.p || c.p.isNegEq c'.p then
|
||||
-- Remove `c'`
|
||||
modify' fun s => { s with diseqs := s.diseqs.modify x fun cs' => cs'.filter fun c => c.p != c'.p }
|
||||
return some (← mkLeCnstr (c.p.addConst 1) (.ofLeDiseq c c'))
|
||||
return none
|
||||
|
||||
def LeCnstr.assert (c : LeCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
let c ← c.norm
|
||||
@@ -56,6 +107,9 @@ def LeCnstr.assert (c : LeCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.le.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
if (← findEq c) then
|
||||
return ()
|
||||
let c ← refineWithDiseq c
|
||||
if a < 0 then
|
||||
trace[grind.cutsat.le.lower] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
|
||||
99
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean
Normal file
99
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean
Normal file
@@ -0,0 +1,99 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Types
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
private def isIntENode (n : ENode) : MetaM Bool :=
|
||||
withDefault do isDefEq (← inferType n.self) Int.mkType
|
||||
|
||||
private def getCutsatAssignment? (goal : Goal) (node : ENode) : Option Rat := Id.run do
|
||||
let some e := node.cutsat? | return none
|
||||
let some x := goal.arith.cutsat.varMap.find? { expr := e } | return none
|
||||
if h : x < goal.arith.cutsat.assignment.size then
|
||||
return goal.arith.cutsat.assignment[x]
|
||||
else
|
||||
return none
|
||||
|
||||
private partial def satisfyDiseqs (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (v : Int) : Bool := Id.run do
|
||||
let some parents := goal.parents.find? { expr := e } | return true
|
||||
for parent in parents do
|
||||
let_expr Eq _ lhs rhs := parent | continue
|
||||
let some root := goal.getRoot? parent | continue
|
||||
if root.isConstOf ``False then
|
||||
let some lhsRoot := goal.getRoot? lhs | continue
|
||||
let some rhsRoot := goal.getRoot? rhs | continue
|
||||
if lhsRoot == e && !checkDiseq rhsRoot then return false
|
||||
if rhsRoot == e && !checkDiseq lhsRoot then return false
|
||||
return true
|
||||
where
|
||||
checkDiseq (other : Expr) : Bool :=
|
||||
if let some v' := a[other]? then
|
||||
v' != v
|
||||
else
|
||||
true
|
||||
|
||||
private partial def pickUnusedValue (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (next : Int) (alreadyUsed : Std.HashSet Int) : Int :=
|
||||
go next
|
||||
where
|
||||
go (next : Int) : Int :=
|
||||
if alreadyUsed.contains next then
|
||||
go (next+1)
|
||||
else if satisfyDiseqs goal a e next then
|
||||
next
|
||||
else
|
||||
go (next + 1)
|
||||
|
||||
private def assignEqc (goal : Goal) (e : Expr) (v : Rat) (a : Std.HashMap Expr Rat) : Std.HashMap Expr Rat := Id.run do
|
||||
let mut a := a
|
||||
for e in goal.getEqc e do
|
||||
a := a.insert e v
|
||||
return a
|
||||
|
||||
private def isInterpretedTerm (e : Expr) : Bool :=
|
||||
isIntNum e || e.isAppOf ``HAdd.hAdd || e.isAppOf ``HMul.hMul || e.isAppOf ``HSub.hSub
|
||||
|| e.isAppOf ``Neg.neg -- TODO add missing ones
|
||||
|
||||
/--
|
||||
Construct a model that statisfies all constraints in the cutsat model.
|
||||
It also assigns values to integer terms that have not been internalized by the
|
||||
cutsat model.
|
||||
|
||||
Remark: it uses rational numbers because cutsat may have failed to build an
|
||||
integer model.
|
||||
-/
|
||||
def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
|
||||
let mut used : Std.HashSet Int := {}
|
||||
let mut nextVal : Int := 0
|
||||
let mut model := {}
|
||||
let nodes := goal.getENodes
|
||||
-- Assign on expressions associated with cutsat terms or interpreted terms
|
||||
for node in nodes do
|
||||
if isSameExpr node.root node.self then
|
||||
if (← isIntENode node) then
|
||||
if let some v := getCutsatAssignment? goal node then
|
||||
model := assignEqc goal node.self v model
|
||||
if v.den == 1 then used := used.insert v.num
|
||||
else if let some v ← getIntValue? node.self then
|
||||
model := assignEqc goal node.self v model
|
||||
used := used.insert v
|
||||
-- Assign the remaining ones with values not used by cutsat
|
||||
for node in nodes do
|
||||
if isSameExpr node.root node.self then
|
||||
if (← isIntENode node) then
|
||||
if (← getIntValue? node.self).isNone &&
|
||||
(getCutsatAssignment? goal node).isNone then
|
||||
let v := pickUnusedValue goal model node.self nextVal used
|
||||
model := assignEqc goal node.self v model
|
||||
used := used.insert v
|
||||
let mut r := #[]
|
||||
for (e, v) in model do
|
||||
unless isInterpretedTerm e do
|
||||
r := r.push (e, v)
|
||||
return r
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
@@ -13,6 +13,7 @@ private def DvdCnstr.get_d_a (c : DvdCnstr) : GoalM (Int × Int) := do
|
||||
let .add a _ _ := c.p | c.throwUnexpected
|
||||
return (d, a)
|
||||
|
||||
mutual
|
||||
partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
@@ -28,24 +29,11 @@ partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_eq_subst)
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
|
||||
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
return h
|
||||
| .core p₁ p₂ h =>
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_of_core) (← getContext) (toExpr p₁) (toExpr p₂) (toExpr c'.p) reflBoolTrue h
|
||||
| .norm c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.diseq_norm) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .divCoeffs c =>
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_coeff) (← getContext) (toExpr c.p) (toExpr c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
|
||||
| .subst x c₁ c₂ =>
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_diseq_subst)
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
| .ofLeGe c₁ c₂ =>
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_of_le_ge)
|
||||
(← getContext) (toExpr c₁.p) (toExpr c₂.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
|
||||
mutual
|
||||
partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
@@ -104,29 +92,131 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := c'.caching do
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue
|
||||
(← c₁.toExprProof) (← c₂.toExprProof)
|
||||
| .ofLeDiseq c₁ c₂ =>
|
||||
return mkApp7 (mkConst ``Int.Linear.le_of_le_diseq)
|
||||
(← getContext) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
| .ofDiseqSplit c₁ fvarId h _ =>
|
||||
let p₂ := c₁.p.addConst 1
|
||||
let hFalse ← h.toExprProofCore
|
||||
let hNot := mkLambda `h .default (mkIntLE (← p₂.denoteExpr') (mkIntLit 0)) (hFalse.abstract #[mkFVar fvarId])
|
||||
return mkApp7 (mkConst ``Int.Linear.diseq_split_resolve)
|
||||
(← getContext) (toExpr c₁.p) (toExpr p₂) (toExpr c'.p) reflBoolTrue (← c₁.toExprProof) hNot
|
||||
|
||||
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
return h
|
||||
| .core p₁ p₂ h =>
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_of_core) (← getContext) (toExpr p₁) (toExpr p₂) (toExpr c'.p) reflBoolTrue h
|
||||
| .norm c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.diseq_norm) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .divCoeffs c =>
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_coeff) (← getContext) (toExpr c.p) (toExpr c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
|
||||
| .neg c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.diseq_neg) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .subst x c₁ c₂ =>
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_diseq_subst)
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
|
||||
partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
|
||||
match h with
|
||||
| .le c =>
|
||||
trace[grind.cutsat.le.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .dvd c =>
|
||||
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
|
||||
return mkApp5 (mkConst ``Int.Linear.dvd_unsat) (← getContext) (toExpr c.d) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .eq c =>
|
||||
trace[grind.cutsat.eq.unsat] "{← c.pp}"
|
||||
if c.p.isUnsatEq then
|
||||
return mkApp4 (mkConst ``Int.Linear.eq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
else
|
||||
let k := c.p.gcdCoeffs'
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) (← getContext) (toExpr c.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
|
||||
| .diseq c =>
|
||||
trace[grind.cutsat.diseq.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.diseq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
|
||||
end
|
||||
|
||||
def setInconsistent (h : UnsatProof) : GoalM Unit := do
|
||||
let hf ← withProofContext do
|
||||
match h with
|
||||
| .le c =>
|
||||
trace[grind.cutsat.le.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .dvd c =>
|
||||
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
|
||||
return mkApp5 (mkConst ``Int.Linear.dvd_unsat) (← getContext) (toExpr c.d) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .eq c =>
|
||||
trace[grind.cutsat.eq.unsat] "{← c.pp}"
|
||||
if c.p.isUnsatEq then
|
||||
return mkApp4 (mkConst ``Int.Linear.eq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
else
|
||||
let k := c.p.gcdCoeffs'
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) (← getContext) (toExpr c.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
|
||||
| .diseq c =>
|
||||
trace[grind.cutsat.diseq.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.diseq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
def UnsatProof.toExprProof (h : UnsatProof) : GoalM Expr := do
|
||||
withProofContext do h.toExprProofCore
|
||||
|
||||
closeGoal hf
|
||||
def setInconsistent (h : UnsatProof) : GoalM Unit := do
|
||||
if (← get').caseSplits then
|
||||
-- Let the search procedure in `SearchM` resolve the conflict.
|
||||
modify' fun s => { s with conflict? := some h }
|
||||
else
|
||||
let h ← h.toExprProof
|
||||
closeGoal h
|
||||
|
||||
/-!
|
||||
A cutsat proof may depend on decision variables.
|
||||
We collect them and perform non chronological backtracking.
|
||||
-/
|
||||
|
||||
structure CollectDecVars.State where
|
||||
visited : Std.HashSet Nat := {}
|
||||
found : FVarIdSet := {}
|
||||
|
||||
abbrev CollectDecVarsM := ReaderT FVarIdSet (StateM CollectDecVars.State)
|
||||
|
||||
private def alreadyVisited (id : Nat) : CollectDecVarsM Bool := do
|
||||
if (← get).visited.contains id then return true
|
||||
modify fun s => { s with visited := s.visited.insert id }
|
||||
return false
|
||||
|
||||
private def markAsFound (fvarId : FVarId) : CollectDecVarsM Unit := do
|
||||
modify fun s => { s with found := s.found.insert fvarId }
|
||||
|
||||
private def collectExpr (e : Expr) : CollectDecVarsM Unit := do
|
||||
let .fvar fvarId := e | return ()
|
||||
if (← read).contains fvarId then
|
||||
markAsFound fvarId
|
||||
|
||||
mutual
|
||||
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .core .. => return () -- Equalities coming from the core never contain cutsat decision variables
|
||||
| .norm c | .divCoeffs c => c.collectDecVars
|
||||
| .subst _ c₁ c₂ | .ofLeGe c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
partial def DvdCnstr.collectDecVars (c' : DvdCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .norm c | .elim c | .divCoeffs c | .ofEq _ c => c.collectDecVars
|
||||
| .solveCombine c₁ c₂ | .solveElim c₁ c₂ | .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .notExpr .. => return () -- This kind of proof is used for connecting with the `grind` core.
|
||||
| .norm c | .divCoeffs c => c.collectDecVars
|
||||
| .combine c₁ c₂ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
| .ofDiseqSplit _ _ _ decVars =>
|
||||
-- Recall that we cache the decision variables used in this kind of proof
|
||||
for fvar in decVars do
|
||||
markAsFound fvar
|
||||
|
||||
partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .core .. => return () -- Disequalities coming from the core never contain cutsat decision variables
|
||||
| .norm c | .divCoeffs c | .neg c => c.collectDecVars
|
||||
| .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
end
|
||||
|
||||
def UnsatProof.collectDecVars (h : UnsatProof) : CollectDecVarsM Unit := do
|
||||
match h with
|
||||
| .le c | .dvd c | .eq c | .diseq c => c.collectDecVars
|
||||
|
||||
abbrev CollectDecVarsM.run (x : CollectDecVarsM Unit) (decVars : FVarIdSet) : FVarIdSet :=
|
||||
let (_, s) := x decVars |>.run {}
|
||||
s.found
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -8,16 +8,62 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
def getBestLower? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
private def checkIsNextVar (x : Var) : GoalM Unit := do
|
||||
if x != (← get').assignment.size then
|
||||
throwError "`grind` internal error, assigning variable out of order"
|
||||
|
||||
private def traceAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
|
||||
|
||||
private def setAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
checkIsNextVar x
|
||||
traceAssignment x v
|
||||
modify' fun s => { s with assignment := s.assignment.push v }
|
||||
|
||||
private def skipAssignment (x : Var) : GoalM Unit := do
|
||||
checkIsNextVar x
|
||||
modify' fun s => { s with assignment := s.assignment.push 0 }
|
||||
|
||||
/-- Assign eliminated variables using `elimEqs` field. -/
|
||||
private def assignElimVars : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
go (← get').elimStack
|
||||
where
|
||||
go (xs : List Var) : GoalM Unit := do
|
||||
match xs with
|
||||
| [] => return ()
|
||||
| x :: xs =>
|
||||
let some c := (← get').elimEqs[x]!
|
||||
| throwError "`grind` internal error, eliminated variable must have equation associated with it"
|
||||
-- `x` may not be the max variable
|
||||
let a := c.p.coeff x
|
||||
if a == 0 then c.throwUnexpected
|
||||
-- ensure `x` is 0 when evaluating `c.p`
|
||||
modify' fun s => { s with assignment := s.assignment.set x 0 }
|
||||
let some v ← c.p.eval? | c.throwUnexpected
|
||||
let v := (-v) / a
|
||||
traceAssignment x v
|
||||
modify' fun s => { s with assignment := s.assignment.set x v }
|
||||
go xs
|
||||
|
||||
/--
|
||||
Assuming all variables smaller than `x` have already been assigned,
|
||||
returns the best lower bound for `x` using the given partial assignment and
|
||||
inequality constraints where `x` is the maximal variable.
|
||||
-/
|
||||
def getBestLower? (x : Var) : GoalM (Option (Rat × LeCnstr)) := do
|
||||
let s ← get'
|
||||
let mut best? := none
|
||||
for c in s.lowers[x]! do
|
||||
let .add k _ p := c.p | c.throwUnexpected
|
||||
let some v ← p.eval? | c.throwUnexpected
|
||||
let lower' := Int.Linear.cdiv v (-k)
|
||||
let lower' := v / (-k)
|
||||
if let some (lower, _) := best? then
|
||||
if lower' > lower then
|
||||
best? := some (lower', c)
|
||||
@@ -25,7 +71,12 @@ def getBestLower? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
best? := some (lower', c)
|
||||
return best?
|
||||
|
||||
def getBestUpper? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
/--
|
||||
Assuming all variables smaller than `x` have already been assigned,
|
||||
returns the best upper bound for `x` using the given partial assignment and
|
||||
inequality constraints where `x` is the maximal variable.
|
||||
-/
|
||||
def getBestUpper? (x : Var) : GoalM (Option (Rat × LeCnstr)) := do
|
||||
let s ← get'
|
||||
let mut best? := none
|
||||
for c in s.uppers[x]! do
|
||||
@@ -39,10 +90,40 @@ def getBestUpper? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
best? := some (upper', c)
|
||||
return best?
|
||||
|
||||
def DvdCnstr.getSolutions? (c : DvdCnstr) : GoalM (Option (Int × Int)) := do
|
||||
/-- Returns values we cannot assign `x` because of disequality constraints. -/
|
||||
def getDiseqValues (x : Var) : SearchM (Array (Rat × DiseqCnstr)) := do
|
||||
let s ← get'
|
||||
let mut r := #[]
|
||||
for c in s.diseqs[x]! do
|
||||
let .add k _ p := c.p | c.throwUnexpected
|
||||
let some v ← p.eval? | c.throwUnexpected
|
||||
if (← isApprox) then
|
||||
r := r.push (((-v)/k), c)
|
||||
else
|
||||
-- We are building an integer model,
|
||||
-- if `k` does not divide `v`, we can just ignore the disequality.
|
||||
let v := v.num
|
||||
if v % k == 0 then
|
||||
r := r.push (v / k, c)
|
||||
return r
|
||||
|
||||
/--
|
||||
Solution space for a divisibility constraint of the form `d ∣ a*x + b`
|
||||
See `DvdCnstr.getSolutions?` to understand how it is computed.
|
||||
-/
|
||||
structure DvdSolution where
|
||||
d : Int := 1
|
||||
b : Int := 0
|
||||
|
||||
def DvdCnstr.getSolutions? (c : DvdCnstr) : SearchM (Option DvdSolution) := do
|
||||
let d := c.d
|
||||
let .add a _ p := c.p | c.throwUnexpected
|
||||
let some b ← p.eval? | c.throwUnexpected
|
||||
if b.den != 1 then
|
||||
-- `b` is a rational number, mark model as imprecise, and ignore the constraint
|
||||
setImprecise
|
||||
return none
|
||||
let b := b.num
|
||||
-- We must solve `d ∣ a*x + b`
|
||||
let g := d.gcd a
|
||||
if b % g != 0 then
|
||||
@@ -58,30 +139,7 @@ def DvdCnstr.getSolutions? (c : DvdCnstr) : GoalM (Option (Int × Int)) := do
|
||||
-- `a*x = -b (mod d)`
|
||||
-- `x = -b*a' (mod d)`
|
||||
-- `x = k*d + -b*a'` for any k
|
||||
return some (d, -b*a')
|
||||
|
||||
private partial def setAssignment (x : Var) (v : Int) : GoalM Unit := do
|
||||
if x == (← get').assignment.size then
|
||||
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
|
||||
modify' fun s => { s with assignment := s.assignment.push v }
|
||||
else if x > (← get').assignment.size then
|
||||
modify' fun s => { s with assignment := s.assignment.push 0 }
|
||||
setAssignment x v
|
||||
else
|
||||
throwError "`grind` internal error, variable is already assigned"
|
||||
|
||||
def resolveLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
|
||||
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
|
||||
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
|
||||
if (← p.satisfiedLe) == .false then
|
||||
-- If current assignment does not satisfy the real shadow, we use it even if it is not precise when
|
||||
-- `a₁.natAbs != 1 && a₂.natAbs != 1`
|
||||
(← mkLeCnstr p (.combine c₁ c₂)).assert
|
||||
else
|
||||
assert! a₁.natAbs != 1 && a₂.natAbs != 1
|
||||
throwError "NIY"
|
||||
return some { d, b := -b*a' }
|
||||
|
||||
def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.conflict] "{← c.pp}"
|
||||
@@ -89,72 +147,267 @@ def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
|
||||
let .add a _ p := c.p | c.throwUnexpected
|
||||
(← mkDvdCnstr (a.gcd d) p (.elim c)).assert
|
||||
|
||||
def decideVar (x : Var) : GoalM Unit := do
|
||||
/--
|
||||
Given a divisibility constraint solution space `s := { b, d }`,
|
||||
and a candidate assignment `v`, we want to find
|
||||
an assignment `w` such that `w ≥ v` such that exists `k`, `w = k*d + b`
|
||||
Thus,
|
||||
- `k*d + b ≥ v`
|
||||
- `k ≥ cdiv (v - b) d`
|
||||
So, we take `w = (cdiv (v - b) d)*d + b`
|
||||
-/
|
||||
def DvdSolution.ge (s : DvdSolution) (v : Int) : Int :=
|
||||
(Int.Linear.cdiv (v - s.b) s.d)*s.d + s.b
|
||||
|
||||
/--
|
||||
Given a divisibility constraint solution space `s := { b, d }`,
|
||||
and a candidate assignment `v`, we want to find
|
||||
an assignment `w` such that `w ≤ v` such that exists `k`, `w = k*d + b`
|
||||
Thus,
|
||||
- `k*d + b ≤ v`
|
||||
- `k ≤ (v - b) / d`
|
||||
So, we take `w = ((v - b) / d)*d + b`
|
||||
-/
|
||||
def DvdSolution.le (s : DvdSolution) (v : Int) : Int :=
|
||||
((v - s.b)/s.d)*s.d + s.b
|
||||
|
||||
def findDiseq? (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
|
||||
(·.2) <$> dvals.find? fun (d, _) =>
|
||||
d.den == 1 && d.num == v
|
||||
|
||||
def inDiseqValues (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Bool :=
|
||||
Option.isSome <| findDiseq? v dvals
|
||||
|
||||
def findRatDiseq? (v : Rat) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
|
||||
(·.2) <$> dvals.find? fun (d, _) => v == d
|
||||
|
||||
partial def DvdSolution.geAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
|
||||
let v := s.ge v
|
||||
if inDiseqValues v dvals then
|
||||
geAvoiding s (v+1) dvals
|
||||
else
|
||||
v
|
||||
|
||||
partial def DvdSolution.leAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
|
||||
let v := s.le v
|
||||
if inDiseqValues v dvals then
|
||||
geAvoiding s (v-1) dvals
|
||||
else
|
||||
v
|
||||
|
||||
inductive FindIntValResult where
|
||||
| found (val : Int)
|
||||
| diseq (c : DiseqCnstr)
|
||||
| dvd
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
Tries to find an integer `v` s.t. `lower ≤ v ≤ upper`, `v ∉ dvals`, and `v ∈ s`.
|
||||
Returns `.found v` if result was found, `.dvd` if it failed because of the divisibility constraint,
|
||||
and `.diseq c` because of the disequality constraint `c`.
|
||||
-/
|
||||
partial def findIntVal (s : DvdSolution) (lower : Int) (upper : Int) (dvals : Array (Rat × DiseqCnstr)) : FindIntValResult :=
|
||||
let v := s.ge lower
|
||||
if v > upper then
|
||||
.dvd
|
||||
else
|
||||
go v
|
||||
where
|
||||
go (v : Int) : FindIntValResult :=
|
||||
if let some c := findDiseq? v dvals then
|
||||
let v := s.ge (v+1)
|
||||
if v > upper then .diseq c else go v
|
||||
else
|
||||
.found v
|
||||
|
||||
partial def findRatVal (lower upper : Rat) (diseqVals : Array (Rat × DiseqCnstr)) : Rat :=
|
||||
let v := (lower + upper)/2
|
||||
if (findRatDiseq? v diseqVals).isSome then
|
||||
findRatVal lower v diseqVals
|
||||
else
|
||||
v
|
||||
|
||||
def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
|
||||
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
|
||||
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
|
||||
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
|
||||
if (← p.satisfiedLe) != .false then
|
||||
return false
|
||||
else
|
||||
let c ← mkLeCnstr p (.combine c₁ c₂)
|
||||
c.assert
|
||||
return true
|
||||
|
||||
def resolveCooperLeft (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-left NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
def resolveCooperRight (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-right NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
def resolveCooper (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
if c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs then
|
||||
resolveCooperLeft c₁ c₂
|
||||
else
|
||||
resolveCooperRight c₁ c₂
|
||||
|
||||
def resolveCooperDvdLeft (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-dvd-left NIY {← c₁.pp} {← c₂.pp} {← c.pp}"
|
||||
|
||||
def resolveCooperDvdRight (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-dvd-right NIY {← c₁.pp} {← c₂.pp} {← c.pp}"
|
||||
|
||||
def resolveCooperDvd (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
if c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs then
|
||||
resolveCooperDvdLeft c₁ c₂ c
|
||||
else
|
||||
resolveCooperDvdRight c₁ c₂ c
|
||||
|
||||
def resolveCooperDiseq (c₁ : DiseqCnstr) (c₂ : LeCnstr) (_c? : Option DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-diseq NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
/--
|
||||
Given `c₁` of the form `-a₁*x + p₁ ≤ 0`, and `c` of the form `b*x + p ≠ 0`,
|
||||
splits `c` and resolve with `c₁`.
|
||||
Recall that a disequality
|
||||
-/
|
||||
def resolveRatDiseq (c₁ : LeCnstr) (c : DiseqCnstr) : SearchM Unit := do
|
||||
let c ← if c.p.leadCoeff < 0 then
|
||||
mkDiseqCnstr (c.p.mul (-1)) (.neg c)
|
||||
else
|
||||
pure c
|
||||
let fvarId ← if let some fvarId := (← get').diseqSplits.find? c.p then
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, reusing {fvarId.name}"
|
||||
pure fvarId
|
||||
else
|
||||
let fvarId ← mkCase (.diseq c)
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, {fvarId.name}"
|
||||
modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
|
||||
pure fvarId
|
||||
let p₂ := c.p.addConst 1
|
||||
let c₂ ← mkLeCnstr p₂ (.expr (mkFVar fvarId))
|
||||
let b ← resolveRealLowerUpperConflict c₁ c₂
|
||||
assert! b
|
||||
|
||||
def processVar (x : Var) : SearchM Unit := do
|
||||
if (← eliminated x) then
|
||||
/-
|
||||
Variable has been eliminated, and will be assigned later after we have assigned
|
||||
variables that have not been eliminated.
|
||||
-/
|
||||
skipAssignment x
|
||||
return ()
|
||||
-- Solution space for divisibility constraint is `x = k*d + b`
|
||||
let dvdSol ← if let some c := (← get').dvds[x]! then
|
||||
if let some solutions ← c.getSolutions? then
|
||||
pure solutions
|
||||
else
|
||||
resolveDvdConflict c
|
||||
return ()
|
||||
else
|
||||
pure {}
|
||||
let lower? ← getBestLower? x
|
||||
let upper? ← getBestUpper? x
|
||||
let dvd? := (← get').dvds[x]!
|
||||
match lower?, upper?, dvd? with
|
||||
| none, none, none =>
|
||||
setAssignment x 0
|
||||
| some (lower, _), none, none =>
|
||||
setAssignment x lower
|
||||
| none, some (upper, _), none =>
|
||||
setAssignment x upper
|
||||
| some (lower, c₁), some (upper, c₂), none =>
|
||||
if lower ≤ upper then
|
||||
setAssignment x lower
|
||||
else
|
||||
trace[grind.cutsat.conflict] "{lower} ≤ {← getVar x} ≤ {upper}"
|
||||
resolveLowerUpperConflict c₁ c₂
|
||||
| none, none, some c =>
|
||||
if let some (_, v) ← c.getSolutions? then
|
||||
let diseqVals ← getDiseqValues x
|
||||
match lower?, upper? with
|
||||
| none, none =>
|
||||
let v := dvdSol.geAvoiding 0 diseqVals
|
||||
setAssignment x v
|
||||
| some (lower, _), none =>
|
||||
let lower := lower.ceil
|
||||
let v := dvdSol.geAvoiding lower diseqVals
|
||||
setAssignment x v
|
||||
| none, some (upper, _) =>
|
||||
let upper := upper.floor
|
||||
let v := dvdSol.leAvoiding upper diseqVals
|
||||
setAssignment x v
|
||||
| some (lower, c₁), some (upper, c₂) =>
|
||||
if lower > upper then
|
||||
let .true ← resolveRealLowerUpperConflict c₁ c₂
|
||||
| throwError "`grind` internal error, conflict resolution failed"
|
||||
return ()
|
||||
-- `lower ≤ upper` here
|
||||
if lower.ceil > upper.floor then
|
||||
if (← resolveRealLowerUpperConflict c₁ c₂) then
|
||||
-- Resolved conflict using "real" shadow
|
||||
return ()
|
||||
if !(← isApprox) then
|
||||
resolveCooper c₁ c₂
|
||||
return ()
|
||||
let r := findIntVal dvdSol lower.ceil upper.floor diseqVals
|
||||
if let .found v := r then
|
||||
setAssignment x v
|
||||
return ()
|
||||
if (← isApprox) then
|
||||
if lower < upper then
|
||||
setAssignment x <| findRatVal lower upper diseqVals
|
||||
else if let some c := findRatDiseq? lower diseqVals then
|
||||
resolveRatDiseq c₁ c
|
||||
else
|
||||
setAssignment x lower
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| some (lower, _), none, some c =>
|
||||
if let some (d, b) ← c.getSolutions? then
|
||||
/-
|
||||
- `x ≥ lower ∧ x = k*d + b`
|
||||
- `k*d + b ≥ lower`
|
||||
- `k ≥ cdiv (lower - b) d`
|
||||
- So, we take `x = (cdiv (lower - b) d)*d + b`
|
||||
-/
|
||||
setAssignment x ((Int.Linear.cdiv (lower - b) d)*d + b)
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| none, some (upper, _), some c =>
|
||||
if let some (d, b) ← c.getSolutions? then
|
||||
/-
|
||||
- `x ≤ upper ∧ x = k*d + b`
|
||||
- `k*d + b ≤ upper`
|
||||
- `k ≤ (upper - b)/d`
|
||||
- So, we take `x = ((upper - b)/d)*d + b`
|
||||
-/
|
||||
setAssignment x (((upper - b)/d)*d + b)
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| _, _, _ =>
|
||||
-- TODO: cases containing a divisibility constraint.
|
||||
-- TODO: remove the following
|
||||
setAssignment x 0
|
||||
match r with
|
||||
| .dvd => resolveCooperDvd c₁ c₂ (← get').dvds[x]!.get!
|
||||
| .diseq c => resolveCooperDiseq c c₂ (← get').dvds[x]!
|
||||
| _ => unreachable!
|
||||
|
||||
/-- Returns `true` if we already have a complete assignment / model. -/
|
||||
def hasAssignment : GoalM Bool := do
|
||||
return (← get').vars.size == (← get').assignment.size
|
||||
|
||||
private def isDone : GoalM Bool := do
|
||||
if (← hasAssignment) then
|
||||
private def findCase (decVars : FVarIdSet) : SearchM Case := do
|
||||
repeat
|
||||
let numCases := (← get).cases.size
|
||||
assert! numCases > 0
|
||||
let case := (← get).cases[numCases-1]!
|
||||
modify fun s => { s with cases := s.cases.pop }
|
||||
if decVars.contains case.fvarId then
|
||||
return case
|
||||
-- Conflict does not depend on this case.
|
||||
trace[grind.debug.cutsat.backtrack] "skipping {case.fvarId.name}"
|
||||
unreachable!
|
||||
|
||||
def resolveConflict (h : UnsatProof) : SearchM Bool := do
|
||||
let decVars := h.collectDecVars.run (← get).decVars
|
||||
if decVars.isEmpty then
|
||||
closeGoal (← h.toExprProof)
|
||||
return false
|
||||
let c ← findCase decVars
|
||||
modify' fun _ => c.saved
|
||||
match c.kind with
|
||||
| .diseq c₁ =>
|
||||
let decVars := decVars.erase c.fvarId |>.toArray
|
||||
let p' := c₁.p.mul (-1) |>.addConst 1
|
||||
let c' ← mkLeCnstr p' (.ofDiseqSplit c₁ c.fvarId h decVars)
|
||||
trace[grind.debug.cutsat.backtrack] "resolved diseq split: {← c'.pp}"
|
||||
c'.assert
|
||||
return true
|
||||
if (← inconsistent) then
|
||||
return true
|
||||
return false
|
||||
| _ => throwError "NIY resolve conflict"
|
||||
|
||||
/-- Search for an assignment/model for the linear constraints. -/
|
||||
def searchAssigment : GoalM Unit := do
|
||||
def searchAssigmentMain : SearchM Unit := do
|
||||
repeat
|
||||
if (← isDone) then
|
||||
if (← hasAssignment) then
|
||||
return ()
|
||||
if (← isInconsistent) then
|
||||
-- `grind` state is inconsistent
|
||||
return ()
|
||||
if let some c := (← get').conflict? then
|
||||
unless (← resolveConflict c) do
|
||||
return ()
|
||||
let x : Var := (← get').assignment.size
|
||||
decideVar x
|
||||
processVar x
|
||||
|
||||
def traceModel : GoalM Unit := do
|
||||
if (← isTracingEnabledFor `grind.cutsat.model) then
|
||||
for (x, v) in (← mkModel (← get)) do
|
||||
trace[grind.cutsat.model] "{quoteIfNotAtom x} := {v}"
|
||||
|
||||
def searchAssigment : GoalM Unit := do
|
||||
-- TODO: .int case
|
||||
-- TODO:
|
||||
searchAssigmentMain .rat |>.run' {}
|
||||
assignElimVars
|
||||
traceModel
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
83
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean
Normal file
83
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean
Normal file
@@ -0,0 +1,83 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
/--
|
||||
In principle, we only need to support two kinds of case split.
|
||||
- Disequalities.
|
||||
- Cooper-Left, but we have 4 different variants of this one.
|
||||
-/
|
||||
inductive CaseKind where
|
||||
| diseq (d : DiseqCnstr)
|
||||
| copperLeft
|
||||
| copperDvdLeft
|
||||
| cooperRight
|
||||
| cooperDvdRight
|
||||
deriving Inhabited
|
||||
|
||||
structure Case where
|
||||
kind : CaseKind
|
||||
/--
|
||||
Decision variable used to represent the case-split.
|
||||
For example, suppose we are splitting on `p ≠ 0`. Then,
|
||||
we create a decision variable `h : p + 1 ≤ 0`
|
||||
-/
|
||||
fvarId : FVarId
|
||||
/--
|
||||
Snapshot of the cutsat state for backtracking purposes.
|
||||
We do not use a trail stack.
|
||||
-/
|
||||
saved : State
|
||||
deriving Inhabited
|
||||
|
||||
inductive Search.Kind where
|
||||
| /--
|
||||
Allow variables to be assigned to rational numbers during model
|
||||
construction.
|
||||
-/
|
||||
rat
|
||||
| /--
|
||||
Variables must be assigned to integer numbers.
|
||||
Cooper case splits are required in this mode.
|
||||
-/
|
||||
int
|
||||
deriving Inhabited, BEq
|
||||
|
||||
/--
|
||||
State of the model search procedure.
|
||||
-/
|
||||
structure Search.State where
|
||||
/-- Decision stack (aka case-split stack) -/
|
||||
cases : PArray Case := {}
|
||||
/-- `precise := false` if not all constraints were satisfied during the search. -/
|
||||
precise : Bool := true
|
||||
/-- Set of decision variables in `cases`. -/
|
||||
decVars : FVarIdSet := {}
|
||||
|
||||
abbrev SearchM := ReaderT Search.Kind (StateRefT Search.State GoalM)
|
||||
|
||||
/-- Returns `true` if approximations are allowed. -/
|
||||
def isApprox : SearchM Bool :=
|
||||
return (← read) == .rat
|
||||
|
||||
/-- Sets `precise` to `false` to indicate that some constraint was not satisfied. -/
|
||||
def setImprecise : SearchM Unit := do
|
||||
modify fun s => { s with precise := false }
|
||||
|
||||
def mkCase (kind : CaseKind) : SearchM FVarId := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
let saved ← get'
|
||||
modify fun s => { s with
|
||||
cases := s.cases.push { saved, fvarId, kind }
|
||||
decVars := s.decVars.insert fvarId
|
||||
}
|
||||
modify' fun s => { s with caseSplits := true }
|
||||
return fvarId
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Linear
|
||||
import Std.Internal.Rat
|
||||
import Lean.Data.PersistentArray
|
||||
import Lean.Meta.Tactic.Grind.ENodeKey
|
||||
import Lean.Meta.Tactic.Grind.Arith.Util
|
||||
@@ -12,6 +13,57 @@ import Lean.Meta.Tactic.Grind.Arith.Util
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
export Int.Linear (Var Poly)
|
||||
export Std.Internal (Rat)
|
||||
|
||||
deriving instance Hashable for Poly
|
||||
|
||||
/-!
|
||||
This module implements a model-based decision procedure for linear integer arithmetic,
|
||||
inspired by Section 4 of "Cutting to the Chase: Solving Linear Integer Arithmetic".
|
||||
Our implementation includes several enhancements and modifications:
|
||||
Key Features:
|
||||
- Extended constraint support (equality and disequality)
|
||||
- Optimized encoding of `Cooper-Left` rule using "big"-disjunction instead of fresh variables
|
||||
- Decision variable tracking for case splits (disequalities, `Cooper-Left`, `Cooper-Right`)
|
||||
|
||||
Constraint Types:
|
||||
We handle four categories of linear polynomial constraints (where p is a linear polynomial):
|
||||
1. Equality: `p = 0`
|
||||
2. Divisibility: `d ∣ p`
|
||||
3. Inequality: `p ≤ 0`
|
||||
4. Disequality: `p ≠ 0`
|
||||
|
||||
Implementation Details:
|
||||
- Polynomials use `Int.Linear.Poly` with sorted linear monomials (leading monomial contains max variable)
|
||||
- Equalities are eliminated eagerly
|
||||
- Divisibility constraints are maintained in solved form (one constraint per variable) using `Div-Solve`
|
||||
|
||||
Model Construction:
|
||||
The procedure builds a model incrementally, resolving conflicts through constraint generation.
|
||||
For example:
|
||||
Given a partial model `{x := 1}` and constraint `3 ∣ 3*y + x + 1`:
|
||||
- Cannot extend to `y` because `3 ∣ 3*y + 2` is unsatisfiable
|
||||
- Generate implied constraint `3 ∣ x + 1`
|
||||
- Force model update for `x`
|
||||
|
||||
Variable Assignment:
|
||||
When assigning a variable `y`, we consider:
|
||||
- Best upper and lower bounds (inequalities)
|
||||
- Divisibility constraint
|
||||
- Disequality constraints
|
||||
`Cooper-Left` and `Cooper-Right` rules handle the combination of inequalities and divisibility.
|
||||
For unsatisfiable disequalities p ≠ 0, we generate case split: `p + 1 ≤ 0 ∨ -p + 1 ≤ 0`
|
||||
|
||||
Contradiction Handling:
|
||||
- Check dependency on decision variables
|
||||
- If independent, use contradiction to close current grind goal
|
||||
- Otherwise, trigger backtracking
|
||||
|
||||
Optimization:
|
||||
We employ rational approximation for model construction:
|
||||
- Continue with rational solutions when integer solutions aren't immediately found
|
||||
- Helps identify simpler unsatisfiability proofs before full integer model construction
|
||||
-/
|
||||
|
||||
/-
|
||||
Remark: we will not define a parent structure `Cnstr` with the common
|
||||
@@ -31,24 +83,8 @@ inductive EqCnstrProof where
|
||||
| norm (c : EqCnstr)
|
||||
| divCoeffs (c : EqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
|
||||
end
|
||||
| ofLeGe (c₁ : LeCnstr) (c₂ : LeCnstr)
|
||||
|
||||
mutual
|
||||
/-- A disequality constraint and its justification/proof. -/
|
||||
structure DiseqCnstr where
|
||||
p : Poly
|
||||
h : DiseqCnstrProof
|
||||
id : Nat
|
||||
|
||||
inductive DiseqCnstrProof where
|
||||
| expr (h : Expr)
|
||||
| core (p₁ p₂ : Poly) (h : Expr)
|
||||
| norm (c : DiseqCnstr)
|
||||
| divCoeffs (c : DiseqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : DiseqCnstr)
|
||||
end
|
||||
|
||||
mutual
|
||||
/-- A divisibility constraint and its justification/proof. -/
|
||||
structure DvdCnstr where
|
||||
d : Int
|
||||
@@ -80,8 +116,23 @@ inductive LeCnstrProof where
|
||||
| divCoeffs (c : LeCnstr)
|
||||
| combine (c₁ c₂ : LeCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : LeCnstr)
|
||||
| ofLeDiseq (c₁ : LeCnstr) (c₂ : DiseqCnstr)
|
||||
| ofDiseqSplit (c₁ : DiseqCnstr) (decVar : FVarId) (h : UnsatProof) (decVars : Array FVarId)
|
||||
-- TODO: missing constructors
|
||||
end
|
||||
|
||||
/-- A disequality constraint and its justification/proof. -/
|
||||
structure DiseqCnstr where
|
||||
p : Poly
|
||||
h : DiseqCnstrProof
|
||||
id : Nat
|
||||
|
||||
inductive DiseqCnstrProof where
|
||||
| expr (h : Expr)
|
||||
| core (p₁ p₂ : Poly) (h : Expr)
|
||||
| norm (c : DiseqCnstr)
|
||||
| divCoeffs (c : DiseqCnstr)
|
||||
| neg (c : DiseqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : DiseqCnstr)
|
||||
|
||||
/--
|
||||
A proof of `False`.
|
||||
@@ -93,6 +144,11 @@ inductive UnsatProof where
|
||||
| eq (c : EqCnstr)
|
||||
| diseq (c : DiseqCnstr)
|
||||
|
||||
end
|
||||
|
||||
instance : Inhabited DvdCnstr where
|
||||
default := { d := 0, p := .num 0, h := .expr default, id := 0 }
|
||||
|
||||
abbrev VarSet := RBTree Var compare
|
||||
|
||||
/-- State of the cutsat procedure. -/
|
||||
@@ -142,14 +198,29 @@ structure State where
|
||||
-/
|
||||
occurs : PArray VarSet := {}
|
||||
/-- Partial assignment being constructed by cutsat. -/
|
||||
assignment : PArray Int := {}
|
||||
assignment : PArray Rat := {}
|
||||
/-- Next unique id for a constraint. -/
|
||||
nextCnstrId : Nat := 0
|
||||
/--
|
||||
`caseSplits` is `true` if cutsat is searching for model and already performed case splits.
|
||||
This information is used to decide whether a conflict should immediately close the
|
||||
current `grind` goal or not.
|
||||
-/
|
||||
caseSplits : Bool := false
|
||||
/--
|
||||
`conflict?` is `some ..` if a contradictory constraint was derived.
|
||||
This field is only set when `caseSplits` is `true`. Otherwise, we
|
||||
can convert `UnsatProof` into a Lean term and close the current `grind` goal.
|
||||
-/
|
||||
conflict? : Option UnsatProof := none
|
||||
/--
|
||||
Cache decision variables used when splitting on disequalities.
|
||||
This is necessary because the same disequality may be in different conflicts.
|
||||
-/
|
||||
diseqSplits : PHashMap Poly FVarId := {}
|
||||
|
||||
/-
|
||||
TODO: support for storing
|
||||
- Disjuctions: they come from conflict resolution, and disequalities.
|
||||
- Disequalities.
|
||||
- Linear integer terms appearing in the main module, and model-based equality propagation.
|
||||
TODO: Model-based theory combination.
|
||||
-/
|
||||
deriving Inhabited
|
||||
|
||||
|
||||
@@ -46,9 +46,8 @@ def get' : GoalM State := do
|
||||
|
||||
/-- Returns `true` if the cutsat state is inconsistent. -/
|
||||
def inconsistent : GoalM Bool := do
|
||||
-- TODO: we will have a nested backtracking search in cutsat
|
||||
-- and this function will have to be refined.
|
||||
isInconsistent
|
||||
if (← isInconsistent) then return true
|
||||
return (← get').conflict?.isSome
|
||||
|
||||
def getVars : GoalM (PArray Expr) :=
|
||||
return (← get').vars
|
||||
@@ -65,11 +64,22 @@ def mkCnstrId : GoalM Nat := do
|
||||
modify' fun s => { s with nextCnstrId := id + 1 }
|
||||
return id
|
||||
|
||||
private partial def shrink (a : PArray Int) (sz : Nat) : PArray Int :=
|
||||
if a.size > sz then
|
||||
shrink a.pop sz
|
||||
else
|
||||
a
|
||||
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
|
||||
@[extern "lean_grind_cutsat_assert_eq"] -- forward definition
|
||||
opaque EqCnstr.assert (c : EqCnstr) : GoalM Unit
|
||||
|
||||
-- TODO: PArray.shrink and PArray.resize
|
||||
|
||||
partial def shrink (a : PArray Rat) (sz : Nat) : PArray Rat :=
|
||||
if a.size > sz then shrink a.pop sz else a
|
||||
|
||||
partial def resize (a : PArray Rat) (sz : Nat) : PArray Rat :=
|
||||
if a.size > sz then shrink a sz else go a
|
||||
where
|
||||
go (a : PArray Rat) : PArray Rat :=
|
||||
if a.size < sz then go (a.push 0) else a
|
||||
|
||||
/-- Resets the assingment of any variable bigger or equal to `x`. -/
|
||||
def resetAssignmentFrom (x : Var) : GoalM Unit := do
|
||||
@@ -210,9 +220,9 @@ abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
|
||||
Tries to evaluate the polynomial `p` using the partial model/assignment built so far.
|
||||
The result is `none` if the polynomial contains variables that have not been assigned.
|
||||
-/
|
||||
def _root_.Int.Linear.Poly.eval? (p : Poly) : GoalM (Option Int) := do
|
||||
def _root_.Int.Linear.Poly.eval? (p : Poly) : GoalM (Option Rat) := do
|
||||
let a := (← get').assignment
|
||||
let rec go (v : Int) : Poly → Option Int
|
||||
let rec go (v : Rat) : Poly → Option Rat
|
||||
| .num k => some (v + k)
|
||||
| .add k x p =>
|
||||
if _ : x < a.size then
|
||||
@@ -233,7 +243,8 @@ Returns `.true` if `c` is satisfied by the current partial model,
|
||||
-/
|
||||
def DvdCnstr.satisfied (c : DvdCnstr) : GoalM LBool := do
|
||||
let some v ← c.p.eval? | return .undef
|
||||
return decide (c.d ∣ v) |>.toLBool
|
||||
if v.den != 1 then return .false
|
||||
return decide (c.d ∣ v.num) |>.toLBool
|
||||
|
||||
def _root_.Int.Linear.Poly.satisfiedLe (p : Poly) : GoalM LBool := do
|
||||
let some v ← p.eval? | return .undef
|
||||
|
||||
@@ -5,3 +5,4 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Arith.Offset.Model
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
@@ -127,6 +127,18 @@ private def ppOffset : M Unit := do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `offset } "Assignment satisfying offset contraints" ms
|
||||
|
||||
private def ppCutsat : M Unit := do
|
||||
let goal ← read
|
||||
let s := goal.arith.cutsat
|
||||
let nodes := s.varMap
|
||||
if nodes.isEmpty then return ()
|
||||
let model ← Arith.Cutsat.mkModel goal
|
||||
if model.isEmpty then return ()
|
||||
let mut ms := #[]
|
||||
for (e, val) in model do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `cutsat } "Assignment satisfying integer contraints" ms
|
||||
|
||||
private def ppThresholds (c : Grind.Config) : M Unit := do
|
||||
let goal ← read
|
||||
let maxGen := goal.enodes.foldl (init := 0) fun g _ n => Nat.max g n.generation
|
||||
@@ -165,6 +177,7 @@ where
|
||||
ppCasesTrace
|
||||
ppActiveTheoremPatterns
|
||||
ppOffset
|
||||
ppCutsat
|
||||
ppThresholds config
|
||||
|
||||
end Lean.Meta.Grind
|
||||
|
||||
@@ -390,7 +390,8 @@ def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Ch
|
||||
let opts := cmdlineOpts.mergeBy (fun _ _ fileOpt => fileOpt) fileSetupResult.fileOptions
|
||||
|
||||
-- default to async elaboration; see also `Elab.async` docs
|
||||
let opts := Elab.async.setIfNotSet opts true
|
||||
-- (temporarily disabled pending #7241)
|
||||
--let opts := Elab.async.setIfNotSet opts true
|
||||
|
||||
return .ok {
|
||||
mainModuleName
|
||||
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Lean.Expr
|
||||
import Lean.ToLevel
|
||||
import Init.Data.BitVec.Basic
|
||||
import Init.Data.SInt.Basic
|
||||
universe u
|
||||
|
||||
namespace Lean
|
||||
@@ -97,6 +98,71 @@ instance : ToExpr USize where
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (mkConst ``USize) r
|
||||
(.app (.const ``USize.instOfNat []) r)
|
||||
|
||||
instance : ToExpr Int8 where
|
||||
toTypeExpr := mkConst ``Int8
|
||||
toExpr i := if 0 ≤ i then
|
||||
mkNat i.toNatClampNeg
|
||||
else
|
||||
mkApp3 (.const ``Neg.neg [0]) (.const ``Int8 []) (.const ``Int8.instNeg [])
|
||||
(mkNat (-(i.toInt)).toNat)
|
||||
where
|
||||
mkNat (n : Nat) : Expr :=
|
||||
let r := mkRawNatLit n
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (.const ``Int8 []) r
|
||||
(.app (.const ``Int8.instOfNat []) r)
|
||||
|
||||
instance : ToExpr Int16 where
|
||||
toTypeExpr := mkConst ``Int16
|
||||
toExpr i := if 0 ≤ i then
|
||||
mkNat i.toNatClampNeg
|
||||
else
|
||||
mkApp3 (.const ``Neg.neg [0]) (.const ``Int16 []) (.const ``Int16.instNeg [])
|
||||
(mkNat (-(i.toInt)).toNat)
|
||||
where
|
||||
mkNat (n : Nat) : Expr :=
|
||||
let r := mkRawNatLit n
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (.const ``Int16 []) r
|
||||
(.app (.const ``Int16.instOfNat []) r)
|
||||
|
||||
instance : ToExpr Int32 where
|
||||
toTypeExpr := mkConst ``Int32
|
||||
toExpr i := if 0 ≤ i then
|
||||
mkNat i.toNatClampNeg
|
||||
else
|
||||
mkApp3 (.const ``Neg.neg [0]) (.const ``Int32 []) (.const ``Int32.instNeg [])
|
||||
(mkNat (-(i.toInt)).toNat)
|
||||
where
|
||||
mkNat (n : Nat) : Expr :=
|
||||
let r := mkRawNatLit n
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (.const ``Int32 []) r
|
||||
(.app (.const ``Int32.instOfNat []) r)
|
||||
|
||||
instance : ToExpr Int64 where
|
||||
toTypeExpr := mkConst ``Int64
|
||||
toExpr i := if 0 ≤ i then
|
||||
mkNat i.toNatClampNeg
|
||||
else
|
||||
mkApp3 (.const ``Neg.neg [0]) (.const ``Int64 []) (.const ``Int64.instNeg [])
|
||||
(mkNat (-(i.toInt)).toNat)
|
||||
where
|
||||
mkNat (n : Nat) : Expr :=
|
||||
let r := mkRawNatLit n
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (.const ``Int64 []) r
|
||||
(.app (.const ``Int64.instOfNat []) r)
|
||||
|
||||
instance : ToExpr ISize where
|
||||
toTypeExpr := mkConst ``ISize
|
||||
toExpr i := if 0 ≤ i then
|
||||
mkNat i.toNatClampNeg
|
||||
else
|
||||
mkApp3 (.const ``Neg.neg [0]) (.const ``ISize []) (.const ``ISize.instNeg [])
|
||||
(mkNat (-(i.toInt)).toNat)
|
||||
where
|
||||
mkNat (n : Nat) : Expr :=
|
||||
let r := mkRawNatLit n
|
||||
mkApp3 (.const ``OfNat.ofNat [0]) (.const ``ISize []) r
|
||||
(.app (.const ``ISize.instOfNat []) r)
|
||||
|
||||
instance : ToExpr Bool where
|
||||
toExpr := fun b => if b then mkConst ``Bool.true else mkConst ``Bool.false
|
||||
toTypeExpr := mkConst ``Bool
|
||||
|
||||
@@ -253,6 +253,9 @@ section LawfulEq
|
||||
/--
|
||||
A typeclass for comparison functions satisfying `cmp a b = .eq` if and only if the logical equality
|
||||
`a = b` holds.
|
||||
|
||||
This typeclass distinguishes itself from `LawfulBEqCmp` by using logical equality (`=`) instead of
|
||||
boolean equality (`==`).
|
||||
-/
|
||||
class LawfulEqCmp {α : Type u} (cmp : α → α → Ordering) : Prop extends ReflCmp cmp where
|
||||
/-- If two values compare equal, then they are logically equal. -/
|
||||
@@ -261,6 +264,9 @@ class LawfulEqCmp {α : Type u} (cmp : α → α → Ordering) : Prop extends Re
|
||||
/--
|
||||
A typeclass for types with a comparison function that satisfies `compare a b = .eq` if and only if
|
||||
the logical equality `a = b` holds.
|
||||
|
||||
This typeclass distinguishes itself from `LawfulBEqOrd` by using logical equality (`=`) instead of
|
||||
boolean equality (`==`).
|
||||
-/
|
||||
abbrev LawfulEqOrd (α : Type u) [Ord α] := LawfulEqCmp (compare : α → α → Ordering)
|
||||
|
||||
@@ -276,6 +282,48 @@ theorem compare_beq_iff_eq {a b : α} : cmp a b == .eq ↔ a = b :=
|
||||
|
||||
end LawfulEq
|
||||
|
||||
section LawfulBEq
|
||||
|
||||
/--
|
||||
A typeclass for comparison functions satisfying `cmp a b = .eq` if and only if the boolean equality
|
||||
`a == b` holds.
|
||||
|
||||
This typeclass distinguishes itself from `LawfulEqCmp` by using boolean equality (`==`) instead of
|
||||
logical equality (`=`).
|
||||
-/
|
||||
class LawfulBEqCmp {α : Type u} [BEq α] (cmp : α → α → Ordering) : Prop where
|
||||
/-- If two values compare equal, then they are logically equal. -/
|
||||
compare_eq_iff_beq {a b : α} : cmp a b = .eq ↔ a == b
|
||||
|
||||
/--
|
||||
A typeclass for types with a comparison function that satisfies `compare a b = .eq` if and only if
|
||||
the boolean equality `a == b` holds.
|
||||
|
||||
This typeclass distinguishes itself from `LawfulEqOrd` by using boolean equality (`==`) instead of
|
||||
logical equality (`=`).
|
||||
-/
|
||||
abbrev LawfulBEqOrd (α : Type u) [BEq α] [Ord α] := LawfulBEqCmp (compare : α → α → Ordering)
|
||||
|
||||
variable {α : Type u} [BEq α] {cmp : α → α → Ordering}
|
||||
|
||||
instance [LawfulEqCmp cmp] [LawfulBEq α] :
|
||||
LawfulBEqCmp cmp where
|
||||
compare_eq_iff_beq := compare_eq_iff_eq.trans beq_iff_eq.symm
|
||||
|
||||
theorem LawfulBEqCmp.equivBEq [inst : LawfulBEqCmp cmp] [TransCmp cmp] : EquivBEq α where
|
||||
refl := inst.compare_eq_iff_beq.mp ReflCmp.compare_self
|
||||
symm := by
|
||||
simp only [← inst.compare_eq_iff_beq]
|
||||
exact OrientedCmp.eq_symm
|
||||
trans := by
|
||||
simp only [← inst.compare_eq_iff_beq]
|
||||
exact TransCmp.eq_trans
|
||||
|
||||
instance LawfulBEqOrd.equivBEq [Ord α] [LawfulBEqOrd α] [TransOrd α] : EquivBEq α :=
|
||||
LawfulBEqCmp.equivBEq (cmp := compare)
|
||||
|
||||
end LawfulBEq
|
||||
|
||||
namespace Internal
|
||||
|
||||
variable {α : Type u}
|
||||
@@ -292,6 +340,16 @@ def beqOfOrd [Ord α] : BEq α where
|
||||
theorem beq_eq [Ord α] {a b : α} : (a == b) = (compare a b == .eq) :=
|
||||
rfl
|
||||
|
||||
theorem beq_iff [Ord α] {a b : α} : (a == b) = true ↔ compare a b = .eq := by
|
||||
rw [beq_eq, beq_iff_eq]
|
||||
|
||||
theorem eq_beqOfOrd_of_lawfulBEqOrd [Ord α] (inst : BEq α) [instLawful : LawfulBEqOrd α] :
|
||||
inst = beqOfOrd := by
|
||||
cases inst; rename_i instBEq
|
||||
congr; ext a b
|
||||
rw [Bool.eq_iff_iff, beq_iff_eq, instLawful.compare_eq_iff_beq]
|
||||
rfl
|
||||
|
||||
theorem equivBEq_of_transOrd [Ord α] [TransOrd α] : EquivBEq α where
|
||||
symm {a b} h := by simp_all [OrientedCmp.eq_comm]
|
||||
trans h₁ h₂ := by simp_all only [beq_eq, beq_iff_eq]; exact TransCmp.eq_trans h₁ h₂
|
||||
|
||||
@@ -170,6 +170,7 @@ def erase [BEq α] (a : α) : AssocList α β → AssocList α β
|
||||
| cons k v l => bif k == a then l else cons k v (l.erase a)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[specialize]
|
||||
def modify [BEq α] [LawfulBEq α] (a : α) (f : β a → β a) :
|
||||
AssocList α β → AssocList α β
|
||||
| nil => nil
|
||||
@@ -182,6 +183,7 @@ def modify [BEq α] [LawfulBEq α] (a : α) (f : β a → β a) :
|
||||
cons k v (modify a f l)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[specialize]
|
||||
def alter [BEq α] [LawfulBEq α] (a : α) (f : Option (β a) → Option (β a)) :
|
||||
AssocList α β → AssocList α β
|
||||
| nil => match f none with
|
||||
@@ -200,6 +202,7 @@ def alter [BEq α] [LawfulBEq α] (a : α) (f : Option (β a) → Option (β a))
|
||||
namespace Const
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[specialize]
|
||||
def modify [BEq α] {β : Type v} (a : α) (f : β → β) :
|
||||
AssocList α (fun _ => β) → AssocList α (fun _ => β)
|
||||
| nil => nil
|
||||
@@ -210,6 +213,7 @@ def modify [BEq α] {β : Type v} (a : α) (f : β → β) :
|
||||
cons k v (modify a f l)
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
@[specialize]
|
||||
def alter [BEq α] {β : Type v} (a : α) (f : Option β → Option β) :
|
||||
AssocList α (fun _ => β) → AssocList α (fun _ => β)
|
||||
| nil => match f none with
|
||||
|
||||
@@ -858,7 +858,7 @@ theorem distinct_keys [EquivBEq α] [LawfulHashable α] (h : m.1.WF) :
|
||||
m.1.keys.Pairwise (fun a b => (a == b) = false) := by
|
||||
simp_to_model using (Raw.WF.out h).distinct.distinct
|
||||
|
||||
theorem map_sigma_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
m.1.toList.map Sigma.fst = m.1.keys := by
|
||||
simp_to_model
|
||||
rw [List.keys_eq_map]
|
||||
@@ -894,9 +894,9 @@ namespace Const
|
||||
|
||||
variable {β : Type v} (m : Raw₀ α (fun _ => β))
|
||||
|
||||
theorem map_prod_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
(Raw.Const.toList m.1).map Prod.fst = m.1.keys := by
|
||||
simp_to_model using List.map_prod_fst_map_toProd_eq_keys
|
||||
simp_to_model using List.map_fst_map_toProd_eq_keys
|
||||
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] (h : m.1.WF) :
|
||||
(Raw.Const.toList m.1).length = m.1.size := by
|
||||
|
||||
@@ -964,9 +964,14 @@ theorem distinct_keys [EquivBEq α] [LawfulHashable α] :
|
||||
Raw₀.distinct_keys ⟨m.1, m.2.size_buckets_pos⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
m.1.toList.map Sigma.fst = m.1.keys :=
|
||||
Raw₀.map_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_sigma_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
m.1.toList.map Sigma.fst = m.1.keys :=
|
||||
Raw₀.map_sigma_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
Raw₀.map_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] :
|
||||
@@ -1010,9 +1015,14 @@ namespace Const
|
||||
variable {β : Type v} {m : DHashMap α (fun _ => β)}
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
(toList m).map Prod.fst = m.keys :=
|
||||
Raw₀.Const.map_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_prod_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
(toList m).map Prod.fst = m.keys :=
|
||||
Raw₀.Const.map_prod_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
Raw₀.Const.map_fst_toList_eq_keys ⟨m.1, m.2.size_buckets_pos⟩
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] :
|
||||
@@ -1081,7 +1091,7 @@ theorem fold_eq_foldl_toList {f : δ → (a : α) → β a → δ} {init : δ} :
|
||||
|
||||
@[simp]
|
||||
theorem forM_eq_forM [Monad m'] [LawfulMonad m'] {f : (a : α) → β a → m' PUnit} :
|
||||
DHashMap.forM f m = ForM.forM m (fun a => f a.1 a.2):= rfl
|
||||
DHashMap.forM f m = ForM.forM m (fun a => f a.1 a.2) := rfl
|
||||
|
||||
theorem forM_eq_forM_toList [Monad m'] [LawfulMonad m'] {f : (a : α) × β a → m' PUnit} :
|
||||
ForM.forM m f = ForM.forM m.toList f :=
|
||||
|
||||
@@ -1053,9 +1053,14 @@ theorem distinct_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
simp_to_raw using Raw₀.distinct_keys ⟨m, h.size_buckets_pos⟩ h
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
m.toList.map Sigma.fst = m.keys := by
|
||||
apply Raw₀.map_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_sigma_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
m.toList.map Sigma.fst = m.keys := by
|
||||
apply Raw₀.map_sigma_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
apply Raw₀.map_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
@@ -1099,9 +1104,14 @@ namespace Const
|
||||
variable {β : Type v} {m : Raw α (fun _ => β)}
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
(Raw.Const.toList m).map Prod.fst = m.keys := by
|
||||
apply Raw₀.Const.map_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_prod_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
(Raw.Const.toList m).map Prod.fst = m.keys := by
|
||||
apply Raw₀.Const.map_prod_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
apply Raw₀.Const.map_fst_toList_eq_keys ⟨m, h.size_buckets_pos⟩
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Raw
|
||||
import Std.Data.DTreeMap.Raw.Basic
|
||||
import Std.Data.DTreeMap.Internal.WF.Lemmas
|
||||
|
||||
/-!
|
||||
@@ -25,31 +25,16 @@ private local instance : Coe (Type v) (α → Type v) where coe γ := fun _ =>
|
||||
namespace Std.DTreeMap
|
||||
open Internal (Impl)
|
||||
|
||||
namespace Raw
|
||||
|
||||
/--
|
||||
Updates the values of the map by applying the given function to all mappings, keeping
|
||||
only those mappings where the function returns `some` value.
|
||||
-/
|
||||
def filterMap (f : (a : α) → β a → Option (γ a)) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.filterMap! f⟩
|
||||
|
||||
/-- Updates the values of the map by applying the given function to all mappings. -/
|
||||
@[inline]
|
||||
def map (f : (a : α) → β a → γ a) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.map f⟩
|
||||
|
||||
/-!
|
||||
We do not provide `get*GE`, `get*GT`, `get*LE` and `get*LT` functions for the raw trees.
|
||||
-/
|
||||
|
||||
end Raw
|
||||
|
||||
@[inline, inherit_doc Raw.filterMap]
|
||||
def filterMap (f : (a : α) → β a → Option (γ a)) (t : DTreeMap α β cmp) : DTreeMap α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.filterMap f t.wf.balanced |>.impl, t.wf.filterMap⟩
|
||||
|
||||
@[inline, inherit_doc Raw.map]
|
||||
/-- Updates the values of the map by applying the given function to all mappings. -/
|
||||
@[inline]
|
||||
def map (f : (a : α) → β a → γ a) (t : DTreeMap α β cmp) : DTreeMap α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.map f, t.wf.map⟩
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ This file develops the type `Std.DTreeMap` of dependent tree maps.
|
||||
Lemmas about the operations on `Std.DTreeMap` will be available in the
|
||||
module `Std.Data.DTreeMap.Lemmas`.
|
||||
|
||||
See the module `Std.Data.DTreeMap.Raw` for a variant of this type which is safe to use in
|
||||
See the module `Std.Data.DTreeMap.Raw.Basic` for a variant of this type which is safe to use in
|
||||
nested inductive types.
|
||||
-/
|
||||
|
||||
@@ -791,17 +791,17 @@ def fold (f : δ → (a : α) → β a → δ) (init : δ) (t : DTreeMap α β c
|
||||
|
||||
/-- Folds the given monadic function over the mappings in the map in descending order. -/
|
||||
@[inline]
|
||||
def foldrM (f : δ → (a : α) → β a → m δ) (init : δ) (t : DTreeMap α β cmp) : m δ :=
|
||||
def foldrM (f : (a : α) → β a → δ → m δ) (init : δ) (t : DTreeMap α β cmp) : m δ :=
|
||||
t.inner.foldrM f init
|
||||
|
||||
/-- Folds the given function over the mappings in the map in descending order. -/
|
||||
@[inline]
|
||||
def foldr (f : δ → (a : α) → β a → δ) (init : δ) (t : DTreeMap α β cmp) : δ :=
|
||||
def foldr (f : (a : α) → β a → δ → δ) (init : δ) (t : DTreeMap α β cmp) : δ :=
|
||||
t.inner.foldr f init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → β a → δ) (init : δ) (t : DTreeMap α β cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun k v acc => f acc k v) init t
|
||||
|
||||
/-- Partitions a tree map into two tree maps based on a predicate. -/
|
||||
@[inline] def partition (f : (a : α) → β a → Bool)
|
||||
|
||||
@@ -59,7 +59,8 @@ private def queryNames : Array Name :=
|
||||
``get!_eq_getValueCast!, ``Const.get!_eq_getValue!,
|
||||
``getD_eq_getValueCastD, ``Const.getD_eq_getValueD,
|
||||
``getKey?_eq_getKey?, ``getKey_eq_getKey,
|
||||
``getKey!_eq_getKey!, ``getKeyD_eq_getKeyD]
|
||||
``getKey!_eq_getKey!, ``getKeyD_eq_getKeyD,
|
||||
``keys_eq_keys, ``toList_eq_toListModel, ``Const.toList_eq_toListModel_map]
|
||||
|
||||
private def modifyMap : Std.HashMap Name Name :=
|
||||
.ofList
|
||||
@@ -1465,4 +1466,109 @@ theorem getThenInsertIfNew?!_snd [TransOrd α] (h : t.WF) {k : α} {v : β} :
|
||||
|
||||
end Const
|
||||
|
||||
theorem length_keys [TransOrd α] (h : t.WF) :
|
||||
t.keys.length = t.size := by
|
||||
simp_to_model using List.length_keys_eq_length
|
||||
|
||||
theorem isEmpty_keys :
|
||||
t.keys.isEmpty = t.isEmpty := by
|
||||
simp_to_model using List.isEmpty_keys_eq_isEmpty
|
||||
|
||||
theorem contains_keys [BEq α] [beqOrd : LawfulBEqOrd α] [TransOrd α] {k : α} (h : t.WF) :
|
||||
t.keys.contains k = t.contains k := by
|
||||
rw [contains_eq_containsKey h.ordered, ← eq_beqOfOrd_of_lawfulBEqOrd]
|
||||
simp_to_model using (List.containsKey_eq_keys_contains (a := k) (l := t.toListModel)).symm
|
||||
|
||||
theorem mem_keys [LawfulEqOrd α] [TransOrd α] {k : α} (h : t.WF) :
|
||||
k ∈ t.keys ↔ k ∈ t := by
|
||||
simpa only [mem_iff_contains, ← List.contains_iff, ← Bool.eq_iff_iff] using contains_keys h
|
||||
|
||||
theorem distinct_keys [TransOrd α] (h : t.WF) :
|
||||
t.keys.Pairwise (fun a b => ¬ compare a b = .eq) := by
|
||||
simp only [← not_congr beq_iff_eq, ← beq_eq, Bool.not_eq_true]
|
||||
simp_to_model using h.ordered.distinctKeys.distinct
|
||||
|
||||
theorem map_fst_toList_eq_keys :
|
||||
t.toList.map Sigma.fst = t.keys := by
|
||||
simp_to_model using (List.keys_eq_map ..).symm
|
||||
|
||||
theorem length_toList [TransOrd α] (h : t.WF) :
|
||||
t.toList.length = t.size := by
|
||||
simp_to_model
|
||||
|
||||
theorem isEmpty_toList :
|
||||
t.toList.isEmpty = t.isEmpty := by
|
||||
simp_to_model
|
||||
|
||||
theorem mem_toList_iff_get?_eq_some [TransOrd α] [LawfulEqOrd α] {k : α} {v : β k} (h : t.WF) :
|
||||
⟨k, v⟩ ∈ t.toList ↔ t.get? k = some v := by
|
||||
simp_to_model using List.mem_iff_getValueCast?_eq_some
|
||||
|
||||
theorem find?_toList_eq_some_iff_get?_eq_some [TransOrd α] [LawfulEqOrd α] {k : α} {v : β k}
|
||||
(h : t.WF) :
|
||||
t.toList.find? (compare ·.1 k == .eq) = some ⟨k, v⟩ ↔ t.get? k = some v := by
|
||||
simp_to_model using List.find?_eq_some_iff_getValueCast?_eq_some
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransOrd α] {k : α} (h : t.WF) :
|
||||
t.toList.find? (compare ·.1 k == .eq) = none ↔ t.contains k = false := by
|
||||
simp_to_model using List.find?_eq_none_iff_containsKey_eq_false
|
||||
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransOrd α] {k : α} (h : t.WF) :
|
||||
t.toList.find? (compare ·.1 k == .eq) = none ↔ ¬ k ∈ t := by
|
||||
simpa only [Bool.not_eq_true, mem_iff_contains] using find?_toList_eq_none_iff_contains_eq_false h
|
||||
|
||||
theorem distinct_keys_toList [TransOrd α] (h : t.WF) :
|
||||
t.toList.Pairwise (fun a b => ¬ compare a.1 b.1 = .eq) := by
|
||||
simp only [← beq_iff, Bool.not_eq_true]
|
||||
simp_to_model using List.pairwise_fst_eq_false
|
||||
|
||||
namespace Const
|
||||
|
||||
variable {β : Type v} {t : Impl α β}
|
||||
|
||||
theorem map_fst_toList_eq_keys :
|
||||
(toList t).map Prod.fst = t.keys := by
|
||||
simp_to_model using List.map_fst_map_toProd_eq_keys
|
||||
|
||||
theorem length_toList (h : t.WF) :
|
||||
(toList t).length = t.size := by
|
||||
simp_to_model using List.length_map
|
||||
|
||||
theorem isEmpty_toList :
|
||||
(toList t).isEmpty = t.isEmpty := by
|
||||
rw [Bool.eq_iff_iff, List.isEmpty_iff, isEmpty_eq_isEmpty, List.isEmpty_iff]
|
||||
simp_to_model using List.map_eq_nil_iff
|
||||
|
||||
theorem mem_toList_iff_get?_eq_some [TransOrd α] [LawfulEqOrd α] {k : α} {v : β} (h : t.WF) :
|
||||
(k, v) ∈ toList t ↔ get? t k = some v := by
|
||||
simp_to_model using List.mem_map_toProd_iff_getValue?_eq_some
|
||||
|
||||
theorem mem_toList_iff_getKey?_eq_some_and_get?_eq_some [TransOrd α] {k : α} {v : β} (h : t.WF) :
|
||||
(k, v) ∈ toList t ↔ t.getKey? k = some k ∧ get? t k = some v := by
|
||||
simp_to_model using List.mem_map_toProd_iff_getKey?_eq_some_and_getValue?_eq_some
|
||||
|
||||
theorem get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList [TransOrd α] {k : α} {v : β} (h : t.WF) :
|
||||
get? t k = some v ↔ ∃ (k' : α), compare k k' = .eq ∧ (k', v) ∈ toList t := by
|
||||
simp_to_model using List.getValue?_eq_some_iff_exists_beq_and_mem_toList
|
||||
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some [TransOrd α] {k k' : α} {v : β}
|
||||
(h : t.WF) : (toList t).find? (fun a => compare a.1 k == .eq) = some ⟨k', v⟩ ↔
|
||||
t.getKey? k = some k' ∧ get? t k = some v := by
|
||||
simp_to_model using List.find?_map_toProd_eq_some_iff_getKey?_eq_some_and_getValue?_eq_some
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransOrd α] {k : α} (h : t.WF) :
|
||||
(toList t).find? (compare ·.1 k == .eq) = none ↔ t.contains k = false := by
|
||||
simp_to_model using List.find?_map_eq_none_iff_containsKey_eq_false
|
||||
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransOrd α] {k : α} (h : t.WF) :
|
||||
(toList t).find? (compare ·.1 k == .eq) = none ↔ ¬ k ∈ t := by
|
||||
simpa only [Bool.not_eq_true, mem_iff_contains] using find?_toList_eq_none_iff_contains_eq_false h
|
||||
|
||||
theorem distinct_keys_toList [TransOrd α] (h : t.WF) :
|
||||
(toList t).Pairwise (fun a b => ¬ compare a.1 b.1 = .eq) := by
|
||||
simp only [← beq_iff, Bool.not_eq_true]
|
||||
simp_to_model using List.pairwise_fst_eq_false_map_toProd
|
||||
|
||||
end Const
|
||||
|
||||
end Std.DTreeMap.Internal.Impl
|
||||
|
||||
@@ -605,6 +605,42 @@ theorem containsThenInsertIfNew!_snd_eq_insertIfNew! [Ord α] (t : Impl α β) (
|
||||
rw [containsThenInsertIfNew!, insertIfNew!]
|
||||
split <;> rfl
|
||||
|
||||
theorem insertMin_eq_insertMin! [Ord α] {a b} {t : Impl α β} (htb) :
|
||||
(t.insertMin a b htb).impl = t.insertMin! a b := by
|
||||
cases a, b, t using insertMin!.fun_cases
|
||||
· rfl
|
||||
· simp only [insertMin!, insertMin, balanceL_eq_balanceL!, insertMin_eq_insertMin! htb.left]
|
||||
|
||||
theorem insertMax_eq_insertMax! [Ord α] {a b} {t : Impl α β} (htb) :
|
||||
(t.insertMax a b htb).impl = t.insertMax! a b := by
|
||||
cases a, b, t using insertMax!.fun_cases
|
||||
· rfl
|
||||
· simp only [insertMax!, insertMax, balanceR_eq_balanceR!, insertMax_eq_insertMax! htb.right]
|
||||
|
||||
theorem link_eq_link! [Ord α] {k v} {l r : Impl α β} (hlb hrb) :
|
||||
(link k v l r hlb hrb).impl = link! k v l r := by
|
||||
cases k, v, l, r using link!.fun_cases <;> rw [link, link!]
|
||||
· rw [insertMin_eq_insertMin!]
|
||||
· rw [insertMax_eq_insertMax!]
|
||||
· split <;> simp only [balanceLErase_eq_balanceL!, link_eq_link! hlb hrb.left]
|
||||
· split <;> simp only [balanceRErase_eq_balanceR!, balanceLErase_eq_balanceL!,
|
||||
link_eq_link! hlb hrb.left, link_eq_link! hlb.right hrb]
|
||||
· split
|
||||
· simp only [balanceLErase_eq_balanceL!, link_eq_link! hlb hrb.left]
|
||||
· simp only [Std.Internal.tree_tac]
|
||||
termination_by sizeOf l + sizeOf r
|
||||
|
||||
theorem link2_eq_link2! [Ord α] {l r : Impl α β} (hlb hrb) :
|
||||
(link2 l r hlb hrb).impl = link2! l r := by
|
||||
cases l, r using link2!.fun_cases <;> rw [link2!, link2]
|
||||
· split <;> simp only [balanceLErase_eq_balanceL!, link2_eq_link2! hlb hrb.left]
|
||||
· split <;> simp only [balanceRErase_eq_balanceR!, balanceLErase_eq_balanceL!,
|
||||
link2_eq_link2! hlb.right hrb, link2_eq_link2! hlb hrb.left]
|
||||
· split
|
||||
· simp only [balanceLErase_eq_balanceL!, link2_eq_link2! hlb hrb.left]
|
||||
· simp only [Std.Internal.tree_tac, glue_eq_glue!]
|
||||
termination_by sizeOf l + sizeOf r
|
||||
|
||||
namespace Const
|
||||
|
||||
variable {β : Type v}
|
||||
|
||||
@@ -243,7 +243,7 @@ def link! (k : α) (v : β k) (l r : Impl α β) : Impl α β :=
|
||||
if delta * szl < szr then
|
||||
balanceL! k'' v'' (link! k v l l'') r''
|
||||
else if delta * szr < szl then
|
||||
balanceR! k' v' l' (link! k v r r')
|
||||
balanceR! k' v' l' (link! k v r' r)
|
||||
else
|
||||
.inner (l.size + 1 + r.size) k v l r
|
||||
termination_by sizeOf l + sizeOf r
|
||||
|
||||
@@ -198,16 +198,16 @@ def foldl (f : δ → (a : α) → β a → δ) (init : δ) (t : Impl α β) :
|
||||
|
||||
/-- Folds the given function over the mappings in the tree in descending order. -/
|
||||
@[specialize]
|
||||
def foldrM {m} [Monad m] (f : δ → (a : α) → β a → m δ) (init : δ) : Impl α β → m δ
|
||||
def foldrM {m} [Monad m] (f : (a : α) → β a → δ → m δ) (init : δ) : Impl α β → m δ
|
||||
| .leaf => pure init
|
||||
| .inner _ k v l r => do
|
||||
let right ← foldlM f init r
|
||||
let middle ← f right k v
|
||||
foldlM f middle l
|
||||
let right ← foldrM f init r
|
||||
let middle ← f k v right
|
||||
foldrM f middle l
|
||||
|
||||
/-- Folds the given function over the mappings in the tree in descending order. -/
|
||||
@[inline]
|
||||
def foldr (f : δ → (a : α) → β a → δ) (init : δ) (t : Impl α β) : δ :=
|
||||
def foldr (f : (a : α) → β a → δ → δ) (init : δ) (t : Impl α β) : δ :=
|
||||
Id.run (t.foldrM f init)
|
||||
|
||||
/-- Applies the given function to the mappings in the tree in ascending order. -/
|
||||
@@ -237,7 +237,7 @@ def forIn {m} [Monad m] (f : δ → (a : α) → β a → m (ForInStep δ)) (ini
|
||||
|
||||
/-- Returns a `List` of the keys in order. -/
|
||||
@[inline] def keys (t : Impl α β) : List α :=
|
||||
t.foldr (init := []) fun l k _ => k :: l
|
||||
t.foldr (init := []) fun k _ l => k :: l
|
||||
|
||||
/-- Returns an `Array` of the keys in order. -/
|
||||
@[inline] def keysArray (t : Impl α β) : Array α :=
|
||||
@@ -245,7 +245,7 @@ def forIn {m} [Monad m] (f : δ → (a : α) → β a → m (ForInStep δ)) (ini
|
||||
|
||||
/-- Returns a `List` of the values in order. -/
|
||||
@[inline] def values {β : Type v} (t : Impl α β) : List β :=
|
||||
t.foldr (init := []) fun l _ v => v :: l
|
||||
t.foldr (init := []) fun _ v l => v :: l
|
||||
|
||||
/-- Returns an `Array` of the values in order. -/
|
||||
@[inline] def valuesArray {β : Type v} (t : Impl α β) : Array β :=
|
||||
@@ -253,7 +253,7 @@ def forIn {m} [Monad m] (f : δ → (a : α) → β a → m (ForInStep δ)) (ini
|
||||
|
||||
/-- Returns a `List` of the key/value pairs in order. -/
|
||||
@[inline] def toList (t : Impl α β) : List ((a : α) × β a) :=
|
||||
t.foldr (init := []) fun l k v => ⟨k, v⟩ :: l
|
||||
t.foldr (init := []) fun k v l => ⟨k, v⟩ :: l
|
||||
|
||||
/-- Returns an `Array` of the key/value pairs in order. -/
|
||||
@[inline] def toArray (t : Impl α β) : Array ((a : α) × β a) :=
|
||||
@@ -265,7 +265,7 @@ variable {β : Type v}
|
||||
|
||||
/-- Returns a `List` of the key/value pairs in order. -/
|
||||
@[inline] def toList (t : Impl α β) : List (α × β) :=
|
||||
t.foldr (init := []) fun l k v => (k, v) :: l
|
||||
t.foldr (init := []) fun k v l => (k, v) :: l
|
||||
|
||||
/-- Returns a `List` of the key/value pairs in order. -/
|
||||
@[inline] def toArray (t : Impl α β) : Array (α × β) :=
|
||||
|
||||
@@ -797,7 +797,7 @@ theorem toListModel_insert [Ord α] [TransOrd α] {k : α} {v : β k} {l : Impl
|
||||
### `insert!`
|
||||
-/
|
||||
|
||||
theorem WF.insert! [Ord α] [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
theorem WF.insert! {_ : Ord α} [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
(h : l.WF) : (l.insert! k v).WF := by
|
||||
simpa [insert_eq_insert!] using WF.insert (h := h.balanced) h
|
||||
|
||||
@@ -843,7 +843,7 @@ theorem toListModel_erase [Ord α] [TransOrd α] {k : α} {t : Impl α β} (htb
|
||||
### `erase!`
|
||||
-/
|
||||
|
||||
theorem WF.erase! [Ord α] [TransOrd α] {k : α} {l : Impl α β}
|
||||
theorem WF.erase! {_ : Ord α} [TransOrd α] {k : α} {l : Impl α β}
|
||||
(h : l.WF) : (l.erase! k).WF := by
|
||||
simpa [erase_eq_erase!] using WF.erase (h := h.balanced) h
|
||||
|
||||
@@ -883,7 +883,7 @@ theorem toListModel_containsThenInsert [Ord α] [TransOrd α] {k : α} {v : β k
|
||||
### containsThenInsert!
|
||||
-/
|
||||
|
||||
theorem WF.containsThenInsert! [Ord α] [TransOrd α] {k : α} {v : β k} {t : Impl α β} (h : t.WF) :
|
||||
theorem WF.containsThenInsert! {_ : Ord α} [TransOrd α] {k : α} {v : β k} {t : Impl α β} (h : t.WF) :
|
||||
(t.containsThenInsert! k v).2.WF := by
|
||||
simpa [containsThenInsert!_snd_eq_containsThenInsert_snd, h.balanced] using WF.containsThenInsert (h := h.balanced) h
|
||||
|
||||
@@ -919,7 +919,7 @@ theorem ordered_insertIfNew! [Ord α] [TransOrd α] {k : α} {v : β k} {l : Imp
|
||||
(h : l.Balanced) (ho : l.Ordered) : (l.insertIfNew! k v).Ordered := by
|
||||
simpa [insertIfNew_eq_insertIfNew!] using ordered_insertIfNew h ho
|
||||
|
||||
theorem WF.insertIfNew! [Ord α] [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
theorem WF.insertIfNew! {_ : Ord α} [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
(h : l.WF) : (l.insertIfNew! k v).WF := by
|
||||
simpa [insertIfNew_eq_insertIfNew!] using h.insertIfNew (h := h.balanced)
|
||||
|
||||
@@ -950,7 +950,7 @@ theorem ordered_containsThenInsertIfNew! [Ord α] [TransOrd α] {k : α} {v : β
|
||||
(h : l.Balanced) (ho : l.Ordered) : (l.containsThenInsertIfNew! k v).2.Ordered := by
|
||||
simpa [containsThenInsertIfNew!_snd_eq_insertIfNew!] using ordered_insertIfNew! h ho
|
||||
|
||||
theorem WF.containsThenInsertIfNew! [Ord α] [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
theorem WF.containsThenInsertIfNew! {_ : Ord α} [TransOrd α] {k : α} {v : β k} {l : Impl α β}
|
||||
(h : l.WF) : (l.containsThenInsertIfNew! k v).2.WF := by
|
||||
simpa [containsThenInsertIfNew!_snd_eq_insertIfNew!] using WF.insertIfNew! (h := h)
|
||||
|
||||
@@ -1055,6 +1055,26 @@ theorem ordered_alter [Ord α] [TransOrd α] [LawfulEqOrd α] {t : Impl α β} {
|
||||
rw [alter_eq_alterₘ htb hto, alterₘ]
|
||||
exact ordered_updateAtKey htb hto
|
||||
|
||||
/-!
|
||||
### alter!
|
||||
-/
|
||||
|
||||
theorem alter_eq_alter! [Ord α] [LawfulEqOrd α] {t : Impl α β} {a f} (htb) :
|
||||
(alter a f t htb).impl = alter! a f t := by
|
||||
induction t with
|
||||
| leaf =>
|
||||
rw [alter, alter!]
|
||||
cases f none <;> rfl
|
||||
| inner sz k' v' l' r' ihl ihr =>
|
||||
rw [alter, alter!]
|
||||
split
|
||||
case h_1 => simp only [balance_eq_balance!, ihl htb.left]
|
||||
case h_2 => simp only [balance_eq_balance!, ihr htb.right]
|
||||
case h_3 =>
|
||||
cases f (some _)
|
||||
· exact glue_eq_glue!
|
||||
· rfl
|
||||
|
||||
/-!
|
||||
### modify
|
||||
-/
|
||||
@@ -1086,10 +1106,88 @@ theorem ordered_mergeWith [Ord α] [TransOrd α] [LawfulEqOrd α] {t₁ t₂ : I
|
||||
| leaf => exact hto
|
||||
| inner sz k v l r ihl ihr => exact ihr _ (ordered_alter _ (ihl htb hto))
|
||||
|
||||
/-!
|
||||
### foldlM
|
||||
-/
|
||||
|
||||
theorem foldlM_eq_foldlM {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
|
||||
{f : δ → (a : α) → β a → m δ} {init} :
|
||||
t.foldlM (init := init) f = t.toListModel.foldlM (init := init) fun acc p => f acc p.1 p.2 := by
|
||||
induction t generalizing init with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr =>
|
||||
simp only [foldlM, toListModel_inner, List.foldl_append, List.foldl_cons]
|
||||
simp [ihl, ihr]
|
||||
|
||||
/-!
|
||||
### foldl
|
||||
-/
|
||||
|
||||
theorem foldl_eq_foldl {t : Impl α β} {δ} {f : δ → (a : α) → β a → δ} {init} :
|
||||
t.foldl (init := init) f = t.toListModel.foldl (init := init) fun acc p => f acc p.1 p.2 := by
|
||||
rw [foldl, foldlM_eq_foldlM, List.foldl_eq_foldlM, Id.run]
|
||||
|
||||
/-!
|
||||
### foldrM
|
||||
-/
|
||||
|
||||
theorem foldrM_eq_foldrM {t : Impl α β} {m δ} [Monad m] [LawfulMonad m]
|
||||
{f : (a : α) → β a → δ → m δ} {init} :
|
||||
t.foldrM (init := init) f = t.toListModel.foldrM (init := init) fun p acc => f p.1 p.2 acc := by
|
||||
induction t generalizing init with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr =>
|
||||
simp only [foldrM, toListModel_inner, List.foldr_append, List.foldr_cons]
|
||||
simp [ihl, ihr]
|
||||
|
||||
/-!
|
||||
### foldr
|
||||
-/
|
||||
|
||||
theorem foldr_eq_foldr {t : Impl α β} {δ} {f : (a : α) → β a → δ → δ} {init} :
|
||||
t.foldr (init := init) f = t.toListModel.foldr (init := init) fun p acc => f p.1 p.2 acc := by
|
||||
rw [foldr, foldrM_eq_foldrM, List.foldr_eq_foldrM, Id.run]
|
||||
|
||||
/-!
|
||||
### toList
|
||||
-/
|
||||
|
||||
theorem toList_eq_toListModel {t : Impl α β} :
|
||||
t.toList = t.toListModel := by
|
||||
rw [toList, foldr_eq_foldr]
|
||||
induction t with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr => simp
|
||||
|
||||
/-!
|
||||
### keys
|
||||
-/
|
||||
|
||||
theorem keys_eq_keys {t : Impl α β} :
|
||||
t.keys = t.toListModel.keys := by
|
||||
rw [keys, foldr_eq_foldr, List.keys.eq_def]
|
||||
simp
|
||||
induction t.toListModel with
|
||||
| nil => rfl
|
||||
| cons e es ih =>
|
||||
simp [ih]
|
||||
rw [List.keys.eq_def]
|
||||
|
||||
namespace Const
|
||||
|
||||
variable {β : Type v}
|
||||
|
||||
/-!
|
||||
### getThenInsertIfNew?!
|
||||
-/
|
||||
|
||||
theorem WF.getThenInsertIfNew?! [Ord α] [TransOrd α] [LawfulEqOrd α] {k : α} {v : β} {t : Impl α β}
|
||||
(h : t.WF) : (getThenInsertIfNew?! t k v).2.WF := by
|
||||
rw [getThenInsertIfNew?!.eq_def]
|
||||
cases get? t k
|
||||
· exact h.insertIfNew!
|
||||
· exact h
|
||||
|
||||
/-!
|
||||
### alter
|
||||
-/
|
||||
@@ -1141,6 +1239,26 @@ theorem ordered_alter [Ord α] [TransOrd α] {t : Impl α β} {a f}
|
||||
rw [alter_eq_alterₘ htb hto, alterₘ]
|
||||
exact ordered_updateAtKey htb hto
|
||||
|
||||
/-!
|
||||
### alter!
|
||||
-/
|
||||
|
||||
theorem alter_eq_alter! [Ord α] {t : Impl α β} {a f} (htb) :
|
||||
(alter a f t htb).impl = alter! a f t := by
|
||||
induction t with
|
||||
| leaf =>
|
||||
rw [alter, alter!]
|
||||
cases f none <;> rfl
|
||||
| inner sz k' v' l' r' ihl ihr =>
|
||||
rw [alter, alter!]
|
||||
cases compare a k'
|
||||
case lt => simp only [balance_eq_balance!, ihl htb.left]
|
||||
case gt => simp only [balance_eq_balance!, ihr htb.right]
|
||||
case eq =>
|
||||
cases f (some v')
|
||||
· exact glue_eq_glue!
|
||||
· rfl
|
||||
|
||||
/-!
|
||||
### modify
|
||||
-/
|
||||
@@ -1173,6 +1291,17 @@ theorem ordered_mergeWith [Ord α] [TransOrd α] {t₁ t₂ : Impl α β} {f}
|
||||
| leaf => exact hto
|
||||
| inner sz k v l r ihl ihr => exact ihr _ (ordered_alter _ (ihl htb hto))
|
||||
|
||||
/-!
|
||||
### toList
|
||||
-/
|
||||
|
||||
theorem toList_eq_toListModel_map {t : Impl α β} :
|
||||
Const.toList t = t.toListModel.map fun ⟨k, v⟩ => (k, v) := by
|
||||
rw [toList, foldr_eq_foldr]
|
||||
induction t with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr => simp
|
||||
|
||||
end Const
|
||||
|
||||
/-!
|
||||
@@ -1252,6 +1381,108 @@ theorem wf [Ord α] {t : Impl α β} {t' : Impl α β'} (hs : SameKeys t t') (h
|
||||
|
||||
end SameKeys
|
||||
|
||||
/-!
|
||||
### getThenInsertIfNew?!
|
||||
-/
|
||||
|
||||
theorem WF.getThenInsertIfNew?! {_ : Ord α} [TransOrd α] [LawfulEqOrd α] {k : α} {v : β k} {t : Impl α β}
|
||||
(h : t.WF) : (t.getThenInsertIfNew?! k v).2.WF := by
|
||||
rw [getThenInsertIfNew?!.eq_def]
|
||||
cases get? t k
|
||||
· exact h.insertIfNew!
|
||||
· exact h
|
||||
|
||||
theorem WF.constGetThenInsertIfNew?! {β : Type v} {_ : Ord α} [TransOrd α] {k : α} {v : β} {t : Impl α β}
|
||||
(h : t.WF) : (Const.getThenInsertIfNew?! t k v).2.WF := by
|
||||
rw [Const.getThenInsertIfNew?!.eq_def]
|
||||
cases Const.get? t k
|
||||
· exact h.insertIfNew!
|
||||
· exact h
|
||||
|
||||
/-!
|
||||
### `eraseMany!`
|
||||
-/
|
||||
|
||||
theorem WF.eraseMany! {_ : Ord α} [TransOrd α] {ρ} [ForIn Id ρ α] {l : ρ}
|
||||
{t : Impl α β} (h : t.WF) : (t.eraseMany! l).1.WF :=
|
||||
(t.eraseMany! l).2 h (fun _ _ h' => h'.erase!)
|
||||
|
||||
/-!
|
||||
### `insertMany!`
|
||||
-/
|
||||
|
||||
theorem WF.insertMany! {_ : Ord α} [TransOrd α] {ρ} [ForIn Id ρ ((a : α) × β a)] {l : ρ}
|
||||
{t : Impl α β} (h : t.WF) : (t.insertMany! l).1.WF :=
|
||||
(t.insertMany! l).2 h (fun _ _ _ h' => h'.insert!)
|
||||
|
||||
theorem WF.constInsertMany! {β : Type v} {_ : Ord α} [TransOrd α] {ρ} [ForIn Id ρ (α × β)] {l : ρ}
|
||||
{t : Impl α β} (h : t.WF) : (Const.insertMany! t l).1.WF :=
|
||||
(Const.insertMany! t l).2 h (fun _ _ _ h' => h'.insert!)
|
||||
|
||||
theorem WF.constInsertManyIfNewUnit! {_ : Ord α} [TransOrd α] {ρ} [ForIn Id ρ α] {l : ρ}
|
||||
{t : Impl α Unit} (h : t.WF) : (Const.insertManyIfNewUnit! t l).1.WF :=
|
||||
(Const.insertManyIfNewUnit! t l).2 h (fun _ _ h' => h'.insertIfNew!)
|
||||
|
||||
/-!
|
||||
### alter!
|
||||
-/
|
||||
|
||||
theorem WF.alter! {_ : Ord α} [LawfulEqOrd α] {t : Impl α β} {a f} (h : t.WF) :
|
||||
(alter! a f t).WF := by
|
||||
rw [← alter_eq_alter! h.balanced]
|
||||
exact h.alter
|
||||
|
||||
theorem WF.constAlter! {_ : Ord α} {β : Type v} {t : Impl α β} {a f} (h : t.WF) :
|
||||
(Const.alter! a f t).WF := by
|
||||
rw [← Const.alter_eq_alter! h.balanced]
|
||||
exact h.constAlter
|
||||
|
||||
/-!
|
||||
### mergeWith!
|
||||
-/
|
||||
|
||||
theorem mergeWith_eq_mergeWith! {_ : Ord α} [LawfulEqOrd α] {mergeFn} {t₁ t₂ : Impl α β}
|
||||
(h : t₁.Balanced) :
|
||||
(mergeWith mergeFn t₁ t₂ h).impl = mergeWith! mergeFn t₁ t₂ := by
|
||||
rw [mergeWith, mergeWith!]
|
||||
induction t₂ generalizing t₁ with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr =>
|
||||
simp only [foldl, foldlM, Id.run, bind]
|
||||
simp only [foldl, Id.run, bind] at ihl ihr
|
||||
rw [ihr]
|
||||
congr
|
||||
simp only [SizedBalancedTree.toBalancedTree]
|
||||
rw [alter_eq_alter!]
|
||||
congr
|
||||
exact ihl h
|
||||
|
||||
theorem WF.mergeWith! {_ : Ord α} [LawfulEqOrd α] {mergeFn} {t₁ t₂ : Impl α β} (h : t₁.WF) :
|
||||
(Impl.mergeWith! mergeFn t₁ t₂).WF := by
|
||||
rw [← mergeWith_eq_mergeWith! h.balanced]
|
||||
exact h.mergeWith
|
||||
|
||||
theorem Const.mergeWith_eq_mergeWith! {β : Type v} {_ : Ord α} {mergeFn} {t₁ t₂ : Impl α β}
|
||||
(h : t₁.Balanced) :
|
||||
(mergeWith mergeFn t₁ t₂ h).impl = mergeWith! mergeFn t₁ t₂ := by
|
||||
rw [mergeWith, mergeWith!]
|
||||
induction t₂ generalizing t₁ with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr =>
|
||||
simp only [foldl, foldlM, Id.run, bind]
|
||||
simp only [foldl, Id.run, bind] at ihl ihr
|
||||
rw [ihr]
|
||||
congr
|
||||
simp only [SizedBalancedTree.toBalancedTree]
|
||||
rw [alter_eq_alter!]
|
||||
congr
|
||||
exact ihl h
|
||||
|
||||
theorem WF.constMergeWith! {β : Type v} {_ : Ord α} {mergeFn} {t₁ t₂ : Impl α β} (h : t₁.WF) :
|
||||
(Impl.Const.mergeWith! mergeFn t₁ t₂).WF := by
|
||||
rw [← Const.mergeWith_eq_mergeWith! h.balanced]
|
||||
exact h.constMergeWith
|
||||
|
||||
/-!
|
||||
### filterMap
|
||||
-/
|
||||
@@ -1260,6 +1491,44 @@ theorem WF.filterMap [Ord α] {t : Impl α β} {h} {f : (a : α) → β a → Op
|
||||
(t.filterMap f h).impl.WF :=
|
||||
.wf balanced_filterMap (ordered_filterMap hwf.ordered)
|
||||
|
||||
/-!
|
||||
### filterMap!
|
||||
-/
|
||||
|
||||
theorem filterMap_eq_filterMap! [Ord α] {t : Impl α β} {h} {f : (a : α) → β a → Option (γ a)} :
|
||||
(t.filterMap f h).impl = t.filterMap! f := by
|
||||
induction t with
|
||||
| leaf => rfl
|
||||
| inner sz k v _ _ ihl ihr =>
|
||||
simp [filterMap, filterMap!]
|
||||
cases f k v
|
||||
· simp only [link2_eq_link2!, ihl, ihr, h.left, h.right]
|
||||
· simp only [link_eq_link!, ihl, ihr, h.left, h.right]
|
||||
|
||||
theorem WF.filterMap! {_ : Ord α} {t : Impl α β} {f : (a : α) → β a → Option (γ a)} (h : t.WF) :
|
||||
(t.filterMap! f).WF := by
|
||||
rw [← filterMap_eq_filterMap! (h := h.balanced)]
|
||||
exact h.filterMap
|
||||
|
||||
/-!
|
||||
### filter!
|
||||
-/
|
||||
|
||||
theorem filter_eq_filter! [Ord α] {t : Impl α β} {h} {f : (a : α) → β a → Bool} :
|
||||
(t.filter f h).impl = t.filter! f := by
|
||||
induction t with
|
||||
| leaf => rfl
|
||||
| inner sz k v l r ihl ihr =>
|
||||
simp only [filter!, filter]
|
||||
split
|
||||
· simp only [ihl, ihr, link2_eq_link2!, h.left, h.right]
|
||||
· simp only [ihl, ihr, link_eq_link!, h.left, h.right]
|
||||
|
||||
theorem WF.filter! {_ : Ord α} {t : Impl α β} {f : (a : α) → β a → Bool} (h : t.WF) :
|
||||
(t.filter! f).WF := by
|
||||
rw [← filter_eq_filter! (h := h.balanced)]
|
||||
exact h.filter
|
||||
|
||||
/-!
|
||||
### map
|
||||
-/
|
||||
|
||||
@@ -922,4 +922,118 @@ theorem getThenInsertIfNew?_snd [TransCmp cmp] {k : α} {v : β} :
|
||||
|
||||
end Const
|
||||
|
||||
@[simp]
|
||||
theorem length_keys [TransCmp cmp] :
|
||||
t.keys.length = t.size :=
|
||||
Impl.length_keys t.wf
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_keys :
|
||||
t.keys.isEmpty = t.isEmpty :=
|
||||
Impl.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_keys [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
t.keys.contains k = t.contains k :=
|
||||
Impl.contains_keys t.wf
|
||||
|
||||
@[simp]
|
||||
theorem mem_keys [LawfulEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
k ∈ t.keys ↔ k ∈ t :=
|
||||
Impl.mem_keys t.wf
|
||||
|
||||
theorem distinct_keys [TransCmp cmp] :
|
||||
t.keys.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
Impl.distinct_keys t.wf
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
t.toList.map Sigma.fst = t.keys :=
|
||||
Impl.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [TransCmp cmp] :
|
||||
t.toList.length = t.size :=
|
||||
Impl.length_toList t.wf
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
t.toList.isEmpty = t.isEmpty :=
|
||||
Impl.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] {k : α} {v : β k} :
|
||||
⟨k, v⟩ ∈ t.toList ↔ t.get? k = some v :=
|
||||
Impl.mem_toList_iff_get?_eq_some t.wf
|
||||
|
||||
theorem find?_toList_eq_some_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] {k : α} {v : β k} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = some ⟨k, v⟩ ↔ t.get? k = some v :=
|
||||
Impl.find?_toList_eq_some_iff_get?_eq_some t.wf
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] {k : α} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
Impl.find?_toList_eq_none_iff_contains_eq_false t.wf
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] {k : α} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t := by
|
||||
simpa only [Bool.not_eq_true, mem_iff_contains] using find?_toList_eq_none_iff_contains_eq_false
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] :
|
||||
t.toList.Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
Impl.distinct_keys_toList t.wf
|
||||
|
||||
namespace Const
|
||||
|
||||
variable {β : Type v} {t : DTreeMap α β cmp}
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
(toList t).map Prod.fst = t.keys :=
|
||||
Impl.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList :
|
||||
(toList t).length = t.size :=
|
||||
Impl.Const.length_toList t.wf
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
(toList t).isEmpty = t.isEmpty :=
|
||||
Impl.Const.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ get? t k = some v :=
|
||||
Impl.Const.mem_toList_iff_get?_eq_some t.wf
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getKey?_eq_some_and_get?_eq_some [TransCmp cmp] {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t.getKey? k = some k ∧ get? t k = some v :=
|
||||
Impl.Const.mem_toList_iff_getKey?_eq_some_and_get?_eq_some t.wf
|
||||
|
||||
theorem get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList [TransCmp cmp] {k : α} {v : β} :
|
||||
get? t k = some v ↔ ∃ (k' : α), cmp k k' = .eq ∧ (k', v) ∈ toList t :=
|
||||
Impl.Const.get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList t.wf
|
||||
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some [TransCmp cmp] {k k' : α} {v : β} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = some ⟨k', v⟩ ↔
|
||||
t.getKey? k = some k' ∧ get? t k = some v :=
|
||||
Impl.Const.find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some t.wf
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
Impl.Const.find?_toList_eq_none_iff_contains_eq_false t.wf
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t :=
|
||||
Impl.Const.find?_toList_eq_none_iff_not_mem t.wf
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] :
|
||||
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
Impl.Const.distinct_keys_toList t.wf
|
||||
|
||||
end Const
|
||||
|
||||
end Std.DTreeMap
|
||||
|
||||
44
src/Std/Data/DTreeMap/Raw/AdditionalOperations.lean
Normal file
44
src/Std/Data/DTreeMap/Raw/AdditionalOperations.lean
Normal file
@@ -0,0 +1,44 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Raw.Basic
|
||||
import Std.Data.DTreeMap.AdditionalOperations
|
||||
|
||||
/-!
|
||||
# Additional dependent raw tree map operations
|
||||
|
||||
This file defines more operations on `Std.DTreeMap.Raw`.
|
||||
We currently do not provide lemmas for these functions.
|
||||
-/
|
||||
|
||||
set_option autoImplicit false
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
variable {α : Type u} {β : α → Type v} {γ : α → Type w} {cmp : α → α → Ordering}
|
||||
private local instance : Coe (Type v) (α → Type v) where coe γ := fun _ => γ
|
||||
|
||||
namespace Std.DTreeMap
|
||||
open Internal (Impl)
|
||||
|
||||
namespace Raw
|
||||
|
||||
@[inline, inherit_doc DTreeMap.filterMap]
|
||||
def filterMap (f : (a : α) → β a → Option (γ a)) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.filterMap! f⟩
|
||||
|
||||
@[inline, inherit_doc DTreeMap.map]
|
||||
def map (f : (a : α) → β a → γ a) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨t.inner.map f⟩
|
||||
|
||||
/-!
|
||||
We do not provide `get*GE`, `get*GT`, `get*LE` and `get*LT` functions for the raw trees.
|
||||
-/
|
||||
|
||||
end Raw
|
||||
|
||||
end Std.DTreeMap
|
||||
@@ -18,7 +18,7 @@ available as `Std.DTreeMap.Raw.WF` and we prove in this file that all operations
|
||||
well-formedness. When in doubt, prefer `DTreeMap` over `DTreeMap.Raw`.
|
||||
|
||||
Lemmas about the operations on `Std.DTreeMap.Raw` will be available in the module
|
||||
`Std.Data.DTreeMap.RawLemmas`.
|
||||
`Std.Data.DTreeMap.Raw.Lemmas`.
|
||||
-/
|
||||
|
||||
set_option autoImplicit false
|
||||
@@ -38,7 +38,7 @@ open Internal (Impl)
|
||||
Dependent tree maps without a bundled well-formedness invariant, suitable for use in nested
|
||||
inductive types. The well-formedness invariant is called `Raw.WF`. When in doubt, prefer `DTreeMap`
|
||||
over `DTreeMap.Raw`. Lemmas about the operations on `Std.DTreeMap.Raw` are available in the
|
||||
module `Std.Data.DTreeMap.RawLemmas`.
|
||||
module `Std.Data.DTreeMap.Raw.Lemmas`.
|
||||
|
||||
A tree map stores an assignment of keys to values. It depends on a comparator function that
|
||||
defines an ordering on the keys and provides efficient order-dependent queries, such as retrieval
|
||||
@@ -559,16 +559,16 @@ def fold (f : δ → (a : α) → β a → δ) (init : δ) (t : Raw α β cmp) :
|
||||
t.foldl f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.foldrM]
|
||||
def foldrM (f : δ → (a : α) → β a → m δ) (init : δ) (t : Raw α β cmp) : m δ :=
|
||||
def foldrM (f : (a : α) → β a → δ → m δ) (init : δ) (t : Raw α β cmp) : m δ :=
|
||||
t.inner.foldrM f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.foldr]
|
||||
def foldr (f : δ → (a : α) → β a → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
def foldr (f : (a : α) → β a → δ → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
t.inner.foldr f init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → β a → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun k v acc => f acc k v) init t
|
||||
|
||||
@[inline, inherit_doc DTreeMap.partition]
|
||||
def partition (f : (a : α) → β a → Bool) (t : Raw α β cmp) : Raw α β cmp × Raw α β cmp :=
|
||||
@@ -5,13 +5,14 @@ Authors: Markus Himmel, Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Internal.Lemmas
|
||||
import Std.Data.DTreeMap.Raw
|
||||
import Std.Data.DTreeMap.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Dependent tree map lemmas
|
||||
|
||||
This file contains lemmas about `Std.Data.DTreeMap.Raw`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
This file contains lemmas about `Std.Data.DTreeMap.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp` and a proof that the involved maps are well-formed.
|
||||
These proofs can be obtained from `Std.Data.DTreeMap.Raw.WF`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -929,4 +930,119 @@ theorem getThenInsertIfNew?_snd [TransCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
|
||||
end Const
|
||||
|
||||
@[simp]
|
||||
theorem length_keys [TransCmp cmp] (h : t.WF) :
|
||||
t.keys.length = t.size :=
|
||||
Impl.length_keys h.out
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_keys :
|
||||
t.keys.isEmpty = t.isEmpty :=
|
||||
Impl.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_keys [BEq α] [LawfulBEqCmp cmp] (h : t.WF) [TransCmp cmp] {k : α} :
|
||||
t.keys.contains k = t.contains k :=
|
||||
Impl.contains_keys h
|
||||
|
||||
@[simp]
|
||||
theorem mem_keys [LawfulEqCmp cmp] [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
k ∈ t.keys ↔ k ∈ t :=
|
||||
Impl.mem_keys h
|
||||
|
||||
theorem distinct_keys [TransCmp cmp] (h : t.WF) :
|
||||
t.keys.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
Impl.distinct_keys h.out
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
t.toList.map Sigma.fst = t.keys :=
|
||||
Impl.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [TransCmp cmp] (h : t.WF) :
|
||||
t.toList.length = t.size :=
|
||||
Impl.length_toList h.out
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
t.toList.isEmpty = t.isEmpty :=
|
||||
Impl.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] (h : t.WF) {k : α} {v : β k} :
|
||||
⟨k, v⟩ ∈ t.toList ↔ t.get? k = some v :=
|
||||
Impl.mem_toList_iff_get?_eq_some h.out
|
||||
|
||||
theorem find?_toList_eq_some_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] (h : t.WF) {k : α}
|
||||
{v : β k} : t.toList.find? (cmp ·.1 k == .eq) = some ⟨k, v⟩ ↔ t.get? k = some v :=
|
||||
Impl.find?_toList_eq_some_iff_get?_eq_some h.out
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
Impl.find?_toList_eq_none_iff_contains_eq_false h.out
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t := by
|
||||
simpa only [Bool.not_eq_true, mem_iff_contains] using find?_toList_eq_none_iff_contains_eq_false h
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] (h : t.WF) :
|
||||
t.toList.Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
Impl.distinct_keys_toList h.out
|
||||
|
||||
namespace Const
|
||||
|
||||
variable {β : Type v} {t : Raw α β cmp}
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
(toList t).map Prod.fst = t.keys :=
|
||||
Impl.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList (h : t.WF) :
|
||||
(toList t).length = t.size :=
|
||||
Impl.Const.length_toList h.out
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
(toList t).isEmpty = t.isEmpty :=
|
||||
Impl.Const.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_get?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ get? t k = some v :=
|
||||
Impl.Const.mem_toList_iff_get?_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getKey?_eq_some_and_get?_eq_some [TransCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t.getKey? k = some k ∧ get? t k = some v :=
|
||||
Impl.Const.mem_toList_iff_getKey?_eq_some_and_get?_eq_some h
|
||||
|
||||
theorem get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList [TransCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
get? t k = some v ↔ ∃ (k' : α), cmp k k' = .eq ∧ (k', v) ∈ toList t :=
|
||||
Impl.Const.get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList h
|
||||
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some [TransCmp cmp] (h : t.WF)
|
||||
{k k' : α} {v : β} :
|
||||
(toList t).find? (fun a => cmp a.1 k = .eq) = some ⟨k', v⟩ ↔
|
||||
t.getKey? k = some k' ∧ get? t k = some v :=
|
||||
Impl.Const.find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some h.out
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
Impl.Const.find?_toList_eq_none_iff_contains_eq_false h.out
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t :=
|
||||
Impl.Const.find?_toList_eq_none_iff_not_mem h.out
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] (h : t.WF) :
|
||||
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
Impl.Const.distinct_keys_toList h.out
|
||||
|
||||
end Const
|
||||
|
||||
end Std.DTreeMap.Raw
|
||||
157
src/Std/Data/DTreeMap/Raw/WF.lean
Normal file
157
src/Std/Data/DTreeMap/Raw/WF.lean
Normal file
@@ -0,0 +1,157 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Internal.Lemmas
|
||||
import Std.Data.DTreeMap.Raw.AdditionalOperations
|
||||
import Std.Data.DTreeMap.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Well-formedness proofs for raw dependent tree maps
|
||||
|
||||
This file contains well-formedness proofs about `Std.Data.DTreeMap.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
set_option autoImplicit false
|
||||
|
||||
open Std.DTreeMap.Internal
|
||||
|
||||
universe u v
|
||||
|
||||
namespace Std.DTreeMap.Raw.WF
|
||||
|
||||
variable {α : Type u} {β : α → Type v} {cmp : α → α → Ordering} {t : Raw α β cmp}
|
||||
private local instance : Coe (Type v) (α → Type v) where coe γ := fun _ => γ
|
||||
|
||||
theorem empty : (empty : Raw α β cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.empty⟩
|
||||
|
||||
theorem emptyc : (∅ : Raw α β cmp).WF :=
|
||||
empty
|
||||
|
||||
theorem erase [TransCmp cmp] {a} (h : t.WF) : WF (t.erase a) :=
|
||||
⟨h.out.erase!⟩
|
||||
|
||||
theorem insert [TransCmp cmp] {a b} (h : t.WF) : WF (t.insert a b) :=
|
||||
⟨h.out.insert!⟩
|
||||
|
||||
theorem insertIfNew [TransCmp cmp] {a b} (h : t.WF) : WF (t.insertIfNew a b) :=
|
||||
⟨h.out.insertIfNew!⟩
|
||||
|
||||
theorem containsThenInsert [TransCmp cmp] {a b} (h : t.WF) : WF (t.containsThenInsert a b).2 :=
|
||||
⟨h.out.containsThenInsert!⟩
|
||||
|
||||
theorem containsThenInsertIfNew [TransCmp cmp] {a b} (h : t.WF) : WF (t.containsThenInsertIfNew a b).2 :=
|
||||
⟨h.out.containsThenInsertIfNew!⟩
|
||||
|
||||
theorem getThenInsertIfNew? [TransCmp cmp] [LawfulEqCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.getThenInsertIfNew? a b).2 :=
|
||||
⟨h.out.getThenInsertIfNew?!⟩
|
||||
|
||||
theorem filter [TransCmp cmp] {f} (h : t.WF) :
|
||||
WF (t.filter f) :=
|
||||
⟨h.out.filter!⟩
|
||||
|
||||
theorem filterMap [TransCmp cmp] {f : (a : α) → β a → Option (β a)} (h : t.WF) :
|
||||
WF (t.filterMap f) :=
|
||||
⟨h.out.filterMap!⟩
|
||||
|
||||
theorem partition_fst [TransCmp cmp] {f} :
|
||||
WF (t.partition f).fst := by
|
||||
rw [partition, foldl, Impl.foldl_eq_foldl, ← List.foldr_reverse]
|
||||
induction t.inner.toListModel.reverse with
|
||||
| nil => exact empty
|
||||
| cons e es ih =>
|
||||
simp only [List.foldr_cons]
|
||||
split
|
||||
· exact ih.insert
|
||||
· exact ih
|
||||
|
||||
theorem partition_snd [TransCmp cmp] {f} :
|
||||
WF (t.partition f).snd := by
|
||||
rw [partition, foldl, Impl.foldl_eq_foldl, ← List.foldr_reverse]
|
||||
induction t.inner.toListModel.reverse with
|
||||
| nil => exact empty
|
||||
| cons e es ih =>
|
||||
simp only [List.foldr_cons]
|
||||
split
|
||||
· exact ih
|
||||
· exact ih.insert
|
||||
|
||||
theorem eraseMany [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α β cmp}
|
||||
(h : t.WF) : WF (t.eraseMany l) :=
|
||||
⟨h.out.eraseMany!⟩
|
||||
|
||||
theorem insertMany [TransCmp cmp] {ρ} [ForIn Id ρ ((a : α) × β a)] {l : ρ} {t : Raw α β cmp}
|
||||
(h : t.WF) : WF (t.insertMany l) :=
|
||||
⟨h.out.insertMany!⟩
|
||||
|
||||
theorem ofList [TransCmp cmp] {l : List ((a : α) × β a)} :
|
||||
(Raw.ofList l cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.insertMany Impl.WF.empty⟩
|
||||
|
||||
theorem ofArray [TransCmp cmp] {a : Array ((a : α) × β a)} :
|
||||
(Raw.ofArray a cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.insertMany Impl.WF.empty⟩
|
||||
|
||||
theorem alter [LawfulEqCmp cmp] {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(t.alter a f).WF :=
|
||||
⟨h.out.alter! (t := t.inner) (a := a) (f := f)⟩
|
||||
|
||||
theorem modify [LawfulEqCmp cmp] {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(t.modify a f).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨h.out.modify⟩
|
||||
|
||||
theorem mergeWith [LawfulEqCmp cmp] {mergeFn} {t₁ t₂ : Raw α β cmp} (h : t₁.WF) :
|
||||
(t₁.mergeWith mergeFn t₂).WF :=
|
||||
⟨h.out.mergeWith!⟩
|
||||
|
||||
section Const
|
||||
|
||||
variable {β : Type v} {t : Raw α β cmp}
|
||||
|
||||
theorem constGetThenInsertIfNew? [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (Raw.Const.getThenInsertIfNew? t a b).2 :=
|
||||
⟨h.out.constGetThenInsertIfNew?!⟩
|
||||
|
||||
theorem constInsertMany [TransCmp cmp] {ρ} [ForIn Id ρ (α × β)] {l : ρ} {t : Raw α β cmp}
|
||||
(h : t.WF) : WF (Const.insertMany t l) :=
|
||||
⟨h.out.constInsertMany!⟩
|
||||
|
||||
theorem constInsertManyIfNewUnit [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α Unit cmp}
|
||||
(h : t.WF) : WF (Const.insertManyIfNewUnit t l) :=
|
||||
⟨h.out.constInsertManyIfNewUnit!⟩
|
||||
|
||||
theorem constOfList [TransCmp cmp] {l : List (α × β)} :
|
||||
(Raw.Const.ofList l cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.constInsertMany Impl.WF.empty⟩
|
||||
|
||||
theorem constOfArray [TransCmp cmp] {a : Array (α × β)} :
|
||||
(Raw.Const.ofArray a cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.constInsertMany Impl.WF.empty⟩
|
||||
|
||||
theorem unitOfList [TransCmp cmp] {l : List α} :
|
||||
(Raw.Const.unitOfList l cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.constInsertManyIfNewUnit Impl.WF.empty⟩
|
||||
|
||||
theorem unitOfArray [TransCmp cmp] {a : Array α} :
|
||||
(Raw.Const.unitOfArray a cmp).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨Impl.WF.constInsertManyIfNewUnit Impl.WF.empty⟩
|
||||
|
||||
theorem constAlter {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(Const.alter t a f).WF :=
|
||||
⟨h.out.constAlter!⟩
|
||||
|
||||
theorem constModify {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(Const.modify t a f).WF :=
|
||||
letI : Ord α := ⟨cmp⟩; ⟨h.out.constModify⟩
|
||||
|
||||
theorem constMergeWith {mergeFn} {t₁ t₂ : Raw α β cmp} (h : t₁.WF) :
|
||||
(Const.mergeWith mergeFn t₁ t₂).WF :=
|
||||
⟨h.out.constMergeWith!⟩
|
||||
|
||||
end Std.DTreeMap.Raw.WF.Const
|
||||
@@ -699,9 +699,14 @@ theorem distinct_keys [EquivBEq α] [LawfulHashable α] :
|
||||
DHashMap.distinct_keys
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
m.toList.map Prod.fst = m.keys :=
|
||||
DHashMap.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_prod_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] :
|
||||
m.toList.map Prod.fst = m.keys :=
|
||||
DHashMap.Const.map_prod_fst_toList_eq_keys
|
||||
DHashMap.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] :
|
||||
@@ -733,7 +738,7 @@ theorem get?_eq_some_iff_exists_beq_and_mem_toList [EquivBEq α] [LawfulHashable
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_getElem?_eq_some
|
||||
[EquivBEq α] [LawfulHashable α] {k k' : α} {v : β} :
|
||||
m.toList.find? (fun a => a.1 == k) = some ⟨k', v⟩ ↔
|
||||
m.getKey? k = some k' ∧ get? m k = some v :=
|
||||
m.getKey? k = some k' ∧ m[k]? = some v :=
|
||||
DHashMap.Const.find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [EquivBEq α] [LawfulHashable α]
|
||||
|
||||
@@ -706,9 +706,14 @@ theorem distinct_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
DHashMap.Raw.distinct_keys h.out
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
m.toList.map Prod.fst = m.keys :=
|
||||
DHashMap.Raw.Const.map_fst_toList_eq_keys h.out
|
||||
|
||||
@[simp, deprecated map_fst_toList_eq_keys (since := "2025-02-28")]
|
||||
theorem map_prod_fst_toList_eq_keys [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
m.toList.map Prod.fst = m.keys :=
|
||||
DHashMap.Raw.Const.map_prod_fst_toList_eq_keys h.out
|
||||
DHashMap.Raw.Const.map_fst_toList_eq_keys h.out
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [EquivBEq α] [LawfulHashable α] (h : m.WF) :
|
||||
|
||||
@@ -1997,7 +1997,7 @@ theorem pairwise_fst_eq_false [BEq α] {l : List ((a : α) × β a)} (h : Distin
|
||||
rw [DistinctKeys.def] at h
|
||||
assumption
|
||||
|
||||
theorem map_prod_fst_map_toProd_eq_keys {β : Type v} {l : List ((_ : α) × β)} :
|
||||
theorem map_fst_map_toProd_eq_keys {β : Type v} {l : List ((_ : α) × β)} :
|
||||
List.map Prod.fst (List.map (fun x => (x.fst, x.snd)) l) = List.keys l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
|
||||
@@ -5,7 +5,7 @@ Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeMap.Basic
|
||||
import Std.Data.TreeMap.Raw
|
||||
import Std.Data.TreeMap.Raw.Basic
|
||||
import Std.Data.DTreeMap.AdditionalOperations
|
||||
|
||||
/-!
|
||||
@@ -24,22 +24,6 @@ variable {α : Type u} {β : Type v} {γ : Type w} {cmp : α → α → Ordering
|
||||
|
||||
namespace Std.TreeMap
|
||||
|
||||
namespace Raw
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.filterMap]
|
||||
def filterMap (f : (a : α) → β → Option γ) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
⟨t.inner.filterMap f⟩
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.map]
|
||||
def map (f : α → β → γ) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
⟨t.inner.map f⟩
|
||||
|
||||
/-!
|
||||
We do not provide `get*GE`, `get*GT`, `get*LE` and `get*LT` functions for the raw trees.
|
||||
-/
|
||||
|
||||
end Raw
|
||||
|
||||
@[inline, inherit_doc DTreeMap.filterMap]
|
||||
def filterMap (f : (a : α) → β → Option γ) (m : TreeMap α β cmp) : TreeMap α γ cmp :=
|
||||
⟨m.inner.filterMap f⟩
|
||||
|
||||
@@ -14,7 +14,7 @@ This file develops the type `Std.TreeMap` of tree maps.
|
||||
Lemmas about the operations on `Std.TreeMap` will be available in the
|
||||
module `Std.Data.TreeMap.Lemmas`.
|
||||
|
||||
See the module `Std.Data.TreeMap.Raw` for a variant of this type which is safe to use in
|
||||
See the module `Std.Data.TreeMap.Raw.Basic` for a variant of this type which is safe to use in
|
||||
nested inductive types.
|
||||
-/
|
||||
|
||||
@@ -52,8 +52,8 @@ Internally, the tree maps are represented as size-bounded trees, a type of self-
|
||||
search tree with efficient order statistic lookups.
|
||||
|
||||
These tree maps contain a bundled well-formedness invariant, which means that they cannot
|
||||
be used in nested inductive types. For these use cases, `Std.Data.TreeMap.Raw` and
|
||||
`Std.Data.TreeMap.Raw.WF` unbundle the invariant from the tree map. When in doubt, prefer
|
||||
be used in nested inductive types. For these use cases, `Std.TreeMap.Raw` and
|
||||
`Std.TreeMap.Raw.WF` unbundle the invariant from the tree map. When in doubt, prefer
|
||||
`TreeMap` over `TreeMap.Raw`.
|
||||
-/
|
||||
structure TreeMap (α : Type u) (β : Type v) (cmp : α → α → Ordering := by exact compare) where
|
||||
@@ -405,16 +405,16 @@ def fold (f : δ → (a : α) → β → δ) (init : δ) (t : TreeMap α β cmp)
|
||||
t.foldl f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.foldrM]
|
||||
def foldrM (f : δ → (a : α) → β → m δ) (init : δ) (t : TreeMap α β cmp) : m δ :=
|
||||
def foldrM (f : (a : α) → β → δ → m δ) (init : δ) (t : TreeMap α β cmp) : m δ :=
|
||||
t.inner.foldrM f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.foldr]
|
||||
def foldr (f : δ → (a : α) → β → δ) (init : δ) (t : TreeMap α β cmp) : δ :=
|
||||
def foldr (f : (a : α) → β → δ → δ) (init : δ) (t : TreeMap α β cmp) : δ :=
|
||||
t.inner.foldr f init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → β → δ) (init : δ) (t : TreeMap α β cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun k v acc => f acc k v) init t
|
||||
|
||||
@[inline, inherit_doc DTreeMap.partition]
|
||||
def partition (f : (a : α) → β → Bool) (t : TreeMap α β cmp) : TreeMap α β cmp × TreeMap α β cmp :=
|
||||
|
||||
@@ -655,4 +655,76 @@ theorem getThenInsertIfNew?_snd [TransCmp cmp] {k : α} {v : β} :
|
||||
(getThenInsertIfNew? t k v).2 = t.insertIfNew k v :=
|
||||
ext <| DTreeMap.Const.getThenInsertIfNew?_snd
|
||||
|
||||
@[simp]
|
||||
theorem length_keys [TransCmp cmp] :
|
||||
t.keys.length = t.size :=
|
||||
DTreeMap.length_keys
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_keys :
|
||||
t.keys.isEmpty = t.isEmpty :=
|
||||
DTreeMap.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_keys [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
t.keys.contains k = t.contains k :=
|
||||
DTreeMap.contains_keys
|
||||
|
||||
@[simp]
|
||||
theorem mem_keys [LawfulEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
k ∈ t.keys ↔ k ∈ t :=
|
||||
DTreeMap.mem_keys
|
||||
|
||||
theorem distinct_keys [TransCmp cmp] :
|
||||
t.keys.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
DTreeMap.distinct_keys
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
(toList t).map Prod.fst = t.keys :=
|
||||
DTreeMap.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList :
|
||||
(toList t).length = t.size :=
|
||||
DTreeMap.Const.length_toList
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
(toList t).isEmpty = t.isEmpty :=
|
||||
DTreeMap.Const.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getElem?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t[k]? = some v :=
|
||||
DTreeMap.Const.mem_toList_iff_get?_eq_some
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getKey?_eq_some_and_getElem?_eq_some [TransCmp cmp] {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t.getKey? k = some k ∧ t[k]? = some v :=
|
||||
DTreeMap.Const.mem_toList_iff_getKey?_eq_some_and_get?_eq_some
|
||||
|
||||
theorem getElem?_eq_some_iff_exists_compare_eq_eq_and_mem_toList [TransCmp cmp] {k : α} {v : β} :
|
||||
t[k]? = some v ↔ ∃ (k' : α), cmp k k' = .eq ∧ (k', v) ∈ toList t :=
|
||||
DTreeMap.Const.get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList
|
||||
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_getElem?_eq_some [TransCmp cmp] {k k' : α}
|
||||
{v : β} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = some ⟨k', v⟩ ↔
|
||||
t.getKey? k = some k' ∧ t[k]? = some v :=
|
||||
DTreeMap.Const.find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
DTreeMap.Const.find?_toList_eq_none_iff_contains_eq_false
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t :=
|
||||
DTreeMap.Const.find?_toList_eq_none_iff_not_mem
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] :
|
||||
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
DTreeMap.Const.distinct_keys_toList
|
||||
|
||||
end Std.TreeMap
|
||||
|
||||
39
src/Std/Data/TreeMap/Raw/AdditionalOperations.lean
Normal file
39
src/Std/Data/TreeMap/Raw/AdditionalOperations.lean
Normal file
@@ -0,0 +1,39 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeMap.Basic
|
||||
import Std.Data.TreeMap.Raw.Basic
|
||||
import Std.Data.DTreeMap.Raw.AdditionalOperations
|
||||
|
||||
/-!
|
||||
# Additional raw tree map operations
|
||||
|
||||
This file defines more operations on `Std.TreeMap.Raw`.
|
||||
We currently do not provide lemmas for these functions.
|
||||
-/
|
||||
|
||||
set_option autoImplicit false
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
variable {α : Type u} {β : Type v} {γ : Type w} {cmp : α → α → Ordering}
|
||||
|
||||
namespace Std.TreeMap.Raw
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.filterMap]
|
||||
def filterMap (f : (a : α) → β → Option γ) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
⟨t.inner.filterMap f⟩
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.map]
|
||||
def map (f : α → β → γ) (t : Raw α β cmp) : Raw α γ cmp :=
|
||||
⟨t.inner.map f⟩
|
||||
|
||||
/-!
|
||||
We do not provide `get*GE`, `get*GT`, `get*LE` and `get*LT` functions for the raw trees.
|
||||
-/
|
||||
|
||||
end Std.TreeMap.Raw
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel, Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Raw
|
||||
import Std.Data.DTreeMap.Raw.Basic
|
||||
|
||||
/-
|
||||
# Tree maps with unbundled well-formedness invariant
|
||||
@@ -17,7 +17,7 @@ available as `Std.TreeMap.Raw.WF` and we prove in this file that all operations
|
||||
well-formedness. When in doubt, prefer `TreeMap` over `TreeMap.Raw`.
|
||||
|
||||
Lemmas about the operations on `Std.TreeMap.Raw` will be available in the module
|
||||
`Std.Data.TreeMap.RawLemmas`.
|
||||
`Std.Data.TreeMap.Raw.Lemmas`.
|
||||
-/
|
||||
|
||||
set_option autoImplicit false
|
||||
@@ -35,7 +35,7 @@ namespace TreeMap
|
||||
Tree maps without a bundled well-formedness invariant, suitable for use in nested
|
||||
inductive types. The well-formedness invariant is called `Raw.WF`. When in doubt, prefer `TreeMap`
|
||||
over `TreeMap.Raw`. Lemmas about the operations on `Std.TreeMap.Raw` are available in the
|
||||
module `Std.Data.TreeMap.RawLemmas`.
|
||||
module `Std.Data.TreeMap.Raw.Lemmas`.
|
||||
|
||||
A tree map stores an assignment of keys to values. It depends on a comparator function that
|
||||
defines an ordering on the keys and provides efficient order-dependent queries, such as retrieval
|
||||
@@ -411,16 +411,16 @@ def fold (f : δ → (a : α) → β → δ) (init : δ) (t : Raw α β cmp) :
|
||||
t.foldl f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.foldrM]
|
||||
def foldrM (f : δ → (a : α) → β → m δ) (init : δ) (t : Raw α β cmp) : m δ :=
|
||||
def foldrM (f : (a : α) → β → δ → m δ) (init : δ) (t : Raw α β cmp) : m δ :=
|
||||
t.inner.foldrM f init
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.foldr]
|
||||
def foldr (f : δ → (a : α) → β → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
def foldr (f : (a : α) → β → δ → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
t.inner.foldr f init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → β → δ) (init : δ) (t : Raw α β cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun k v acc => f acc k v) init t
|
||||
|
||||
@[inline, inherit_doc DTreeMap.Raw.partition]
|
||||
def partition (f : (a : α) → β → Bool) (t : Raw α β cmp) : Raw α β cmp × Raw α β cmp :=
|
||||
@@ -4,14 +4,15 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel, Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.RawLemmas
|
||||
import Std.Data.TreeMap.Raw
|
||||
import Std.Data.DTreeMap.Raw.Lemmas
|
||||
import Std.Data.TreeMap.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Tree map lemmas
|
||||
|
||||
This file contains lemmas about `Std.Data.TreeMap.Raw`. Most of the lemmas require
|
||||
This file contains lemmas about `Std.Data.TreeMap.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
These proofs can be obtained from `Std.Data.TreeMap.Raw.WF`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -661,4 +662,77 @@ theorem getThenInsertIfNew?_snd [TransCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
(getThenInsertIfNew? t k v).2 = t.insertIfNew k v :=
|
||||
ext <| DTreeMap.Raw.Const.getThenInsertIfNew?_snd h
|
||||
|
||||
@[simp]
|
||||
theorem length_keys [TransCmp cmp] (h : t.WF) :
|
||||
t.keys.length = t.size :=
|
||||
DTreeMap.Raw.length_keys h
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_keys :
|
||||
t.keys.isEmpty = t.isEmpty :=
|
||||
DTreeMap.Raw.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_keys [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
t.keys.contains k = t.contains k :=
|
||||
DTreeMap.Raw.contains_keys h
|
||||
|
||||
@[simp]
|
||||
theorem mem_keys [LawfulEqCmp cmp] [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
k ∈ t.keys ↔ k ∈ t :=
|
||||
DTreeMap.Raw.mem_keys h
|
||||
|
||||
theorem distinct_keys [TransCmp cmp] (h : t.WF) :
|
||||
t.keys.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
DTreeMap.Raw.distinct_keys h
|
||||
|
||||
@[simp]
|
||||
theorem map_fst_toList_eq_keys :
|
||||
(toList t).map Prod.fst = t.keys :=
|
||||
DTreeMap.Raw.Const.map_fst_toList_eq_keys
|
||||
|
||||
@[simp]
|
||||
theorem length_toList (h : t.WF) :
|
||||
(toList t).length = t.size :=
|
||||
DTreeMap.Raw.Const.length_toList h
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
(toList t).isEmpty = t.isEmpty :=
|
||||
DTreeMap.Raw.Const.isEmpty_toList
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getElem?_eq_some [TransCmp cmp] [LawfulEqCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t[k]? = some v :=
|
||||
DTreeMap.Raw.Const.mem_toList_iff_get?_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList_iff_getKey?_eq_some_and_getElem?_eq_some [TransCmp cmp] (h : t.WF) {k : α} {v : β} :
|
||||
(k, v) ∈ toList t ↔ t.getKey? k = some k ∧ t[k]? = some v :=
|
||||
DTreeMap.Raw.Const.mem_toList_iff_getKey?_eq_some_and_get?_eq_some h
|
||||
|
||||
theorem getElem?_eq_some_iff_exists_compare_eq_eq_and_mem_toList [TransCmp cmp] (h : t.WF) {k : α}
|
||||
{v : β} :
|
||||
t[k]? = some v ↔ ∃ (k' : α), cmp k k' = .eq ∧ (k', v) ∈ toList t :=
|
||||
DTreeMap.Raw.Const.get?_eq_some_iff_exists_compare_eq_eq_and_mem_toList h
|
||||
|
||||
theorem find?_toList_eq_some_iff_getKey?_eq_some_and_getElem?_eq_some [TransCmp cmp] (h : t.WF)
|
||||
{k k' : α} {v : β} :
|
||||
t.toList.find? (cmp ·.1 k == .eq) = some ⟨k', v⟩ ↔
|
||||
t.getKey? k = some k' ∧ t[k]? = some v :=
|
||||
DTreeMap.Raw.Const.find?_toList_eq_some_iff_getKey?_eq_some_and_get?_eq_some h
|
||||
|
||||
theorem find?_toList_eq_none_iff_contains_eq_false [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ t.contains k = false :=
|
||||
DTreeMap.Raw.Const.find?_toList_eq_none_iff_contains_eq_false h
|
||||
|
||||
@[simp]
|
||||
theorem find?_toList_eq_none_iff_not_mem [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
(toList t).find? (cmp ·.1 k == .eq) = none ↔ ¬ k ∈ t :=
|
||||
DTreeMap.Raw.Const.find?_toList_eq_none_iff_not_mem h
|
||||
|
||||
theorem distinct_keys_toList [TransCmp cmp] (h : t.WF) :
|
||||
(toList t).Pairwise (fun a b => ¬ cmp a.1 b.1 = .eq) :=
|
||||
DTreeMap.Raw.Const.distinct_keys_toList h
|
||||
|
||||
end Std.TreeMap.Raw
|
||||
115
src/Std/Data/TreeMap/Raw/WF.lean
Normal file
115
src/Std/Data/TreeMap/Raw/WF.lean
Normal file
@@ -0,0 +1,115 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.DTreeMap.Raw.WF
|
||||
import Std.Data.TreeMap.Raw.AdditionalOperations
|
||||
import Std.Data.TreeMap.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Well-formedness proofs for raw tree maps
|
||||
|
||||
This file contains well-formedness proofs about `Std.Data.TreeMap.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
set_option autoImplicit false
|
||||
|
||||
universe u v
|
||||
|
||||
namespace Std.TreeMap.Raw.WF
|
||||
|
||||
open DTreeMap.Raw renaming WF → InnerWF
|
||||
|
||||
variable {α : Type u} {β : Type v} {cmp : α → α → Ordering} {t : Raw α β cmp}
|
||||
|
||||
theorem empty : (empty : Raw α β cmp).WF :=
|
||||
⟨InnerWF.empty⟩
|
||||
|
||||
theorem emptyc : (∅ : Raw α β cmp).WF :=
|
||||
empty
|
||||
|
||||
theorem erase [TransCmp cmp] {a} (h : t.WF) :
|
||||
WF (t.erase a) :=
|
||||
⟨InnerWF.erase h⟩
|
||||
|
||||
theorem insert [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.insert a b) :=
|
||||
⟨InnerWF.insert h⟩
|
||||
|
||||
theorem insertIfNew [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.insertIfNew a b) :=
|
||||
⟨InnerWF.insertIfNew h⟩
|
||||
|
||||
theorem containsThenInsert [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.containsThenInsert a b).2 :=
|
||||
⟨InnerWF.containsThenInsert h⟩
|
||||
|
||||
theorem containsThenInsertIfNew [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.containsThenInsertIfNew a b).2 :=
|
||||
⟨InnerWF.containsThenInsertIfNew h⟩
|
||||
|
||||
theorem getThenInsertIfNew? [TransCmp cmp] {a b} (h : t.WF) :
|
||||
WF (t.getThenInsertIfNew? a b).2 :=
|
||||
⟨InnerWF.constGetThenInsertIfNew? h⟩
|
||||
|
||||
theorem filter [TransCmp cmp] {f} (h : t.WF) :
|
||||
WF (t.filter f) :=
|
||||
⟨InnerWF.filter h⟩
|
||||
|
||||
theorem filterMap [TransCmp cmp] {f : α → β → Option β} (h : t.WF) :
|
||||
WF (t.filterMap f) :=
|
||||
⟨InnerWF.filterMap h⟩
|
||||
|
||||
theorem partition_fst [TransCmp cmp] {f} :
|
||||
WF (t.partition f).fst :=
|
||||
⟨InnerWF.partition_fst⟩
|
||||
|
||||
theorem partition_snd [TransCmp cmp] {f} :
|
||||
WF (t.partition f).snd :=
|
||||
⟨InnerWF.partition_snd⟩
|
||||
|
||||
theorem eraseMany [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α β cmp} (h : t.WF) :
|
||||
WF (t.eraseMany l) :=
|
||||
⟨InnerWF.eraseMany h⟩
|
||||
|
||||
theorem insertMany [TransCmp cmp] {ρ} [ForIn Id ρ (α × β)] {l : ρ} {t : Raw α β cmp}
|
||||
(h : t.WF) : WF (t.insertMany l) :=
|
||||
⟨InnerWF.constInsertMany h⟩
|
||||
|
||||
theorem insertManyIfNewUnit [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α Unit cmp}
|
||||
(h : t.WF) : WF (t.insertManyIfNewUnit l) :=
|
||||
⟨InnerWF.constInsertManyIfNewUnit h⟩
|
||||
|
||||
theorem ofList [TransCmp cmp] {l : List (α × β)} :
|
||||
(Raw.ofList l cmp).WF :=
|
||||
⟨InnerWF.constOfList⟩
|
||||
|
||||
theorem ofArray [TransCmp cmp] {a : Array (α × β)} :
|
||||
(Raw.ofArray a cmp).WF :=
|
||||
⟨InnerWF.constOfArray⟩
|
||||
|
||||
theorem alter {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(t.alter a f).WF :=
|
||||
⟨InnerWF.constAlter h⟩
|
||||
|
||||
theorem modify {a f} {t : Raw α β cmp} (h : t.WF) :
|
||||
(t.modify a f).WF :=
|
||||
⟨InnerWF.constModify h⟩
|
||||
|
||||
theorem unitOfList [TransCmp cmp] {l : List α} :
|
||||
(Raw.unitOfList l cmp).WF :=
|
||||
⟨InnerWF.unitOfList⟩
|
||||
|
||||
theorem unitOfArray [TransCmp cmp] {a : Array α} :
|
||||
(Raw.unitOfArray a cmp).WF :=
|
||||
⟨InnerWF.unitOfArray⟩
|
||||
|
||||
theorem mergeWith {mergeFn} {t₁ t₂ : Raw α β cmp} (h : t₁.WF) :
|
||||
(t₁.mergeWith mergeFn t₂).WF :=
|
||||
⟨InnerWF.constMergeWith h⟩
|
||||
|
||||
end Std.TreeMap.Raw.WF
|
||||
@@ -5,7 +5,7 @@ Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeSet.Basic
|
||||
import Std.Data.TreeSet.Raw
|
||||
import Std.Data.TreeSet.Raw.Basic
|
||||
import Std.Data.TreeMap.AdditionalOperations
|
||||
|
||||
/-!
|
||||
|
||||
@@ -14,7 +14,7 @@ This file develops the type `Std.TreeSet` of tree sets.
|
||||
Lemmas about the operations on `Std.Data.TreeSet` will be available in the
|
||||
module `Std.Data.TreeSet.Lemmas`.
|
||||
|
||||
See the module `Std.Data.TreeSet.Raw` for a variant of this type which is safe to use in
|
||||
See the module `Std.Data.TreeSet.Raw.Basic` for a variant of this type which is safe to use in
|
||||
nested inductive types.
|
||||
-/
|
||||
|
||||
@@ -51,8 +51,8 @@ Internally, the tree sets are represented as size-bounded trees, a type of self-
|
||||
search tree with efficient order statistic lookups.
|
||||
|
||||
These tree sets contain a bundled well-formedness invariant, which means that they cannot
|
||||
be used in nested inductive types. For these use cases, `Std.Data.TreeSet.Raw` and
|
||||
`Std.Data.TreeSet.Raw.WF` unbundle the invariant from the tree set. When in doubt, prefer
|
||||
be used in nested inductive types. For these use cases, `Std.TreeSet.Raw` and
|
||||
`Std.TreeSet.Raw.WF` unbundle the invariant from the tree set. When in doubt, prefer
|
||||
`TreeSet` over `TreeSet.Raw`.
|
||||
-/
|
||||
structure TreeSet (α : Type u) (cmp : α → α → Ordering := by exact compare) where
|
||||
@@ -388,17 +388,17 @@ Monadically computes a value by folding the given function over the elements in
|
||||
descending order.
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {m δ} [Monad m] (f : δ → (a : α) → m δ) (init : δ) (t : TreeSet α cmp) : m δ :=
|
||||
t.inner.foldrM (fun c a _ => f c a) init
|
||||
def foldrM {m δ} [Monad m] (f : (a : α) → δ → m δ) (init : δ) (t : TreeSet α cmp) : m δ :=
|
||||
t.inner.foldrM (fun a _ acc => f a acc) init
|
||||
|
||||
/-- Folds the given function over the elements of the tree set in descending order. -/
|
||||
@[inline]
|
||||
def foldr (f : δ → (a : α) → δ) (init : δ) (t : TreeSet α cmp) : δ :=
|
||||
t.inner.foldr (fun c a _ => f c a) init
|
||||
def foldr (f : (a : α) → δ → δ) (init : δ) (t : TreeSet α cmp) : δ :=
|
||||
t.inner.foldr (fun a _ acc => f a acc) init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → δ) (init : δ) (t : TreeSet α cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun a acc => f acc a) init t
|
||||
|
||||
/-- Partitions a tree set into two tree sets based on a predicate. -/
|
||||
@[inline]
|
||||
@@ -437,7 +437,7 @@ def all (t : TreeSet α cmp) (p : α → Bool) : Bool :=
|
||||
/-- Transforms the tree set into a list of elements in ascending order. -/
|
||||
@[inline]
|
||||
def toList (t : TreeSet α cmp) : List α :=
|
||||
t.inner.inner.inner.foldr (fun l a _ => a :: l) ∅
|
||||
t.inner.inner.inner.foldr (fun a _ l => a :: l) ∅
|
||||
|
||||
/-- Transforms a list into a tree set. -/
|
||||
def ofList (l : List α) (cmp : α → α → Ordering := by exact compare) : TreeSet α cmp :=
|
||||
|
||||
@@ -334,4 +334,28 @@ theorem containsThenInsert_snd [TransCmp cmp] {k : α} :
|
||||
(t.containsThenInsert k).2 = t.insert k :=
|
||||
ext <| TreeMap.containsThenInsertIfNew_snd
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [TransCmp cmp] :
|
||||
t.toList.length = t.size :=
|
||||
DTreeMap.length_keys
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
t.toList.isEmpty = t.isEmpty :=
|
||||
DTreeMap.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_toList [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
t.toList.contains k = t.contains k :=
|
||||
DTreeMap.contains_keys
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList [LawfulEqCmp cmp] [TransCmp cmp] {k : α} :
|
||||
k ∈ t.toList ↔ k ∈ t :=
|
||||
DTreeMap.mem_keys
|
||||
|
||||
theorem distinct_toList [TransCmp cmp] :
|
||||
t.toList.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
DTreeMap.distinct_keys
|
||||
|
||||
end Std.TreeSet
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel, Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeMap.Raw
|
||||
import Std.Data.TreeMap.Raw.Basic
|
||||
import Std.Data.TreeSet.Basic
|
||||
|
||||
/-
|
||||
@@ -18,7 +18,7 @@ available as `Std.TreeSet.Raw.WF` and we prove in this file that all operations
|
||||
well-formedness. When in doubt, prefer `TreeSet` over `TreeSet.Raw`.
|
||||
|
||||
Lemmas about the operations on `Std.TreeSet.Raw` will be available in the module
|
||||
`Std.Data.TreeSet.RawLemmas`.
|
||||
`Std.Data.TreeSet.Raw.Lemmas`.
|
||||
-/
|
||||
|
||||
set_option autoImplicit false
|
||||
@@ -36,7 +36,7 @@ namespace TreeSet
|
||||
Tree sets without a bundled well-formedness invariant, suitable for use in nested
|
||||
inductive types. The well-formedness invariant is called `Raw.WF`. When in doubt, prefer `TreeSet`
|
||||
over `TreeSet.Raw`. Lemmas about the operations on `Std.TreeSet.Raw` are available in the
|
||||
module `Std.Data.TreeSet.RawLemmas`.
|
||||
module `Std.Data.TreeSet.Raw.Lemmas`.
|
||||
|
||||
A tree set stores elements of a certain type in a certain order. It depends on a comparator function
|
||||
that defines an ordering on the keys and provides efficient order-dependent queries, such as
|
||||
@@ -272,16 +272,16 @@ def fold (f : δ → (a : α) → δ) (init : δ) (t : Raw α cmp) : δ :=
|
||||
t.foldl f init
|
||||
|
||||
@[inline, inherit_doc TreeSet.empty]
|
||||
def foldrM (f : δ → (a : α) → m δ) (init : δ) (t : Raw α cmp) : m δ :=
|
||||
t.inner.foldrM (fun c a _ => f c a) init
|
||||
def foldrM (f : (a : α) → δ → m δ) (init : δ) (t : Raw α cmp) : m δ :=
|
||||
t.inner.foldrM (fun a _ acc => f a acc) init
|
||||
|
||||
@[inline, inherit_doc TreeSet.empty]
|
||||
def foldr (f : δ → (a : α) → δ) (init : δ) (t : Raw α cmp) : δ :=
|
||||
t.inner.foldr (fun c a _ => f c a) init
|
||||
def foldr (f : (a : α) → δ → δ) (init : δ) (t : Raw α cmp) : δ :=
|
||||
t.inner.foldr (fun a _ acc => f a acc) init
|
||||
|
||||
@[inline, inherit_doc foldr, deprecated foldr (since := "2025-02-12")]
|
||||
def revFold (f : δ → (a : α) → δ) (init : δ) (t : Raw α cmp) : δ :=
|
||||
foldr f init t
|
||||
foldr (fun a acc => f acc a) init t
|
||||
|
||||
@[inline, inherit_doc TreeSet.partition]
|
||||
def partition (f : (a : α) → Bool) (t : Raw α cmp) : Raw α cmp × Raw α cmp :=
|
||||
@@ -311,7 +311,7 @@ def all (t : Raw α cmp) (p : α → Bool) : Bool :=
|
||||
|
||||
@[inline, inherit_doc TreeSet.empty]
|
||||
def toList (t : Raw α cmp) : List α :=
|
||||
t.inner.inner.inner.foldr (fun l a _ => a :: l) ∅
|
||||
t.inner.inner.inner.foldr (fun a _ l => a :: l) ∅
|
||||
|
||||
@[inline, inherit_doc TreeSet.ofList]
|
||||
def ofList (l : List α) (cmp : α → α → Ordering := by exact compare) : Raw α cmp :=
|
||||
@@ -4,14 +4,15 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel, Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeMap.RawLemmas
|
||||
import Std.Data.TreeSet.Raw
|
||||
import Std.Data.TreeMap.Raw.Lemmas
|
||||
import Std.Data.TreeSet.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Tree set lemmas
|
||||
|
||||
This file contains lemmas about `Std.Data.TreeSet.Raw`. Most of the lemmas require
|
||||
This file contains lemmas about `Std.Data.TreeSet.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
These proofs can be obtained from `Std.Data.TreeSet.Raw.WF`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -334,4 +335,28 @@ theorem containsThenInsert_snd [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
(t.containsThenInsert k).2 = t.insert k :=
|
||||
ext <| TreeMap.Raw.containsThenInsertIfNew_snd h
|
||||
|
||||
@[simp]
|
||||
theorem length_toList [TransCmp cmp] (h : t.WF) :
|
||||
t.toList.length = t.size :=
|
||||
DTreeMap.Raw.length_keys h
|
||||
|
||||
@[simp]
|
||||
theorem isEmpty_toList :
|
||||
t.toList.isEmpty = t.isEmpty :=
|
||||
DTreeMap.Raw.isEmpty_keys
|
||||
|
||||
@[simp]
|
||||
theorem contains_toList [BEq α] [LawfulBEqCmp cmp] [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
t.toList.contains k = t.contains k :=
|
||||
DTreeMap.Raw.contains_keys h
|
||||
|
||||
@[simp]
|
||||
theorem mem_toList [LawfulEqCmp cmp] [TransCmp cmp] (h : t.WF) {k : α} :
|
||||
k ∈ t.toList ↔ k ∈ t :=
|
||||
DTreeMap.Raw.mem_keys h
|
||||
|
||||
theorem distinct_toList [TransCmp cmp] (h : t.WF) :
|
||||
t.toList.Pairwise (fun a b => ¬ cmp a b = .eq) :=
|
||||
DTreeMap.Raw.distinct_keys h
|
||||
|
||||
end Std.TreeSet.Raw
|
||||
79
src/Std/Data/TreeSet/Raw/WF.lean
Normal file
79
src/Std/Data/TreeSet/Raw/WF.lean
Normal file
@@ -0,0 +1,79 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Paul Reichert
|
||||
-/
|
||||
prelude
|
||||
import Std.Data.TreeMap.Raw.WF
|
||||
import Std.Data.TreeSet.AdditionalOperations
|
||||
import Std.Data.TreeSet.Raw.Basic
|
||||
|
||||
/-!
|
||||
# Well-formedness proofs for raw tree sets
|
||||
|
||||
This file contains well-formedness proofs about `Std.Data.TreeSet.Raw.Basic`. Most of the lemmas require
|
||||
`TransCmp cmp` for the comparison function `cmp`.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
set_option autoImplicit false
|
||||
|
||||
universe u v
|
||||
|
||||
namespace Std.TreeSet.Raw.WF
|
||||
|
||||
open TreeMap.Raw renaming WF → InnerWF
|
||||
|
||||
variable {α : Type u} {cmp : α → α → Ordering} {t : Raw α cmp}
|
||||
|
||||
theorem empty : (empty : Raw α cmp).WF :=
|
||||
⟨InnerWF.empty⟩
|
||||
|
||||
theorem emptyc : (∅ : Raw α cmp).WF :=
|
||||
empty
|
||||
|
||||
theorem erase [TransCmp cmp] {a} (h : t.WF) :
|
||||
WF (t.erase a) :=
|
||||
⟨InnerWF.erase h⟩
|
||||
|
||||
theorem insert [TransCmp cmp] {a} (h : t.WF) :
|
||||
WF (t.insert a) :=
|
||||
⟨InnerWF.insertIfNew h⟩
|
||||
|
||||
theorem containsThenInsert [TransCmp cmp] {a} (h : t.WF) :
|
||||
WF (t.containsThenInsert a).2 :=
|
||||
⟨InnerWF.containsThenInsertIfNew h⟩
|
||||
|
||||
theorem filter [TransCmp cmp] {f} (h : t.WF) :
|
||||
WF (t.filter f) :=
|
||||
⟨InnerWF.filter h⟩
|
||||
|
||||
theorem partition_fst [TransCmp cmp] {f} :
|
||||
WF (t.partition f).fst :=
|
||||
⟨InnerWF.partition_fst⟩
|
||||
|
||||
theorem partition_snd [TransCmp cmp] {f} :
|
||||
WF (t.partition f).snd :=
|
||||
⟨InnerWF.partition_snd⟩
|
||||
|
||||
theorem eraseMany [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α cmp} (h : t.WF) :
|
||||
WF (t.eraseMany l) :=
|
||||
⟨InnerWF.eraseMany h⟩
|
||||
|
||||
theorem insertMany [TransCmp cmp] {ρ} [ForIn Id ρ α] {l : ρ} {t : Raw α cmp}
|
||||
(h : t.WF) : WF (t.insertMany l) :=
|
||||
⟨InnerWF.insertManyIfNewUnit h⟩
|
||||
|
||||
theorem ofList [TransCmp cmp] {l : List α} :
|
||||
(Raw.ofList l cmp).WF :=
|
||||
⟨InnerWF.unitOfList⟩
|
||||
|
||||
theorem ofArray [TransCmp cmp] {a : Array α} :
|
||||
(Raw.ofArray a cmp).WF :=
|
||||
⟨InnerWF.unitOfArray⟩
|
||||
|
||||
theorem merge {t₁ t₂ : Raw α cmp} (h : t₁.WF) :
|
||||
(t₁.merge t₂).WF :=
|
||||
⟨InnerWF.mergeWith h⟩
|
||||
|
||||
end Std.TreeSet.Raw.WF
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Init.NotationExtra
|
||||
import Init.Data.ToString.Macro
|
||||
import Init.Data.Int.DivMod.Basic
|
||||
import Init.Data.Int.Linear
|
||||
import Init.Data.Nat.Gcd
|
||||
namespace Std
|
||||
namespace Internal
|
||||
@@ -101,15 +102,13 @@ protected def floor (a : Rat) : Int :=
|
||||
if a.den == 1 then
|
||||
a.num
|
||||
else
|
||||
let r := a.num.tmod a.den
|
||||
if a.num < 0 then r - 1 else r
|
||||
a.num / a.den
|
||||
|
||||
protected def ceil (a : Rat) : Int :=
|
||||
if a.den == 1 then
|
||||
a.num
|
||||
else
|
||||
let r := a.num.tmod a.den
|
||||
if a.num > 0 then r + 1 else r
|
||||
Int.Linear.cdiv a.num a.den
|
||||
|
||||
instance : LT Rat where
|
||||
lt a b := (Rat.lt a b) = true
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Henrik Böving
|
||||
prelude
|
||||
import Init.SimpLemmas
|
||||
import Init.Data.Bool
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
/-!
|
||||
This module contains the `Bool` simplifying part of the `bv_normalize` simp set.
|
||||
@@ -52,7 +53,151 @@ theorem if_eq_cond {b : Bool} {x y : α} : (if b = true then x else y) = (bif b
|
||||
theorem Bool.not_xor : ∀ (a b : Bool), !(a ^^ b) = (a == b) := by decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.or_elim : ∀ (a b : Bool), (a || b) = !(!a && !b) := by decide
|
||||
theorem Bool.not_beq_one : ∀ (a : BitVec 1), (!(a == 1#1)) = (a == 0#1) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.not_beq_zero : ∀ (a : BitVec 1), (!(a == 0#1)) = (a == 1#1) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.not_one_beq : ∀ (a : BitVec 1), (!(1#1 == a)) = (a == 0#1) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.not_zero_beq : ∀ (a : BitVec 1), (!(0#1 == a)) = (a == 1#1) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_same_then : ∀ (c t e : Bool), ((bif c then t else e) == t) = (c || (t == e)) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_same_then' : ∀ (c t e : Bool), (t == (bif c then t else e)) = (c || (t == e)) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_same_else : ∀ (c t e : Bool), ((bif c then t else e) == e) = (!c || (t == e)) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_same_else' :
|
||||
∀ (c t e : Bool), (e == (bif c then t else e)) = (!c || (t == e)) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_same_then :
|
||||
∀ (c : Bool) (t e : BitVec w), ((bif c then t else e) == t) = (c || (t == e)) := by
|
||||
intro c t e
|
||||
cases c <;> simp [BEq.comm (a := t) (b := e)]
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_same_then' :
|
||||
∀ (c : Bool) (t e : BitVec w), (t == (bif c then t else e)) = (c || (t == e)) := by
|
||||
intro c t e
|
||||
cases c <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_same_else :
|
||||
∀ (c : Bool) (t e : BitVec w), ((bif c then t else e) == e) = (!c || (t == e)) := by
|
||||
intro c t e
|
||||
cases c <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_same_else' :
|
||||
∀ (c : Bool) (t e : BitVec w), (e == (bif c then t else e)) = (!c || (t == e)) := by
|
||||
intro c t e
|
||||
cases c <;> simp [BEq.comm (a := t) (b := e)]
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_ite (cond : Bool) {a b c : α} :
|
||||
(bif cond then (bif cond then a else b) else c) = (bif cond then a else c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_not_ite (cond : Bool) {a b c : Bool} :
|
||||
(bif cond then !(bif cond then a else b) else c) = (bif cond then !a else c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_then_not_ite (cond : Bool) {a b c : BitVec w} :
|
||||
(bif cond then ~~~(bif cond then a else b) else c) = (bif cond then ~~~a else c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_ite (cond : Bool) {a b c : α} :
|
||||
(bif cond then a else (bif cond then b else c)) = (bif cond then a else c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_not_ite (cond : Bool) {a b c : Bool} :
|
||||
(bif cond then a else !(bif cond then b else c)) = (bif cond then a else !c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_else_not_ite (cond : Bool) {a b c : BitVec w} :
|
||||
(bif cond then a else ~~~(bif cond then b else c)) = (bif cond then a else ~~~c) := by
|
||||
cases cond <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_ite' (c0 c1 : Bool) {a b : α} :
|
||||
(bif c0 then (bif c1 then a else b) else a) = (bif c0 && !c1 then b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_not_ite' (c0 c1 : Bool) {a b : Bool} :
|
||||
(bif c0 then !(bif c1 then !a else b) else a) = (bif c0 && !c1 then !b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_then_not_ite' (c0 c1 : Bool) {a b : BitVec w} :
|
||||
(bif c0 then ~~~(bif c1 then ~~~a else b) else a) = (bif c0 && !c1 then ~~~b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_ite' (c0 c1 : Bool) {a b : α} :
|
||||
(bif c0 then a else (bif c1 then a else b)) = (bif !c0 && !c1 then b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_not_ite' (c0 c1 : Bool) {a b : Bool} :
|
||||
(bif c0 then a else !(bif c1 then !a else b)) = (bif !c0 && !c1 then !b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_else_not_ite' (c0 c1 : Bool) {a b : BitVec w} :
|
||||
(bif c0 then a else ~~~(bif c1 then ~~~a else b)) = (bif !c0 && !c1 then ~~~b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_ite'' (c0 c1 : Bool) {a b : α} :
|
||||
(bif c0 then (bif c1 then b else a) else a) = (bif c0 && c1 then b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_then_not_ite'' (c0 c1 : Bool) {a b : Bool} :
|
||||
(bif c0 then !(bif c1 then b else !a) else a) = (bif c0 && c1 then !b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_then_not_ite'' (c0 c1 : Bool) {a b : BitVec w} :
|
||||
(bif c0 then ~~~(bif c1 then b else ~~~a) else a) = (bif c0 && c1 then ~~~b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_ite'' (c0 c1 : Bool) {a b : α} :
|
||||
(bif c0 then a else (bif c1 then b else a)) = (bif !c0 && c1 then b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.ite_else_not_ite'' (c0 c1 : Bool) {a b : Bool} :
|
||||
(bif c0 then a else !(bif c1 then b else !a)) = (bif !c0 && c1 then !b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.ite_else_not_ite'' (c0 c1 : Bool) {a b : BitVec w} :
|
||||
(bif c0 then a else ~~~(bif c1 then b else ~~~a )) = (bif !c0 && c1 then ~~~b else a) := by
|
||||
cases c0 <;> cases c1 <;> simp
|
||||
|
||||
theorem Bool.and_left (lhs rhs : Bool) (h : (lhs && rhs) = true) : lhs = true := by
|
||||
revert lhs rhs
|
||||
|
||||
@@ -54,9 +54,8 @@ theorem Bool.and_to_and (a b : Bool) : ((a = true) ∧ (b = true)) = ((a && b) =
|
||||
simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.iff_to_or (a b : Bool)
|
||||
: ((a = true) ↔ (b = true)) = (((!a || b) && (!b || a)) = true) := by
|
||||
revert a b
|
||||
theorem Bool.iff_to_beq :
|
||||
∀ (a b : Bool), ((a = true) ↔ (b = true)) = ((a == b) = true) := by
|
||||
decide
|
||||
|
||||
@[bv_normalize]
|
||||
@@ -67,10 +66,6 @@ theorem Bool.eq_false (a : Bool) : ((a = true) = False) = ((!a) = true) := by
|
||||
theorem Bool.decide_eq_true (a : Bool) : (decide (a = true)) = a := by
|
||||
simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.eq_true_eq_true_eq (x y : Bool) : ((x = true) = (y = true)) ↔ ((x == y) = true) := by
|
||||
simp
|
||||
|
||||
attribute [bv_normalize] BitVec.getLsbD_cast
|
||||
attribute [bv_normalize] BitVec.testBit_toNat
|
||||
|
||||
@@ -80,10 +75,13 @@ theorem BitVec.lt_ult (x y : BitVec w) : (x < y) = (BitVec.ult x y = true) := by
|
||||
simp only [(· < ·)]
|
||||
simp
|
||||
|
||||
@[bv_normalize]
|
||||
theorem Bool.or_elim : ∀ (a b : Bool), (a || b) = !(!a && !b) := by decide
|
||||
|
||||
@[bv_normalize]
|
||||
theorem BitVec.or_elim (x y : BitVec w) : x ||| y = ~~~(~~~x &&& ~~~y) := by
|
||||
ext
|
||||
simp_all
|
||||
simp
|
||||
|
||||
attribute [bv_normalize] BitVec.natCast_eq_ofNat
|
||||
attribute [bv_normalize] BitVec.append_eq
|
||||
|
||||
@@ -92,10 +92,13 @@ def renderProgress (running unfinished : Array OpaqueJob) (h : 0 < unfinished.si
|
||||
(spinnerFrames[s.spinnerIdx], {s with spinnerIdx := s.spinnerIdx + ⟨1, by decide⟩})
|
||||
let resetCtrl ← modifyGet fun s => (s.resetCtrl, {s with resetCtrl := Ansi.resetLine})
|
||||
let caption :=
|
||||
-- Prefer the newest running job.
|
||||
-- This avoids the monitor focusing too long on any one job.
|
||||
-- (e.g., "Running job computation")
|
||||
if _ : 0 < running.size then
|
||||
s!"Running {running[0].caption} (+ {running.size - 1} more)"
|
||||
s!"Running {running[running.size - 1].caption} (+ {running.size - 1} more)"
|
||||
else
|
||||
s!"Running {unfinished[0].caption}"
|
||||
s!"Running {unfinished[unfinished.size - 1].caption}"
|
||||
print s!"{resetCtrl}{spinnerIcon} [{jobNo}/{totalJobs}] {caption}"
|
||||
flush
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ open Lean
|
||||
|
||||
namespace Lake
|
||||
|
||||
initialize
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
ref := by exact decl_name%
|
||||
name := `test_runner
|
||||
|
||||
@@ -9,37 +9,37 @@ import Lake.Util.OrderedTagAttribute
|
||||
open Lean
|
||||
namespace Lake
|
||||
|
||||
initialize packageAttr : OrderedTagAttribute ←
|
||||
builtin_initialize packageAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `package "mark a definition as a Lake package configuration"
|
||||
|
||||
initialize packageDepAttr : OrderedTagAttribute ←
|
||||
builtin_initialize packageDepAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `package_dep "mark a definition as a Lake package dependency"
|
||||
|
||||
initialize postUpdateAttr : OrderedTagAttribute ←
|
||||
builtin_initialize postUpdateAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `post_update "mark a definition as a Lake package post-update hook"
|
||||
|
||||
initialize scriptAttr : OrderedTagAttribute ←
|
||||
builtin_initialize scriptAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `script "mark a definition as a Lake script"
|
||||
|
||||
initialize defaultScriptAttr : OrderedTagAttribute ←
|
||||
builtin_initialize defaultScriptAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `default_script "mark a Lake script as the package's default"
|
||||
fun name => do
|
||||
unless (← getEnv <&> (scriptAttr.hasTag · name)) do
|
||||
throwError "attribute `default_script` can only be used on a `script`"
|
||||
|
||||
initialize leanLibAttr : OrderedTagAttribute ←
|
||||
builtin_initialize leanLibAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `lean_lib "mark a definition as a Lake Lean library target configuration"
|
||||
|
||||
initialize leanExeAttr : OrderedTagAttribute ←
|
||||
builtin_initialize leanExeAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `lean_exe "mark a definition as a Lake Lean executable target configuration"
|
||||
|
||||
initialize externLibAttr : OrderedTagAttribute ←
|
||||
builtin_initialize externLibAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `extern_lib "mark a definition as a Lake external library target"
|
||||
|
||||
initialize targetAttr : OrderedTagAttribute ←
|
||||
builtin_initialize targetAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `target "mark a definition as a custom Lake target"
|
||||
|
||||
initialize defaultTargetAttr : OrderedTagAttribute ←
|
||||
builtin_initialize defaultTargetAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `default_target "mark a Lake target as the package's default"
|
||||
fun name => do
|
||||
let valid ← getEnv <&> fun env =>
|
||||
@@ -50,7 +50,7 @@ initialize defaultTargetAttr : OrderedTagAttribute ←
|
||||
unless valid do
|
||||
throwError "attribute `default_target` can only be used on a target (e.g., `lean_lib`, `lean_exe`)"
|
||||
|
||||
initialize testDriverAttr : OrderedTagAttribute ←
|
||||
builtin_initialize testDriverAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `test_driver "mark a Lake script, executable, or library as package's test driver"
|
||||
fun name => do
|
||||
let valid ← getEnv <&> fun env =>
|
||||
@@ -60,7 +60,7 @@ initialize testDriverAttr : OrderedTagAttribute ←
|
||||
unless valid do
|
||||
throwError "attribute `test_driver` can only be used on a `script`, `lean_exe`, or `lean_lib`"
|
||||
|
||||
initialize lintDriverAttr : OrderedTagAttribute ←
|
||||
builtin_initialize lintDriverAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `lint_driver "mark a Lake script or executable as package's linter"
|
||||
fun name => do
|
||||
let valid ← getEnv <&> fun env =>
|
||||
@@ -69,11 +69,11 @@ initialize lintDriverAttr : OrderedTagAttribute ←
|
||||
unless valid do
|
||||
throwError "attribute `lint_driver` can only be used on a `script` or `lean_exe`"
|
||||
|
||||
initialize moduleFacetAttr : OrderedTagAttribute ←
|
||||
builtin_initialize moduleFacetAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `module_facet "mark a definition as a Lake module facet"
|
||||
|
||||
initialize packageFacetAttr : OrderedTagAttribute ←
|
||||
builtin_initialize packageFacetAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `package_facet "mark a definition as a Lake package facet"
|
||||
|
||||
initialize libraryFacetAttr : OrderedTagAttribute ←
|
||||
builtin_initialize libraryFacetAttr : OrderedTagAttribute ←
|
||||
registerOrderedTagAttribute `library_facet "mark a definition as a Lake library facet"
|
||||
|
||||
@@ -28,7 +28,7 @@ during the Lakefile's elaboration.
|
||||
-/
|
||||
scoped syntax (name := dirConst) "__dir__" : term
|
||||
|
||||
@[term_elab dirConst]
|
||||
@[builtin_term_elab dirConst]
|
||||
def elabDirConst : TermElab := fun stx expectedType? => do
|
||||
let exp :=
|
||||
if let some dir := dirExt.getState (← getEnv) then
|
||||
@@ -48,7 +48,7 @@ or via the `with` clause in a `require` statement.
|
||||
-/
|
||||
scoped syntax (name := getConfig) "get_config? " ident :term
|
||||
|
||||
@[term_elab getConfig]
|
||||
@[builtin_term_elab getConfig]
|
||||
def elabGetConfig : TermElab := fun stx expectedType? => do
|
||||
tryPostponeIfNoneOrMVar expectedType?
|
||||
match stx with
|
||||
|
||||
@@ -10,8 +10,8 @@ open Lean
|
||||
|
||||
namespace Lake
|
||||
|
||||
initialize dirExt : EnvExtension (Option System.FilePath) ←
|
||||
builtin_initialize dirExt : EnvExtension (Option System.FilePath) ←
|
||||
registerEnvExtension (pure none)
|
||||
|
||||
initialize optsExt : EnvExtension (Option (NameMap String)) ←
|
||||
builtin_initialize optsExt : EnvExtension (Option (NameMap String)) ←
|
||||
registerEnvExtension (pure none)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user