mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 03:14:08 +00:00
Compare commits
80 Commits
array_repl
...
release_no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03f8c5a99d | ||
|
|
aa8faae576 | ||
|
|
2f8901d6d0 | ||
|
|
9ff8c5ac2d | ||
|
|
48491e5262 | ||
|
|
9f5cc7262b | ||
|
|
957beb02bc | ||
|
|
017a1f2b94 | ||
|
|
f8f1b2212a | ||
|
|
dab6a161bd | ||
|
|
8e47d29bf9 | ||
|
|
e337129108 | ||
|
|
d3eb2fe13c | ||
|
|
d2239a5770 | ||
|
|
a244b06882 | ||
|
|
0a55f4bf36 | ||
|
|
e7a411a66d | ||
|
|
783671261d | ||
|
|
01d951c3fc | ||
|
|
6cf3402f1c | ||
|
|
e3c6909ad5 | ||
|
|
255810db64 | ||
|
|
f094652481 | ||
|
|
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 | ||
|
|
2e66341f69 | ||
|
|
2e44585ce9 | ||
|
|
e2f0e14b04 | ||
|
|
e801dc96ca | ||
|
|
56a3ac1814 | ||
|
|
6c62f720c8 | ||
|
|
a57efd0a88 | ||
|
|
7e2d6e2254 | ||
|
|
4603e1a6ad | ||
|
|
550d2918b8 | ||
|
|
eb5ad2c03a | ||
|
|
769fe4ebf6 | ||
|
|
8130fdc474 | ||
|
|
41bba59868 | ||
|
|
115f06c32a | ||
|
|
1e1e17cb35 | ||
|
|
831e8d768b | ||
|
|
b4b878b2d0 | ||
|
|
2377f35426 | ||
|
|
c7f706baeb |
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
|
||||
|
||||
@@ -25,7 +25,10 @@ cp llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
|
||||
echo '
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
|
||||
#define SEM_FAILCRITICALERRORS 0x0001
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);' > stage1/include/clang/windows.h
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
|
||||
#define CP_UTF8 65001
|
||||
__declspec(dllimport) __stdcall int SetConsoleOutputCP(unsigned int wCodePageID);' > stage1/include/clang/windows.h
|
||||
# COFF dependencies
|
||||
cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
|
||||
# runtime
|
||||
|
||||
@@ -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))
|
||||
@@ -168,7 +170,12 @@ def main():
|
||||
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
|
||||
print(f"## {section_title}\n")
|
||||
for _, entry in sorted(entries, key=lambda x: x[0]):
|
||||
print(f"* {entry}\n")
|
||||
# Split entry into lines and indent all lines after the first
|
||||
lines = entry.splitlines()
|
||||
print(f"* {lines[0]}")
|
||||
for line in lines[1:]:
|
||||
print(f" {line}")
|
||||
print() # Empty line after each entry
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 18)
|
||||
set(LEAN_VERSION_MINOR 19)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -49,6 +49,14 @@ theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = fal
|
||||
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b → b == c → a == c :=
|
||||
PartialEquivBEq.trans
|
||||
|
||||
theorem BEq.congr_left [BEq α] [PartialEquivBEq α] {a b c : α} (h : a == b) :
|
||||
(a == c) = (b == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨BEq.trans (BEq.symm h), BEq.trans h⟩
|
||||
|
||||
theorem BEq.congr_right [BEq α] [PartialEquivBEq α] {a b c : α} (h : b == c) :
|
||||
(a == b) = (a == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨fun h' => BEq.trans h' h, fun h' => BEq.trans h' (BEq.symm h)⟩
|
||||
|
||||
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
|
||||
(a == b) = false → b == c → (a == c) = false :=
|
||||
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1467,6 +1473,16 @@ theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi
|
||||
simp [hk, show k ≤ hi - lo by omega]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem ne_not_self {a : BitVec w} (h : 0 < w) : a ≠ ~~~a := by
|
||||
have : ∃ x, x < w := ⟨w - 1, by omega⟩
|
||||
simp [BitVec.eq_of_getElem_eq_iff, this]
|
||||
|
||||
@[simp]
|
||||
theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a ≠ a := by
|
||||
rw [ne_comm]
|
||||
simp [h]
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
|
||||
@@ -2693,6 +2709,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 +4128,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
|
||||
@@ -1006,6 +1005,474 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [h, ←Int.sub_eq_add_neg, Int.sub_self]
|
||||
|
||||
def Poly.isUnsatDiseq (p : Poly) : Bool :=
|
||||
match p with
|
||||
| .num 0 => true
|
||||
| _ => false
|
||||
|
||||
theorem diseq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx ≠ 0 → p₂.denote' ctx ≠ 0 := by
|
||||
simp at h
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
simp at h
|
||||
simp [*]
|
||||
|
||||
theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p' k → p.denote' ctx ≠ 0 → p'.denote' ctx ≠ 0 := by
|
||||
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
|
||||
|
||||
def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
let a := p₁.coeff x
|
||||
let b := p₂.coeff x
|
||||
a != 0 && p₃ == (p₁.mul b |>.combine (p₂.mul (-a)))
|
||||
|
||||
theorem eq_diseq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: diseq_eq_subst_cert x p₁ p₂ p₃ → p₁.denote' ctx = 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≠ 0 := by
|
||||
simp [diseq_eq_subst_cert]
|
||||
intros _ _; subst p₃
|
||||
intro h₁ h₂
|
||||
simp [*]
|
||||
|
||||
theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: eq_of_core_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ p₂.denote' ctx → p₃.denote' ctx ≠ 0 := by
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
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_one {p} : OrOver 1 p → p 0 := 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
|
||||
|
||||
@@ -80,9 +80,9 @@ instance : OfScientific Float32 where
|
||||
def Float32.ofNat (n : Nat) : Float32 :=
|
||||
OfScientific.ofScientific n false 0
|
||||
|
||||
def Float32.ofInt : Int → Float
|
||||
| Int.ofNat n => Float.ofNat n
|
||||
| Int.negSucc n => Float.neg (Float.ofNat (Nat.succ n))
|
||||
def Float32.ofInt : Int → Float32
|
||||
| Int.ofNat n => Float32.ofNat n
|
||||
| Int.negSucc n => Float32.neg (Float32.ofNat (Nat.succ n))
|
||||
|
||||
instance : OfNat Float32 n := ⟨Float32.ofNat n⟩
|
||||
|
||||
|
||||
@@ -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,9 +5,94 @@ 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
|
||||
@[simp] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem UInt64.toBitVec_toInt64 (x : UInt64) : x.toInt64.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem USize.toBitVec_toISize (x : USize) : x.toISize.toBitVec = x.toBitVec := rfl
|
||||
|
||||
@[simp] theorem Int8.ofBitVec_uInt8ToBitVec (x : UInt8) : Int8.ofBitVec x.toBitVec = x.toInt8 := rfl
|
||||
@[simp] theorem Int16.ofBitVec_uInt16ToBitVec (x : UInt16) : Int16.ofBitVec x.toBitVec = x.toInt16 := rfl
|
||||
@[simp] theorem Int32.ofBitVec_uInt32ToBitVec (x : UInt32) : Int32.ofBitVec x.toBitVec = x.toInt32 := rfl
|
||||
@[simp] theorem Int64.ofBitVec_uInt64ToBitVec (x : UInt64) : Int64.ofBitVec x.toBitVec = x.toInt64 := rfl
|
||||
@[simp] theorem ISize.ofBitVec_uSize8ToBitVec (x : USize) : ISize.ofBitVec x.toBitVec = x.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUInt8_toInt8 (x : UInt8) : x.toInt8.toUInt8 = x := rfl
|
||||
@[simp] theorem UInt16.toUInt16_toInt16 (x : UInt16) : x.toInt16.toUInt16 = x := rfl
|
||||
@[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)
|
||||
@@ -295,6 +297,51 @@ theorem UInt16.toNat_lt_usizeSize (n : UInt16) : n.toNat < USize.size :=
|
||||
theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
|
||||
|
||||
theorem UInt8.size_dvd_usizeSize : UInt8.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem UInt16.size_dvd_usizeSize : UInt16.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem UInt32.size_dvd_usizeSize : UInt32.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem USize.size_dvd_uInt64Size : USize.size ∣ UInt64.size := by cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
@[simp] theorem mod_usizeSize_uInt8Size (n : Nat) : n % USize.size % UInt8.size = n % UInt8.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt8.size_dvd_usizeSize
|
||||
@[simp] theorem mod_usizeSize_uInt16Size (n : Nat) : n % USize.size % UInt16.size = n % UInt16.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt16.size_dvd_usizeSize
|
||||
@[simp] theorem mod_usizeSize_uInt32Size (n : Nat) : n % USize.size % UInt32.size = n % UInt32.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt32.size_dvd_usizeSize
|
||||
@[simp] theorem mod_uInt64Size_uSizeSize (n : Nat) : n % UInt64.size % USize.size = n % USize.size :=
|
||||
Nat.mod_mod_of_dvd _ USize.size_dvd_uInt64Size
|
||||
|
||||
@[simp] theorem UInt8.toNat_mod_size (n : UInt8) : n.toNat % UInt8.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt8.toNat_mod_uInt16Size (n : UInt8) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uInt32Size (n : UInt8) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uInt64Size (n : UInt8) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uSizeSize (n : UInt8) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt16.toNat_mod_size (n : UInt16) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt16.toNat_mod_uInt32Size (n : UInt16) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt16.toNat_mod_uInt64Size (n : UInt16) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt16.toNat_mod_uSizeSize (n : UInt16) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt32.toNat_mod_size (n : UInt32) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt32.toNat_mod_uInt64Size (n : UInt32) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt32.toNat_mod_uSizeSize (n : UInt32) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt64.toNat_mod_size (n : UInt64) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
|
||||
@[simp] theorem USize.toNat_mod_size (n : USize) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_size
|
||||
@[simp] theorem USize.toNat_mod_uInt64Size (n : USize) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_mod_256 (n : UInt8) : n.toUInt16 % 256 = n.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_mod_256 (n : UInt8) : n.toUInt32 % 256 = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt64_mod_256 (n : UInt8) : n.toUInt64 % 256 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUSize_mod_256 (n : UInt8) : n.toUSize % 256 = n.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_mod_65536 (n : UInt16) : n.toUInt32 % 65536 = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt64_mod_65536 (n : UInt16) : n.toUInt64 % 65536 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_mod_65536 (n : UInt16) : n.toUSize % 65536 = n.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_mod_4294967296 (n : UInt32) : n.toUInt64 % 4294967296 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem Fin.mk_uInt8ToNat (n : UInt8) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@[simp] theorem Fin.mk_uInt16ToNat (n : UInt16) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@[simp] theorem Fin.mk_uInt32ToNat (n : UInt32) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@@ -328,7 +375,7 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
@[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
|
||||
@@ -349,14 +396,14 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
@[simp] theorem UInt16.toBitVec_toUInt64 (n : UInt16) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toUInt64 (n : UInt32) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
|
||||
@[simp] theorem USize.toBitVec_toUInt64 (n : USize) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt (USize.toNat_lt _)])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_toUSize (n : UInt8) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt16.toBitVec_toUSize (n : UInt16) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt32.toBitVec_toUSize (n : UInt32) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt64.toBitVec_toUSize (n : UInt64) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
@@ -420,3 +467,321 @@ theorem USize.ofNatLT_uInt64ToNat (n : UInt64) (h) : USize.ofNatLT n.toNat h = n
|
||||
@[simp] theorem USize.ofFin_uint8ToFin (n : UInt8) : USize.ofFin (n.toFin.castLE UInt8.size_le_usizeSize) = n.toUSize := rfl
|
||||
@[simp] theorem USize.ofFin_uint16ToFin (n : UInt16) : USize.ofFin (n.toFin.castLE UInt16.size_le_usizeSize) = n.toUSize := rfl
|
||||
@[simp] theorem USize.ofFin_uint32ToFin (n : UInt32) : USize.ofFin (n.toFin.castLE UInt32.size_le_usizeSize) = n.toUSize := rfl
|
||||
|
||||
@[simp] theorem Nat.toUInt8_eq {n : Nat} : n.toUInt8 = UInt8.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt16_eq {n : Nat} : n.toUInt16 = UInt16.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt32_eq {n : Nat} : n.toUInt32 = UInt32.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt64_eq {n : Nat} : n.toUInt64 = UInt64.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUSize_eq {n : Nat} : n.toUSize = USize.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.ofNat_uInt16ToNat (n : UInt16) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uInt32ToNat (n : UInt32) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uInt64ToNat (n : UInt64) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uSizeToNat (n : USize) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt16.ofNat_uInt8ToNat (n : UInt8) : UInt16.ofNat n.toNat = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.ofNat_uInt32ToNat (n : UInt32) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofNat_uInt64ToNat (n : UInt64) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofNat_uSizeToNat (n : USize) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem UInt32.ofNat_uInt8ToNat (n : UInt8) : UInt32.ofNat n.toNat = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.ofNat_uInt16ToNat (n : UInt16) : UInt32.ofNat n.toNat = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.ofNat_uInt64ToNat (n : UInt64) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofNat_uSizeToNat (n : USize) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
|
||||
|
||||
@[simp] theorem UInt64.ofNat_uInt8ToNat (n : UInt8) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uInt16ToNat (n : UInt16) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uInt32ToNat (n : UInt32) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uSizeToNat (n : USize) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.ofNat_uInt8ToNat (n : UInt8) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt16ToNat (n : UInt16) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt32ToNat (n : UInt32) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt64ToNat (n : UInt64) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
theorem UInt8.ofNatLT_eq_ofNat (n : Nat) {h} : UInt8.ofNatLT n h = UInt8.ofNat n :=
|
||||
UInt8.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt16.ofNatLT_eq_ofNat (n : Nat) {h} : UInt16.ofNatLT n h = UInt16.ofNat n :=
|
||||
UInt16.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt32.ofNatLT_eq_ofNat (n : Nat) {h} : UInt32.ofNatLT n h = UInt32.ofNat n :=
|
||||
UInt32.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt64.ofNatLT_eq_ofNat (n : Nat) {h} : UInt64.ofNatLT n h = UInt64.ofNat n :=
|
||||
UInt64.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem USize.ofNatLT_eq_ofNat (n : Nat) {h} : USize.ofNatLT n h = USize.ofNat n :=
|
||||
USize.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
|
||||
theorem UInt8.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt8.size) :
|
||||
UInt8.ofNatTruncate n = UInt8.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt8.ofNatLT_eq_ofNat]
|
||||
theorem UInt16.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt16.size) :
|
||||
UInt16.ofNatTruncate n = UInt16.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt16.ofNatLT_eq_ofNat]
|
||||
theorem UInt32.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt32.size) :
|
||||
UInt32.ofNatTruncate n = UInt32.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt32.ofNatLT_eq_ofNat]
|
||||
theorem UInt64.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt64.size) :
|
||||
UInt64.ofNatTruncate n = UInt64.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt64.ofNatLT_eq_ofNat]
|
||||
theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
|
||||
USize.ofNatTruncate n = USize.ofNat n := by
|
||||
simp [ofNatTruncate, hn, USize.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem UInt8.ofNatTruncate_toNat (n : UInt8) : UInt8.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt8.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt16.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt16.ofNatTruncate n.toNat = n.toUInt16 := by
|
||||
rw [UInt16.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt16.ofNatTruncate_toNat (n : UInt16) : UInt16.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt16.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt32.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt32.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt32.ofNatTruncate_toNat (n : UInt32) : UInt32.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt32ToNat (n : UInt32) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_toNat (n : UInt64) : UInt64.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
@[simp] theorem UInt64.ofNatTruncate_uSizeToNat (n : USize) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uSizeToNat]
|
||||
exact n.toNat_lt
|
||||
|
||||
@[simp] theorem USize.ofNatTruncate_uInt8ToNat (n : UInt8) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_uInt16ToNat (n : UInt16) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_uInt32ToNat (n : UInt32) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_toNat (n : USize) : USize.ofNatTruncate n.toNat = n := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt_size]
|
||||
|
||||
@[simp] theorem UInt8.toUInt8_toUInt16 (n : UInt8) : n.toUInt16.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUInt32 (n : UInt8) : n.toUInt32.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUInt64 (n : UInt8) : n.toUInt64.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUSize (n : UInt8) : n.toUSize.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_toUInt32 (n : UInt8) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt16_toUInt64 (n : UInt8) : n.toUInt64.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt16_toUSize (n : UInt8) : n.toUSize.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt32_toUInt16 (n : UInt8) : n.toUInt16.toUInt32 = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_toUInt64 (n : UInt8) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_toUSize (n : UInt8) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt64_toUInt16 (n : UInt8) : n.toUInt16.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_toUInt32 (n : UInt8) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_toUSize (n : UInt8) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUSize_toUInt16 (n : UInt8) : n.toUInt16.toUSize = n.toUSize := rfl
|
||||
@[simp] theorem UInt8.toUSize_toUInt32 (n : UInt8) : n.toUInt32.toUSize = n.toUSize := rfl
|
||||
@[simp] theorem UInt8.toUSize_toUInt64 (n : UInt8) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_toUInt32 (n : UInt16) : n.toUInt32.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt16.toUInt8_toUInt64 (n : UInt16) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt16.toUInt8_toUSize (n : UInt16) : n.toUSize.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt16_toUInt8 (n : UInt16) : n.toUInt8.toUInt16 = n % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt16_toUInt32 (n : UInt16) : n.toUInt32.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt16_toUInt64 (n : UInt16) : n.toUInt64.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt16_toUSize (n : UInt16) : n.toUSize.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_toUInt8 (n : UInt16) : n.toUInt8.toUInt32 = n.toUInt32 % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt32_toUInt64 (n : UInt16) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt32_toUSize (n : UInt16) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt64_toUInt8 (n : UInt16) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_toUInt32 (n : UInt16) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_toUSize (n : UInt16) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUSize_toUInt8 (n : UInt16) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_toUInt32 (n : UInt16) : n.toUInt32.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_toUInt64 (n : UInt16) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_toUInt16 (n : UInt32) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_toUInt64 (n : UInt32) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt32.toUInt8_toUSize (n : UInt32) : n.toUSize.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_toUInt8 (n : UInt32) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_toUInt64 (n : UInt32) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt32.toUInt16_toUSize (n : UInt32) : n.toUSize.toUInt16 = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt32_toUInt8 (n : UInt32) : n.toUInt8.toUInt32 = n % 256 := rfl
|
||||
@[simp] theorem UInt32.toUInt32_toUInt16 (n : UInt32) : n.toUInt16.toUInt32 = n % 65536 := rfl
|
||||
@[simp] theorem UInt32.toUInt32_toUInt64 (n : UInt32) : n.toUInt64.toUInt32 = n :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt32_toUSize (n : UInt32) : n.toUSize.toUInt32 = n :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_toUInt8 (n : UInt32) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem UInt32.toUInt64_toUInt16 (n : UInt32) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
|
||||
@[simp] theorem UInt32.toUInt64_toUSize (n : UInt32) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUSize_toUInt8 (n : UInt32) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUSize_toUInt16 (n : UInt32) : n.toUInt16.toUSize = n.toUSize % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUSize_toUInt64 (n : UInt32) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_toUInt16 (n : UInt64) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_toUInt32 (n : UInt64) : n.toUInt32.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_toUSize (n : UInt64) : n.toUSize.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt16_toUInt8 (n : UInt64) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_toUInt32 (n : UInt64) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_toUSize (n : UInt64) : n.toUSize.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_toUInt8 (n : UInt64) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_toUInt16 (n : UInt64) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_toUSize (n : UInt64) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt64_toUInt8 (n : UInt64) : n.toUInt8.toUInt64 = n % 256 := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toUInt16 (n : UInt64) : n.toUInt16.toUInt64 = n % 65536 := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toUInt32 (n : UInt64) : n.toUInt32.toUInt64 = n % 4294967296 := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUSize_toUInt8 (n : UInt64) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUSize_toUInt16 (n : UInt64) : n.toUInt16.toUSize = n.toUSize % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.toUInt8_toUInt16 (n : USize) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_toUInt32 (n : USize) : n.toUInt32.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_toUInt64 (n : USize) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt16_toUInt8 (n : USize) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_toUInt32 (n : USize) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_toUInt64 (n : USize) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt64_toUInt8 (n : USize) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem USize.toUInt64_toUInt16 (n : USize) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt32_toUInt8 (n : USize) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_toUInt16 (n : USize) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_toUInt64 (n : USize) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.toUSize_toUInt8 (n : USize) : n.toUInt8.toUSize = n % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUSize_toUInt16 (n : USize) : n.toUInt16.toUSize = n % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUSize_toUInt64 (n : USize) : n.toUInt64.toUSize = n :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
-- Note: we are currently missing the following four results for which there does not seem to
|
||||
-- be a good candidate for the RHS:
|
||||
-- @[simp] theorem UInt64.toUInt64_toUSize (n : UInt64) : n.toUSize.toUInt64 = ? :=
|
||||
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
|
||||
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
|
||||
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
@@ -69,6 +69,11 @@ theorem eq_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a = b) = a := by s
|
||||
theorem eq_congr {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = a₂) (h₂ : b₁ = b₂) : (a₁ = b₁) = (a₂ = b₂) := by simp [*]
|
||||
theorem eq_congr' {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = b₂) (h₂ : b₁ = a₂) : (a₁ = b₁) = (a₂ = b₂) := by rw [h₁, h₂, Eq.comm (a := a₂)]
|
||||
|
||||
/-! Ne -/
|
||||
|
||||
theorem ne_of_ne_of_eq_left {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b ≠ c) : a ≠ c := by simp [*]
|
||||
theorem ne_of_ne_of_eq_right {α : Sort u} {a b c : α} (h₁ : a = c) (h₂ : b ≠ c) : b ≠ a := by simp [*]
|
||||
|
||||
/-! Bool.and -/
|
||||
|
||||
theorem Bool.and_eq_of_eq_true_left {a b : Bool} (h : a = true) : (a && b) = b := by simp [h]
|
||||
|
||||
@@ -69,6 +69,10 @@ structure Config where
|
||||
verbose : Bool := true
|
||||
/-- If `clean` is `true`, `grind` uses `expose_names` and only generates accessible names. -/
|
||||
clean : Bool := true
|
||||
/--
|
||||
If `qlia` is `true`, `grind` may generate counterexamples for integer constraints using rational numbers.
|
||||
This approach is cheaper but incomplete. -/
|
||||
qlia : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -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
|
||||
|
||||
/--
|
||||
|
||||
@@ -57,6 +57,11 @@ def EIO.catchExceptions (act : EIO ε α) (h : ε → BaseIO α) : BaseIO α :=
|
||||
| EStateM.Result.ok a s => EStateM.Result.ok a s
|
||||
| EStateM.Result.error ex s => h ex s
|
||||
|
||||
def EIO.ofExcept (e : Except ε α) : EIO ε α :=
|
||||
match e with
|
||||
| Except.ok a => pure a
|
||||
| Except.error e => throw e
|
||||
|
||||
open IO (Error) in
|
||||
abbrev IO : Type → Type := EIO Error
|
||||
|
||||
|
||||
@@ -48,7 +48,9 @@ inductive IO.Error where
|
||||
|
||||
| unexpectedEof
|
||||
| userError (msg : String)
|
||||
deriving Inhabited
|
||||
|
||||
instance : Inhabited IO.Error where
|
||||
default := .userError "(`Inhabited.default` for `IO.Error`)"
|
||||
|
||||
@[export lean_mk_io_user_error]
|
||||
def IO.userError (s : String) : IO.Error :=
|
||||
|
||||
@@ -73,5 +73,5 @@ def Promise.result := @Promise.result!
|
||||
/--
|
||||
Like `Promise.result`, but resolves to `dflt` if the promise is dropped without ever being resolved.
|
||||
-/
|
||||
def Promise.resultD (promise : Promise α) (dflt : α): Task α :=
|
||||
@[macro_inline] def Promise.resultD (promise : Promise α) (dflt : α) : Task α :=
|
||||
promise.result?.map (sync := true) (·.getD dflt)
|
||||
|
||||
@@ -8,12 +8,6 @@ import Lean.CoreM
|
||||
|
||||
namespace Lean
|
||||
|
||||
register_builtin_option debug.skipKernelTC : Bool := {
|
||||
defValue := false
|
||||
group := "debug"
|
||||
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
|
||||
}
|
||||
|
||||
/-- Adds given declaration to the environment, respecting `debug.skipKernelTC`. -/
|
||||
def Kernel.Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except Exception Environment :=
|
||||
|
||||
@@ -252,6 +252,13 @@ def registerEnumAttributes (attrDescrs : List (Name × String × α))
|
||||
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[]
|
||||
r.qsort (fun a b => Name.quickLt a.1 b.1)
|
||||
statsFn := fun s => "enumeration attribute extension" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
-- We assume (and check below) that, if used asynchronously, enum attributes are set only in the
|
||||
-- same context in which the tagged declaration was created
|
||||
asyncMode := .async
|
||||
replay? := some fun _ newState consts st => consts.foldl (init := st) fun st c =>
|
||||
match newState.find? c with
|
||||
| some v => st.insert c v
|
||||
| _ => st
|
||||
}
|
||||
let attrs := attrDescrs.map fun (name, descr, val) => {
|
||||
ref := ref
|
||||
@@ -279,15 +286,16 @@ def getValue [Inhabited α] (attr : EnumAttributes α) (env : Environment) (decl
|
||||
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, default) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some (_, val) => some val
|
||||
| none => none
|
||||
| none => (attr.ext.getState env).find? decl
|
||||
| none => (attr.ext.findStateAsync env decl).find? decl
|
||||
|
||||
def setValue (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment :=
|
||||
def setValue (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment := do
|
||||
if (env.getModuleIdxFor? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, declaration is in an imported module")
|
||||
else if ((attrs.ext.getState env).find? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, attribute has already been set")
|
||||
else
|
||||
Except.ok (attrs.ext.addEntry env (decl, val))
|
||||
throw s!"invalid '{attrs.ext.name}'.setValue, declaration is in an imported module"
|
||||
if !env.asyncMayContain decl then
|
||||
throw s!"invalid '{attrs.ext.name}'.setValue, declaration is not from this async context"
|
||||
if ((attrs.ext.findStateAsync env decl).find? decl).isSome then
|
||||
throw s!"invalid '{attrs.ext.name}'.setValue, attribute has already been set"
|
||||
return attrs.ext.addEntry env (decl, val)
|
||||
|
||||
end EnumAttributes
|
||||
|
||||
|
||||
@@ -11,10 +11,17 @@ namespace Lean
|
||||
structure ClosedTermCache where
|
||||
map : PHashMap Expr Name := {}
|
||||
constNames : NameSet := {}
|
||||
-- used for `replay?` only
|
||||
revExprs : List Expr := []
|
||||
deriving Inhabited
|
||||
|
||||
builtin_initialize closedTermCacheExt : EnvExtension ClosedTermCache ←
|
||||
registerEnvExtension (pure {}) (asyncMode := .sync) -- compilation is non-parallel anyway
|
||||
(replay? := some fun oldState newState _ s =>
|
||||
let newExprs := newState.revExprs.take (newState.revExprs.length - oldState.revExprs.length)
|
||||
newExprs.foldl (init := s) fun s e =>
|
||||
let c := newState.map.find! e
|
||||
{ s with map := s.map.insert e c, constNames := s.constNames.insert c, revExprs := e :: s.revExprs })
|
||||
|
||||
@[export lean_cache_closed_term_name]
|
||||
def cacheClosedTermName (env : Environment) (e : Expr) (n : Name) : Environment :=
|
||||
|
||||
@@ -94,6 +94,7 @@ builtin_initialize declMapExt : SimplePersistentEnvExtension Decl DeclMap ←
|
||||
-- share a name prefix with the top-level Lean declaration being compiled, e.g. from
|
||||
-- specialization.
|
||||
asyncMode := .sync
|
||||
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.name) (fun s d => s.insert d.name d)
|
||||
}
|
||||
|
||||
@[export lean_ir_find_env_decl]
|
||||
|
||||
@@ -143,6 +143,7 @@ builtin_initialize functionSummariesExt : SimplePersistentEnvExtension (FunId ×
|
||||
addEntryFn := fun s ⟨e, n⟩ => s.insert e n
|
||||
toArrayFn := fun s => sortEntries s.toArray
|
||||
asyncMode := .sync -- compilation is non-parallel anyway
|
||||
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.1) (fun s ⟨e, n⟩ => s.insert e n)
|
||||
}
|
||||
|
||||
def addFunctionSummary (env : Environment) (fid : FunId) (v : Value) : Environment :=
|
||||
|
||||
@@ -155,6 +155,7 @@ def emitMainFn : M Unit := do
|
||||
int main(int argc, char ** argv) {
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
lean_object* in; lean_object* res;";
|
||||
if usesLeanAPI then
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -111,6 +111,9 @@ builtin_initialize specExtension : SimplePersistentEnvExtension SpecEntry SpecSt
|
||||
addEntryFn := SpecState.addEntry,
|
||||
addImportedFn := fun es => (mkStateFromImportedEntries SpecState.addEntry {} es).switch
|
||||
asyncMode := .sync -- compilation is non-parallel anyway
|
||||
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (fun
|
||||
| s, .info n _ => !s.specInfo.contains n
|
||||
| s, .cache key _ => !s.cache.contains key) SpecState.addEntry
|
||||
}
|
||||
|
||||
@[export lean_add_specialization_info]
|
||||
|
||||
@@ -194,7 +194,7 @@ protected def withFreshMacroScope (x : CoreM α) : CoreM α := do
|
||||
|
||||
instance : MonadQuotation CoreM where
|
||||
getCurrMacroScope := return (← read).currMacroScope
|
||||
getMainModule := return (← get).env.mainModule
|
||||
getMainModule := return (← getEnv).mainModule
|
||||
withFreshMacroScope := Core.withFreshMacroScope
|
||||
|
||||
instance : Elab.MonadInfoTree CoreM where
|
||||
@@ -365,6 +365,16 @@ for incremental reporting during elaboration of a single command.
|
||||
def getAndEmptyMessageLog : CoreM MessageLog :=
|
||||
modifyGet fun s => (s.messages, { s with messages := s.messages.markAllReported })
|
||||
|
||||
/--
|
||||
Returns the current set of tasks added by `logSnapshotTask` and then resets it. When
|
||||
saving/restoring state of an action that may have logged such tasks during incremental reuse, this
|
||||
function must be used to store them in the corresponding snapshot tree; otherwise, they will leak
|
||||
outside and may be cancelled by a later step, potentially leading to inconsistent state being
|
||||
reused.
|
||||
-/
|
||||
def getAndEmptySnapshotTasks : CoreM (Array (Language.SnapshotTask Language.SnapshotTree)) :=
|
||||
modifyGet fun s => (s.snapshotTasks, { s with snapshotTasks := #[] })
|
||||
|
||||
instance : MonadLog CoreM where
|
||||
getRef := getRef
|
||||
getFileMap := return (← read).fileMap
|
||||
@@ -413,6 +423,26 @@ register_builtin_option stderrAsMessages : Bool := {
|
||||
descr := "(server) capture output to the Lean stderr channel (such as from `dbg_trace`) during elaboration of a command as a diagnostic message"
|
||||
}
|
||||
|
||||
/--
|
||||
Creates snapshot reporting given `withIsolatedStreams` output and diagnostics and traces from the
|
||||
given state.
|
||||
-/
|
||||
def mkSnapshot (output : String) (ctx : Context) (st : State)
|
||||
(desc : String := by exact decl_name%.toString) : BaseIO Language.SnapshotTree := do
|
||||
let mut msgs := st.messages
|
||||
if !output.isEmpty then
|
||||
msgs := msgs.add {
|
||||
fileName := ctx.fileName
|
||||
severity := MessageSeverity.information
|
||||
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
|
||||
data := output
|
||||
}
|
||||
return .mk {
|
||||
desc
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgs)
|
||||
traces := st.traceState
|
||||
} st.snapshotTasks
|
||||
|
||||
open Language in
|
||||
/--
|
||||
Wraps the given action for use in `BaseIO.asTask` etc., discarding its final state except for
|
||||
@@ -443,20 +473,7 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (cancelTk? : Option IO.Cance
|
||||
let ctx ← readThe Core.Context
|
||||
return do
|
||||
match (← t.toBaseIO) with
|
||||
| .ok (output, st) =>
|
||||
let mut msgs := st.messages
|
||||
if !output.isEmpty then
|
||||
msgs := msgs.add {
|
||||
fileName := ctx.fileName
|
||||
severity := MessageSeverity.information
|
||||
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
|
||||
data := output
|
||||
}
|
||||
return .mk {
|
||||
desc
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgs)
|
||||
traces := st.traceState
|
||||
} st.snapshotTasks
|
||||
| .ok (output, st) => mkSnapshot output ctx st desc
|
||||
-- interrupt or abort exception as `try catch` above should have caught any others
|
||||
| .error _ => default
|
||||
|
||||
@@ -528,7 +545,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
|
||||
@@ -646,6 +665,11 @@ def logMessageKind (kind : Name) : CoreM Bool := do
|
||||
modify fun s => { s with messages.loggedKinds := s.messages.loggedKinds.insert kind }
|
||||
return true
|
||||
|
||||
@[inherit_doc Environment.enableRealizationsForConst]
|
||||
def enableRealizationsForConst (n : Name) : CoreM Unit := do
|
||||
let env ← (← getEnv).enableRealizationsForConst (← getOptions) n
|
||||
setEnv env
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.async
|
||||
registerTraceClass `Elab.block
|
||||
|
||||
@@ -1215,7 +1215,7 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
|
||||
let fullName := Name.mkStr structName fieldName
|
||||
for localDecl in (← getLCtx) do
|
||||
if localDecl.isAuxDecl then
|
||||
if let some localDeclFullName := (← read).auxDeclToFullName.find? localDecl.fvarId then
|
||||
if let some localDeclFullName := (← getLCtx).auxDeclToFullName.find? localDecl.fvarId then
|
||||
if fullName == (privateToUserName? localDeclFullName).getD localDeclFullName then
|
||||
/- LVal notation is being used to make a "local" recursive call. -/
|
||||
return LValResolution.localRec structName fullName localDecl.toExpr
|
||||
|
||||
@@ -201,12 +201,27 @@ private def elabTParserMacroAux (prec lhsPrec e : Term) : TermElabM Syntax := do
|
||||
|
||||
@[builtin_macro Lean.Parser.Term.assert] def expandAssert : Macro
|
||||
| `(assert! $cond; $body) =>
|
||||
-- TODO: support for disabling runtime assertions
|
||||
match cond.raw.reprint with
|
||||
| some code => `(if $cond then $body else panic! ("assertion violation: " ++ $(quote code)))
|
||||
| none => `(if $cond then $body else panic! ("assertion violation"))
|
||||
| _ => Macro.throwUnsupported
|
||||
|
||||
register_builtin_option debugAssertions : Bool := {
|
||||
defValue := false
|
||||
descr := "enable `debug_assert!` statements\
|
||||
\n\
|
||||
\nDefaults to `false` unless the Lake `buildType` is `debug`."
|
||||
}
|
||||
|
||||
@[builtin_term_elab Lean.Parser.Term.debugAssert] def elabDebugAssert : TermElab :=
|
||||
adaptExpander fun
|
||||
| `(Parser.Term.debugAssert| debug_assert! $cond; $body) => do
|
||||
if debugAssertions.get (← getOptions) then
|
||||
`(assert! $cond; $body)
|
||||
else
|
||||
return body
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_macro Lean.Parser.Term.dbgTrace] def expandDbgTrace : Macro
|
||||
| `(dbg_trace $arg:interpolatedStr; $body) => `(dbgTrace (s! $arg) fun _ => $body)
|
||||
| `(dbg_trace $arg:term; $body) => `(dbgTrace (toString $arg) fun _ => $body)
|
||||
|
||||
@@ -491,6 +491,8 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
|
||||
-- check absence of traces; see Note [Incremental Macros]
|
||||
guard <| !oldSnap.hasTraces && !hasTraces
|
||||
return oldSnap
|
||||
if snap.old?.isSome && oldSnap?.isNone then
|
||||
snap.old?.forM (·.val.cancelRec)
|
||||
let oldCmds? := oldSnap?.map fun old =>
|
||||
if old.newStx.isOfKind nullKind then old.newStx.getArgs else #[old.newStx]
|
||||
let cmdPromises ← cmds.mapM fun _ => IO.Promise.new
|
||||
@@ -519,6 +521,8 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
|
||||
let old ← oldSnap?
|
||||
return { stx := (← oldCmd?), val := (← old.next[i]?) }
|
||||
} }) do
|
||||
if oldSnap?.isSome && (← read).snap?.isNone then
|
||||
oldSnap?.bind (·.next[i]?) |>.forM (·.cancelRec)
|
||||
elabCommand cmd
|
||||
-- Resolve promise for commands not supporting incrementality; waiting for
|
||||
-- `withAlwaysResolvedPromises` to do this could block reporting by later
|
||||
|
||||
@@ -49,9 +49,11 @@ structure BodyProcessedSnapshot extends Language.Snapshot where
|
||||
state : Term.SavedState
|
||||
/-- Elaboration result. -/
|
||||
value : Expr
|
||||
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
|
||||
moreSnaps : Array (SnapshotTask SnapshotTree)
|
||||
deriving Nonempty
|
||||
instance : Language.ToSnapshotTree BodyProcessedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot, #[]⟩
|
||||
toSnapshotTree s := ⟨s.toSnapshot, s.moreSnaps⟩
|
||||
|
||||
/-- Snapshot after elaboration of a definition header. -/
|
||||
structure HeaderProcessedSnapshot extends Language.Snapshot where
|
||||
@@ -67,13 +69,15 @@ structure HeaderProcessedSnapshot extends Language.Snapshot where
|
||||
bodyStx : Syntax
|
||||
/-- Result of body elaboration. -/
|
||||
bodySnap : SnapshotTask (Option BodyProcessedSnapshot)
|
||||
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
|
||||
moreSnaps : Array (SnapshotTask SnapshotTree)
|
||||
deriving Nonempty
|
||||
instance : Language.ToSnapshotTree HeaderProcessedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot,
|
||||
(match s.tacSnap? with
|
||||
| some tac => #[tac.map (sync := true) toSnapshotTree]
|
||||
| none => #[]) ++
|
||||
#[s.bodySnap.map (sync := true) toSnapshotTree]⟩
|
||||
#[s.bodySnap.map (sync := true) toSnapshotTree] ++ s.moreSnaps⟩
|
||||
|
||||
/-- State before elaboration of a mutual definition. -/
|
||||
structure DefParsed where
|
||||
|
||||
@@ -134,7 +134,9 @@ partial def mkEnumOfNat (declName : Name) : MetaM Unit := do
|
||||
let enumType := mkConst declName
|
||||
let ctors := indVal.ctors.toArray
|
||||
withLocalDeclD `n (mkConst ``Nat) fun n => do
|
||||
let cond := mkConst ``cond [levelZero]
|
||||
-- After the next stage0 update, this can be reverted to
|
||||
-- let cond := mkConst ``cond [1]
|
||||
let cond := (← mkAppOptM ``cond #[enumType]).appFn!
|
||||
let rec mkDecTree (low high : Nat) : Expr :=
|
||||
if low + 1 == high then
|
||||
mkConst ctors[low]!
|
||||
|
||||
@@ -1036,6 +1036,9 @@ def seqToTerm (action : Syntax) (k : Syntax) : M Syntax := withRef action <| wit
|
||||
else if action.getKind == ``Parser.Term.doAssert then
|
||||
let cond := action[1]
|
||||
`(assert! $cond; $k)
|
||||
else if action.getKind == ``Parser.Term.doDebugAssert then
|
||||
let cond := action[1]
|
||||
`(debugAssert| debug_assert! $cond; $k)
|
||||
else
|
||||
let action ← withRef action ``(($action : $((←read).m) PUnit))
|
||||
``(Bind.bind $action (fun (_ : PUnit) => $k))
|
||||
@@ -1765,6 +1768,8 @@ mutual
|
||||
return mkSeq doElem (← doSeqToCode doElems)
|
||||
else if k == ``Parser.Term.doAssert then
|
||||
return mkSeq doElem (← doSeqToCode doElems)
|
||||
else if k == ``Parser.Term.doDebugAssert then
|
||||
return mkSeq doElem (← doSeqToCode doElems)
|
||||
else if k == ``Parser.Term.doNested then
|
||||
let nestedDoSeq := doElem[1]
|
||||
doSeqToCode (getDoSeqElems nestedDoSeq ++ doElems)
|
||||
|
||||
@@ -166,6 +166,8 @@ private def elabHeaders (views : Array DefView)
|
||||
view.value.eqWithInfoAndTraceReuse (← getOptions) old.bodyStx
|
||||
-- no syntax guard to store, we already did the necessary checks
|
||||
oldBodySnap? := guard reuseBody *> pure ⟨.missing, old.bodySnap⟩
|
||||
if oldBodySnap?.isNone then
|
||||
old.bodySnap.cancelRec
|
||||
oldTacSnap? := do
|
||||
guard reuseTac
|
||||
some ⟨(← old.tacStx?), (← old.tacSnap?)⟩
|
||||
@@ -229,6 +231,7 @@ private def elabHeaders (views : Array DefView)
|
||||
snap.new.resolve <| some {
|
||||
diagnostics :=
|
||||
(← Language.Snapshot.Diagnostics.ofMessageLog (← Core.getAndEmptyMessageLog))
|
||||
moreSnaps := (← Core.getAndEmptySnapshotTasks)
|
||||
view := newHeader.toDefViewElabHeaderData
|
||||
state := newState
|
||||
tacStx?
|
||||
@@ -428,6 +431,10 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
if let some old := old.val.get then
|
||||
snap.new.resolve <| some old
|
||||
reusableResult? := some (old.value, old.state)
|
||||
else
|
||||
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
|
||||
-- importantly kernel checking and compilation of the top-level declaration
|
||||
old.val.cancelRec
|
||||
|
||||
let (val, state) ← withRestoreOrSaveFull reusableResult? header.tacSnap? do
|
||||
withReuseContext header.value do
|
||||
@@ -479,6 +486,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
|
||||
snap.new.resolve <| some {
|
||||
diagnostics :=
|
||||
(← Language.Snapshot.Diagnostics.ofMessageLog (← Core.getAndEmptyMessageLog))
|
||||
moreSnaps := (← Core.getAndEmptySnapshotTasks)
|
||||
state
|
||||
value := val
|
||||
}
|
||||
@@ -1094,6 +1102,8 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
|
||||
return ⟨.missing, oldParsed.headerProcessedSnap⟩
|
||||
new := headerPromise
|
||||
} }
|
||||
if snap.old?.isSome && (view.headerSnap?.bind (·.old?)).isNone then
|
||||
snap.old?.forM (·.val.cancelRec)
|
||||
defs := defs.push {
|
||||
fullHeaderRef
|
||||
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default }
|
||||
|
||||
@@ -336,7 +336,7 @@ private def withInductiveLocalDecls (rs : Array PreElabHeaderResult) (x : Array
|
||||
let rec loop (i : Nat) (indFVars : Array Expr) := do
|
||||
if h : i < namesAndTypes.size then
|
||||
let (declName, shortDeclName, type) := namesAndTypes[i]
|
||||
Term.withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
|
||||
withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
|
||||
else
|
||||
x params indFVars
|
||||
loop 0 #[]
|
||||
@@ -931,6 +931,8 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
|
||||
for ctor in view.ctors do
|
||||
if (ctor.declId.getPos? (canonicalOnly := true)).isSome then
|
||||
Term.addTermInfo' ctor.declId (← mkConstWithLevelParams ctor.declName) (isBinder := true)
|
||||
enableRealizationsForConst ctor.declName
|
||||
enableRealizationsForConst view.declName
|
||||
return res
|
||||
|
||||
private def mkAuxConstructions (declNames : Array Name) : TermElabM Unit := do
|
||||
|
||||
@@ -161,6 +161,7 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
|
||||
if compile && shouldGenCodeFor preDef then
|
||||
compileDecl decl
|
||||
if applyAttrAfterCompilation then
|
||||
enableRealizationsForConst preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ Assign final attributes to the definitions. Assumes the EqnInfos to be already p
|
||||
def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
markAsRecursive preDef.declName
|
||||
enableRealizationsForConst preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
-- Unless the user asks for something else, mark the definition as irreducible
|
||||
|
||||
@@ -20,18 +20,23 @@ Simple, coarse-grained equation theorem for nonrecursive definitions.
|
||||
-/
|
||||
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
|
||||
if let some (.defnInfo info) := (← getEnv).find? declName then
|
||||
let name := declName ++ suffix
|
||||
-- determinism: `name` and `info` are dependent only on `declName`, not any later env
|
||||
-- modifications
|
||||
realizeConst declName name (doRealize name info)
|
||||
return some name
|
||||
else
|
||||
return none
|
||||
where
|
||||
doRealize name info :=
|
||||
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
|
||||
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
|
||||
let type ← mkForallFVars xs (← mkEq lhs body)
|
||||
let value ← mkLambdaFVars xs (← mkEqRefl lhs)
|
||||
let name := declName ++ suffix
|
||||
addDecl <| Declaration.thmDecl {
|
||||
addDecl <| .thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return some name
|
||||
else
|
||||
return none
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if (← isRecursiveDefinition declName) then
|
||||
|
||||
@@ -193,6 +193,10 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
for preDef in preDefs do
|
||||
-- must happen in separate loop so realizations can see eqnInfos of all other preDefs
|
||||
enableRealizationsForConst preDef.declName
|
||||
-- must happen after `enableRealizationsForConst`
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option T
|
||||
unless (← isProp preDef.type) do
|
||||
WF.mkUnfoldEq preDef preDefNonRec.declName wfPreprocessProof
|
||||
Mutual.addPreDefAttributes preDefs
|
||||
enableRealizationsForConst preDefNonRec.declName
|
||||
|
||||
builtin_initialize registerTraceClass `Elab.definition.wf
|
||||
|
||||
|
||||
@@ -100,4 +100,7 @@ def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessPr
|
||||
}
|
||||
trace[Elab.definition.wf] "mkUnfoldEq defined {.ofConstName name}"
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.wf.eqns
|
||||
|
||||
end Lean.Elab.WF
|
||||
|
||||
@@ -607,7 +607,9 @@ where
|
||||
let parentView := view.parents[i]
|
||||
withRef parentView.ref do
|
||||
-- The only use case for autobound implicits for parents might be outParams, but outParam is not propagated.
|
||||
let parentType ← whnf <| ← Term.withoutAutoBoundImplicit <| Term.elabType parentView.type
|
||||
let parentType ← Term.withoutAutoBoundImplicit <| Term.elabType parentView.type
|
||||
Term.synthesizeSyntheticMVarsNoPostponing
|
||||
let parentType ← whnf parentType
|
||||
if parentType.getAppFn == indFVar then
|
||||
logWarning "structure extends itself, skipping"
|
||||
return ← go (i + 1) infos parents
|
||||
@@ -825,20 +827,18 @@ private partial def checkResultingUniversesForFields (fieldInfos : Array StructF
|
||||
which is not less than or equal to the structure's resulting universe level{indentD u}"
|
||||
throwErrorAt info.ref msg
|
||||
|
||||
@[extern "lean_mk_projections"]
|
||||
private opaque mkProjections (env : Environment) (structName : Name) (projs : List Name) (isClass : Bool) : Except Kernel.Exception Environment
|
||||
|
||||
private def addProjections (r : ElabHeaderResult) (fieldInfos : Array StructFieldInfo) : TermElabM Unit := do
|
||||
if r.type.isProp then
|
||||
if let some fieldInfo ← fieldInfos.findM? (not <$> Meta.isProof ·.fvar) then
|
||||
throwErrorAt fieldInfo.ref m!"failed to generate projections for 'Prop' structure, field '{format fieldInfo.name}' is not a proof"
|
||||
let projNames := fieldInfos |>.filter (!·.isFromSubobject) |>.map (·.declName)
|
||||
let env ← getEnv
|
||||
let env ← ofExceptKernelException (mkProjections env r.view.declName projNames.toList r.view.isClass)
|
||||
setEnv env
|
||||
let projDecls : Array StructProjDecl :=
|
||||
fieldInfos
|
||||
|>.filter (!·.isFromSubobject)
|
||||
|>.map (fun info => { ref := info.ref, projName := info.declName })
|
||||
mkProjections r.view.declName projDecls r.view.isClass
|
||||
for fieldInfo in fieldInfos do
|
||||
if fieldInfo.isSubobject then
|
||||
addDeclarationRangesFromSyntax fieldInfo.declName r.view.ref fieldInfo.ref
|
||||
for decl in projDecls do
|
||||
-- projections may generate equation theorems
|
||||
enableRealizationsForConst decl.projName
|
||||
|
||||
private def registerStructure (structName : Name) (infos : Array StructFieldInfo) : TermElabM Unit := do
|
||||
let fields ← infos.filterMapM fun info => do
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -37,11 +37,9 @@ Assuming that `declName` is an enum inductive construct a function of type `decl
|
||||
that maps `declName` constructors to their numeric indices as `BitVec`.
|
||||
-/
|
||||
def getEnumToBitVecFor (declName : Name) : MetaM Name := do
|
||||
let env ← getEnv
|
||||
let enumToBitVecName := Name.str declName enumToBitVecSuffix
|
||||
if env.contains enumToBitVecName then
|
||||
return enumToBitVecName
|
||||
else
|
||||
realizeConst declName enumToBitVecName do
|
||||
let env ← getEnv
|
||||
let .inductInfo inductiveInfo ← getConstInfo declName | throwError m!"{declName} is not an inductive."
|
||||
if !(← isEnumType declName) then
|
||||
throwError m!"{declName} is not an enum inductive."
|
||||
@@ -67,18 +65,15 @@ def getEnumToBitVecFor (declName : Name) : MetaM Name := do
|
||||
hints := .regular (getMaxHeight env translator + 1)
|
||||
safety := .safe
|
||||
}
|
||||
return enumToBitVecName
|
||||
return enumToBitVecName
|
||||
|
||||
/--
|
||||
Assuming that `declName` is an enum inductive, construct a proof of
|
||||
`∀ (x y : declName) : x = y ↔ x.enumToBitVec = y.enumToBitVec`.
|
||||
-/
|
||||
def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
|
||||
let env ← getEnv
|
||||
let eqIffEnumToBitVecEqName := Name.str declName eqIffEnumToBitVecEqSuffix
|
||||
if env.contains eqIffEnumToBitVecEqName then
|
||||
return eqIffEnumToBitVecEqName
|
||||
else
|
||||
realizeConst declName eqIffEnumToBitVecEqName do
|
||||
/-
|
||||
We prove the lemma by constructing an inverse to `enumToBitVec` and use the fact that all
|
||||
invertible functions respect equality.
|
||||
@@ -110,7 +105,7 @@ def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
|
||||
let inverseValue ←
|
||||
withLocalDeclD `x bvType fun x => do
|
||||
let instBeq ← synthInstance (mkApp (mkConst ``BEq [0]) bvType)
|
||||
let inv := mkInverse x declType instBeq ctors (BitVec.ofNat bvSize 0) (mkConst ctors.head!)
|
||||
let inv ← mkInverse x declType instBeq ctors (BitVec.ofNat bvSize 0) (mkConst ctors.head!)
|
||||
mkLambdaFVars #[x] inv
|
||||
|
||||
let value ←
|
||||
@@ -145,13 +140,13 @@ def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
|
||||
type := type
|
||||
value := value
|
||||
}
|
||||
return eqIffEnumToBitVecEqName
|
||||
return eqIffEnumToBitVecEqName
|
||||
where
|
||||
mkInverse {w : Nat} (input : Expr) (retType : Expr) (instBEq : Expr) (ctors : List Name)
|
||||
(counter : BitVec w) (acc : Expr) :
|
||||
Expr :=
|
||||
MetaM Expr := do
|
||||
match ctors with
|
||||
| [] => acc
|
||||
| [] => pure acc
|
||||
| ctor :: ctors =>
|
||||
let eq :=
|
||||
mkApp4
|
||||
@@ -160,7 +155,10 @@ where
|
||||
instBEq
|
||||
input
|
||||
(toExpr counter)
|
||||
let acc := mkApp4 (mkConst ``cond [0]) retType eq (mkConst ctor) acc
|
||||
-- After the next stage0 update, this can be reverted to
|
||||
-- let acc := mkApp4 (mkConst ``cond [1]) retType eq (mkConst ctor) acc
|
||||
-- and mkInverse can be pure again
|
||||
let acc ← mkAppOptM ``cond #[retType, eq, mkConst ctor, acc]
|
||||
mkInverse input retType instBEq ctors (counter + 1) acc
|
||||
|
||||
/--
|
||||
@@ -169,11 +167,8 @@ Assuming that `declName` is an enum inductive, construct a proof of
|
||||
constructors of `declName`.
|
||||
-/
|
||||
def getEnumToBitVecLeFor (declName : Name) : MetaM Name := do
|
||||
let env ← getEnv
|
||||
let enumToBitVecLeName := Name.str declName enumToBitVecLeSuffix
|
||||
if env.contains enumToBitVecLeName then
|
||||
return enumToBitVecLeName
|
||||
else
|
||||
realizeConst declName enumToBitVecLeName do
|
||||
let enumToBitVec := mkConst (← getEnumToBitVecFor declName)
|
||||
let .inductInfo inductiveInfo ← getConstInfo declName | unreachable!
|
||||
let ctors := inductiveInfo.ctors
|
||||
@@ -207,7 +202,7 @@ def getEnumToBitVecLeFor (declName : Name) : MetaM Name := do
|
||||
type := type
|
||||
value := value
|
||||
}
|
||||
return enumToBitVecLeName
|
||||
return enumToBitVecLeName
|
||||
|
||||
|
||||
builtin_initialize
|
||||
@@ -274,11 +269,11 @@ partial def enumsPass : Pass where
|
||||
|
||||
let simprocs ← Simp.SimprocsArray.add #[] ``enumsPassPost true
|
||||
let ⟨result?, _⟩ ←
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := simprocs)
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := simprocs)
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
postprocess newGoal |>.run' {}
|
||||
where
|
||||
|
||||
@@ -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,11 +15,12 @@ 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 apply relevant `injEq` theorems to support at least equality for these types out
|
||||
of the box.
|
||||
Afterwards we also:
|
||||
- apply relevant `injEq` theorems to support at least equality for these types out of the box.
|
||||
- push projections of relevant types inside of `ite` and `cond`.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
@@ -27,6 +28,33 @@ namespace Frontend.Normalize
|
||||
|
||||
open Lean.Meta
|
||||
|
||||
|
||||
def applyIteSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
|
||||
if h : args.size ≠ 0 then
|
||||
let_expr ite α c instDec t e := args.back | return .continue
|
||||
let params := args.pop
|
||||
let projApp := mkAppN proj params
|
||||
let newT := mkApp projApp t
|
||||
let newE := mkApp projApp e
|
||||
let newIf ← mkAppOptM ``ite #[none, c, instDec, newT, newE]
|
||||
let proof ← mkAppOptM ``apply_ite #[α, none, projApp, c, instDec, t, e]
|
||||
return .visit { expr := newIf, proof? := some proof }
|
||||
else
|
||||
return .continue
|
||||
|
||||
def applyCondSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
|
||||
if h : args.size ≠ 0 then
|
||||
let_expr cond α c t e := args.back | return .continue
|
||||
let params := args.pop
|
||||
let projApp := mkAppN proj params
|
||||
let newT := mkApp projApp t
|
||||
let newE := mkApp projApp e
|
||||
let newCond ← mkAppOptM ``cond #[none, c, newT, newE]
|
||||
let proof ← mkAppOptM ``Bool.apply_cond #[α, none, projApp, c, t, e]
|
||||
return .visit { expr := newCond, proof? := some proof }
|
||||
else
|
||||
return .continue
|
||||
|
||||
partial def structuresPass : Pass where
|
||||
name := `structures
|
||||
run' goal := do
|
||||
@@ -36,14 +64,16 @@ partial def structuresPass : Pass where
|
||||
if decl.isLet || decl.isImplementationDetail then
|
||||
return false
|
||||
else
|
||||
let some const := decl.type.getAppFn.constName? | return false
|
||||
let some const := (← instantiateMVars decl.type).getAppFn.constName? | return false
|
||||
return interesting.contains const
|
||||
match goals with
|
||||
| [goal] => postprocess goal interesting
|
||||
| _ => throwError "structures preprocessor generated more than 1 goal"
|
||||
where
|
||||
postprocess (goal : MVarId) (interesting : Std.HashSet Name) : PreProcessM (Option MVarId) := do
|
||||
let env ← getEnv
|
||||
goal.withContext do
|
||||
let mut simprocs : Simprocs := {}
|
||||
let mut relevantLemmas : SimpTheoremsArray := #[]
|
||||
relevantLemmas ← relevantLemmas.addTheorem (.decl ``ne_eq) (← mkConstWithLevelParams ``ne_eq)
|
||||
for const in interesting do
|
||||
@@ -54,14 +84,43 @@ where
|
||||
trace[Meta.Tactic.bv] m!"Using injEq lemma: {lemmaName}"
|
||||
let statement ← mkConstWithLevelParams lemmaName
|
||||
relevantLemmas ← relevantLemmas.addTheorem (.decl lemmaName) statement
|
||||
let fields := (getStructureInfo env const).fieldNames.size
|
||||
let numParams := constInfo.numParams
|
||||
for proj in [0:fields] do
|
||||
-- We use the simprocs with pre such that we push in projections eagerly in order to
|
||||
-- potentially not have to simplify complex structure expressions that we only project one
|
||||
-- element out of.
|
||||
let path := mkDiscrPathFor const numParams proj ``ite 5
|
||||
simprocs := simprocs.addCore path ``applyIteSimproc false (.inl applyIteSimproc)
|
||||
let path := mkDiscrPathFor const numParams proj ``cond 4
|
||||
simprocs := simprocs.addCore path ``applyCondSimproc false (.inl applyCondSimproc)
|
||||
let cfg ← PreProcessM.getConfig
|
||||
let simpCtx ← Simp.mkContext
|
||||
(config := { failIfUnchanged := false, maxSteps := cfg.maxSteps })
|
||||
(simpTheorems := relevantLemmas)
|
||||
(congrTheorems := ← getSimpCongrTheorems)
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := ← getPropHyps)
|
||||
let ⟨result?, _⟩ ←
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := #[simprocs])
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
return newGoal
|
||||
|
||||
/--
|
||||
For `Prod.fst` and `ite` this function creates the path: `Prod.fst (ite (Prod _ _) _ _ _ _)`.
|
||||
This path can be used to match on applications of structure projections onto control flow primitives.
|
||||
-/
|
||||
mkDiscrPathFor (struct : Name) (structParams : Nat) (projIdx : Nat) (controlFlow : Name)
|
||||
(controlFlowParams : Nat) : Array DiscrTree.Key := Id.run do
|
||||
let stars := structParams + controlFlowParams - 1
|
||||
let mut path : Array DiscrTree.Key := Array.mkEmpty (3 + stars)
|
||||
path := path.push <| .proj struct projIdx 0
|
||||
path := path.push <| .const controlFlow controlFlowParams
|
||||
path := path.push <| .const struct structParams
|
||||
path := Nat.fold (init := path) stars (fun _ _ acc => acc.push .star)
|
||||
return path
|
||||
|
||||
end Frontend.Normalize
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
|
||||
@@ -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
|
||||
|
||||
/-!
|
||||
@@ -21,6 +22,9 @@ partial def typeAnalysisPass : Pass where
|
||||
name := `typeAnalysis
|
||||
run' goal := do
|
||||
checkContext goal
|
||||
let analysis ← PreProcessM.getTypeAnalysis
|
||||
trace[Meta.Tactic.bv] m!"Type analysis found structures: {analysis.interestingStructures.toList}"
|
||||
trace[Meta.Tactic.bv] m!"Type analysis found enums: {analysis.interestingEnums.toList}"
|
||||
return goal
|
||||
where
|
||||
checkContext (goal : MVarId) : PreProcessM Unit := do
|
||||
@@ -64,6 +68,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
|
||||
|
||||
@@ -224,6 +224,8 @@ where
|
||||
guard <| state.term.meta.core.traceState.traces.size == 0
|
||||
guard <| traceState.traces.size == 0
|
||||
return old.val.get
|
||||
if snap.old?.isSome && old?.isNone then
|
||||
snap.old?.forM (·.val.cancelRec)
|
||||
let promise ← IO.Promise.new
|
||||
-- Store new unfolding in the snapshot tree
|
||||
snap.new.resolve {
|
||||
@@ -233,6 +235,7 @@ where
|
||||
finished := .finished stx' {
|
||||
diagnostics := .empty
|
||||
state? := (← Tactic.saveState)
|
||||
moreSnaps := #[]
|
||||
}
|
||||
next := #[{ stx? := stx', task := promise.resultD default }]
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ where
|
||||
(← Core.getAndEmptyMessageLog))
|
||||
infoTree? := (← Term.getInfoTreeWithContext?)
|
||||
state? := state
|
||||
moreSnaps := (← Core.getAndEmptySnapshotTasks)
|
||||
}
|
||||
finally
|
||||
modifyInfoState fun s => { s with trees := trees ++ s.trees }
|
||||
|
||||
@@ -167,7 +167,7 @@ def evalGrindCore
|
||||
let only := only.isSome
|
||||
let params := if let some params := params then params.getElems else #[]
|
||||
if Grind.grind.warning.get (← getOptions) then
|
||||
logWarningAt ref "The `grind` tactic is experimental and still under development. Avoid using it in production projects"
|
||||
logWarningAt ref "The `grind` tactic is experimental and still under development. Avoid using it in production projects."
|
||||
let declName := (← Term.getDeclName?).getD `_grind
|
||||
withMainContext do
|
||||
let result ← grind (← getMainGoal) config only params declName fallback
|
||||
|
||||
@@ -173,6 +173,10 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
|
||||
addNewArg arg
|
||||
loop
|
||||
| _ =>
|
||||
let s ← get
|
||||
let ctx ← read
|
||||
unless s.targetPos = ctx.targets.size do
|
||||
throwError "unexpected number of targets for '{elimInfo.elimExpr}'"
|
||||
pure ()
|
||||
let (_, s) ← (loop).run { elimInfo := elimInfo, targets := targets }
|
||||
|>.run { f := elimInfo.elimExpr, fType := elimInfo.elimType, motive := none }
|
||||
@@ -303,7 +307,11 @@ where
|
||||
return ⟨old.stx, (← old.next[i]?)⟩
|
||||
new := prom
|
||||
}
|
||||
finished.resolve { diagnostics := .empty, state? := (← saveState) }
|
||||
finished.resolve {
|
||||
diagnostics := .empty
|
||||
state? := (← saveState)
|
||||
moreSnaps := (← Core.getAndEmptySnapshotTasks)
|
||||
}
|
||||
return
|
||||
|
||||
goWithIncremental #[]
|
||||
|
||||
@@ -24,28 +24,70 @@ Implementation of the `exact?` tactic.
|
||||
def exact? (ref : Syntax) (required : Option (Array (TSyntax `term))) (requireClose : Bool) :
|
||||
TacticM Unit := do
|
||||
let mvar ← getMainGoal
|
||||
let initialState ← saveState
|
||||
let (_, goal) ← (← getMainGoal).intros
|
||||
goal.withContext do
|
||||
let required := (← (required.getD #[]).mapM getFVarId).toList.map .fvar
|
||||
let tactic := fun exfalso =>
|
||||
solveByElim required (exfalso := exfalso) (maxDepth := 6)
|
||||
solveByElim required (exfalso := exfalso) (maxDepth := 6)
|
||||
let allowFailure := fun g => do
|
||||
let g ← g.withContext (instantiateMVars (.mvar g))
|
||||
return required.all fun e => e.occurs g
|
||||
match ← librarySearch goal tactic allowFailure with
|
||||
match (← librarySearch goal tactic allowFailure) with
|
||||
-- Found goal that closed problem
|
||||
| none =>
|
||||
addExactSuggestion ref (← instantiateMVars (mkMVar mvar)).headBeta
|
||||
addSuggestionIfValid ref mvar initialState
|
||||
-- Found suggestions
|
||||
| some suggestions =>
|
||||
if requireClose then throwError
|
||||
"`exact?` could not close the goal. Try `apply?` to see partial suggestions."
|
||||
if requireClose then
|
||||
let hint := if suggestions.isEmpty then "" else " Try `apply?` to see partial suggestions."
|
||||
throwError "`exact?` could not close the goal.{hint}"
|
||||
reportOutOfHeartbeats `apply? ref
|
||||
for (_, suggestionMCtx) in suggestions do
|
||||
withMCtx suggestionMCtx do
|
||||
addExactSuggestion ref (← instantiateMVars (mkMVar mvar)).headBeta (addSubgoalsMsg := true)
|
||||
addSuggestionIfValid ref mvar initialState (addSubgoalsMsg := true) (errorOnInvalid := false)
|
||||
if suggestions.isEmpty then logError "apply? didn't find any relevant lemmas"
|
||||
admitGoal goal
|
||||
where
|
||||
/--
|
||||
Executes `tac` in `savedState` (then restores the current state). Used to ensure that a suggested
|
||||
tactic is valid.
|
||||
|
||||
Remark: we don't merely elaborate the proof term's syntax because it may successfully round-trip
|
||||
(d)elaboration but still produce an invalid tactic (see the example in #5407).
|
||||
-/
|
||||
evalTacticWithState (savedState : Tactic.SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
|
||||
let currState ← saveState
|
||||
savedState.restore
|
||||
try
|
||||
Term.withoutErrToSorry <| withoutRecover <| evalTactic tac
|
||||
finally
|
||||
currState.restore
|
||||
|
||||
/--
|
||||
Suggests using the value of `goal` as a proof term if the corresponding tactic is valid at
|
||||
`origGoal`, or else informs the user that a proof exists but is not syntactically valid.
|
||||
-/
|
||||
addSuggestionIfValid (ref : Syntax) (goal : MVarId) (initialState : Tactic.SavedState)
|
||||
(addSubgoalsMsg := false) (errorOnInvalid := true) : TacticM Unit := do
|
||||
let proofExpr := (← instantiateMVars (mkMVar goal)).headBeta
|
||||
let proofMVars ← getMVars proofExpr
|
||||
let hasMVars := !proofMVars.isEmpty
|
||||
let suggestion ← mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := false)
|
||||
let mut exposeNames := false
|
||||
try evalTacticWithState initialState suggestion
|
||||
catch _ =>
|
||||
exposeNames := true
|
||||
let suggestion' ← mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := true)
|
||||
try evalTacticWithState initialState suggestion'
|
||||
catch _ =>
|
||||
let suggestionStr ← SuggestionText.prettyExtra suggestion
|
||||
-- Pretty-print the version without `expose_names` so variable names match the Infoview
|
||||
let msg := m!"found a {if hasMVars then "partial " else ""}proof, \
|
||||
but the corresponding tactic failed:{indentD suggestionStr}"
|
||||
if errorOnInvalid then throwError msg else logInfo msg
|
||||
return
|
||||
addExactSuggestion ref proofExpr (addSubgoalsMsg := addSubgoalsMsg) (exposeNames := exposeNames)
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.exact?]
|
||||
def evalExact : Tactic := fun stx => do
|
||||
@@ -69,7 +111,7 @@ def elabExact?Term : TermElab := fun stx expectedType? => do
|
||||
introdGoal.withContext do
|
||||
if let some suggestions ← librarySearch introdGoal then
|
||||
if suggestions.isEmpty then logError "`exact?%` didn't find any relevant lemmas"
|
||||
else logError "`exact?%` could not close the goal. Try `by apply` to see partial suggestions."
|
||||
else logError "`exact?%` could not close the goal. Try `by apply?` to see partial suggestions."
|
||||
mkLabeledSorry expectedType (synthetic := true) (unique := true)
|
||||
else
|
||||
addTermSuggestion stx (← instantiateMVars goal).headBeta
|
||||
|
||||
@@ -48,14 +48,6 @@ private def isExprAccessible (e : Expr) : MetaM Bool := do
|
||||
let (_, s) ← e.collectFVars |>.run {}
|
||||
s.fvarIds.allM isAccessible
|
||||
|
||||
/-- Creates a temporary local context where all names are exposed, and executes `k`-/
|
||||
private def withExposedNames (k : MetaM α) : MetaM α := do
|
||||
withNewMCtxDepth do
|
||||
-- Create a helper goal to apply
|
||||
let mvarId := (← mkFreshExprMVar (mkConst ``True)).mvarId!
|
||||
let mvarId ← mvarId.exposeNames
|
||||
mvarId.withContext do k
|
||||
|
||||
/-- Executes `tac` in the saved state. This function is used to validate a tactic before suggesting it. -/
|
||||
def checkTactic (savedState : SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
|
||||
let currState ← saveState
|
||||
|
||||
@@ -227,9 +227,11 @@ structure SavedState where
|
||||
structure TacticFinishedSnapshot extends Language.Snapshot where
|
||||
/-- State saved for reuse, if no fatal exception occurred. -/
|
||||
state? : Option SavedState
|
||||
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
|
||||
moreSnaps : Array (SnapshotTask SnapshotTree)
|
||||
deriving Inhabited
|
||||
instance : ToSnapshotTree TacticFinishedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot, #[]⟩
|
||||
toSnapshotTree s := ⟨s.toSnapshot, s.moreSnaps⟩
|
||||
|
||||
/-- Snapshot just before execution of a tactic. -/
|
||||
structure TacticParsedSnapshot extends Language.Snapshot where
|
||||
@@ -256,10 +258,6 @@ namespace Term
|
||||
|
||||
structure Context where
|
||||
declName? : Option Name := none
|
||||
/--
|
||||
Map `.auxDecl` local declarations used to encode recursive declarations to their full-names.
|
||||
-/
|
||||
auxDeclToFullName : FVarIdMap Name := {}
|
||||
macroStack : MacroStack := []
|
||||
/--
|
||||
When `mayPostpone == true`, an elaboration function may interrupt its execution by throwing `Exception.postpone`.
|
||||
@@ -421,13 +419,17 @@ part. `act` is then run on the inner part but with reuse information adjusted as
|
||||
context.
|
||||
|
||||
For any tactic that participates in reuse, `withNarrowedTacticReuse` should be applied to the
|
||||
tactic's syntax and `act` should be used to do recursive tactic evaluation of nested parts.
|
||||
tactic's syntax and `act` should be used to do recursive tactic evaluation of nested parts. Also,
|
||||
after this function, `getAndEmptySnapshotTasks` should be called and the result stored in a snapshot
|
||||
so that the tasks don't end up in a snapshot further up and are cancelled together with it; see
|
||||
note [Incremental Cancellation].
|
||||
-/
|
||||
def withNarrowedTacticReuse [Monad m] [MonadWithReaderOf Core.Context m]
|
||||
[MonadWithReaderOf Context m] [MonadOptions m] (split : Syntax → Syntax × Syntax)
|
||||
(act : Syntax → m α) (stx : Syntax) : m α := do
|
||||
def withNarrowedTacticReuse [Monad m] [MonadReaderOf Context m] [MonadLiftT BaseIO m]
|
||||
[MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m] [MonadOptions m]
|
||||
(split : Syntax → Syntax × Syntax) (act : Syntax → m α) (stx : Syntax) : m α := do
|
||||
let (outer, inner) := split stx
|
||||
let opts ← getOptions
|
||||
let ctx ← readThe Term.Context
|
||||
withTheReader Term.Context (fun ctx => { ctx with tacSnap? := ctx.tacSnap?.map fun tacSnap =>
|
||||
{ tacSnap with old? := tacSnap.old?.bind fun old => do
|
||||
let (oldOuter, oldInner) := split old.stx
|
||||
@@ -435,6 +437,9 @@ def withNarrowedTacticReuse [Monad m] [MonadWithReaderOf Core.Context m]
|
||||
return { old with stx := oldInner }
|
||||
}
|
||||
}) do
|
||||
if let some oldOuter := ctx.tacSnap?.bind (·.old?) then
|
||||
if (← read).tacSnap?.bind (·.old?) |>.isNone then
|
||||
oldOuter.val.cancelRec
|
||||
withReuseContext inner (act inner)
|
||||
|
||||
/--
|
||||
@@ -446,8 +451,9 @@ NOTE: child nodes after `argIdx` are not tested (which would almost always disab
|
||||
necessarily shifted by changes at `argIdx`) so it must be ensured that the result of `arg` does not
|
||||
depend on them (i.e. they should not be inspected beforehand).
|
||||
-/
|
||||
def withNarrowedArgTacticReuse [Monad m] [MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m]
|
||||
[MonadOptions m] (argIdx : Nat) (act : Syntax → m α) (stx : Syntax) : m α :=
|
||||
def withNarrowedArgTacticReuse [Monad m] [MonadReaderOf Context m] [MonadLiftT BaseIO m]
|
||||
[MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m] [MonadOptions m]
|
||||
(argIdx : Nat) (act : Syntax → m α) (stx : Syntax) : m α :=
|
||||
withNarrowedTacticReuse (fun stx => (mkNullNode stx.getArgs[:argIdx], stx[argIdx])) act stx
|
||||
|
||||
/--
|
||||
@@ -617,15 +623,6 @@ def withLevelNames (levelNames : List Name) (x : TermElabM α) : TermElabM α :=
|
||||
setLevelNames levelNames
|
||||
try x finally setLevelNames levelNamesSaved
|
||||
|
||||
/--
|
||||
Declare an auxiliary local declaration `shortDeclName : type` for elaborating recursive declaration `declName`,
|
||||
update the mapping `auxDeclToFullName`, and then execute `k`.
|
||||
-/
|
||||
def withAuxDecl (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr → TermElabM α) : TermElabM α :=
|
||||
withLocalDecl shortDeclName .default (kind := .auxDecl) type fun x =>
|
||||
withReader (fun ctx => { ctx with auxDeclToFullName := ctx.auxDeclToFullName.insert x.fvarId! declName }) do
|
||||
k x
|
||||
|
||||
def withoutErrToSorryImp (x : TermElabM α) : TermElabM α :=
|
||||
withReader (fun ctx => { ctx with errToSorry := false }) x
|
||||
|
||||
@@ -1512,169 +1509,6 @@ def blockImplicitLambda (stx : Syntax) : Bool :=
|
||||
isExplicit stx || isExplicitApp stx || isLambdaWithImplicit stx || isHole stx || isTacticBlock stx ||
|
||||
isNoImplicitLambda stx || isTypeAscription stx
|
||||
|
||||
def resolveLocalName (n : Name) : TermElabM (Option (Expr × List String)) := do
|
||||
let lctx ← getLCtx
|
||||
let auxDeclToFullName := (← read).auxDeclToFullName
|
||||
let currNamespace ← getCurrNamespace
|
||||
let view := extractMacroScopes n
|
||||
/- Simple case. "Match" function for regular local declarations. -/
|
||||
let matchLocalDecl? (localDecl : LocalDecl) (givenName : Name) : Option LocalDecl := do
|
||||
guard (localDecl.userName == givenName)
|
||||
return localDecl
|
||||
/-
|
||||
"Match" function for auxiliary declarations that correspond to recursive definitions being defined.
|
||||
This function is used in the first-pass.
|
||||
Note that we do not check for `localDecl.userName == givenName` in this pass as we do for regular local declarations.
|
||||
Reason: consider the following example
|
||||
```
|
||||
mutual
|
||||
inductive Foo
|
||||
| somefoo : Foo | bar : Bar → Foo → Foo
|
||||
inductive Bar
|
||||
| somebar : Bar| foobar : Foo → Bar → Bar
|
||||
end
|
||||
|
||||
mutual
|
||||
private def Foo.toString : Foo → String
|
||||
| Foo.somefoo => go 2 ++ toString.go 2 ++ Foo.toString.go 2
|
||||
| Foo.bar b f => toString f ++ Bar.toString b
|
||||
where
|
||||
go (x : Nat) := s!"foo {x}"
|
||||
|
||||
private def _root_.Ex2.Bar.toString : Bar → String
|
||||
| Bar.somebar => "bar"
|
||||
| Bar.foobar f b => Foo.toString f ++ Bar.toString b
|
||||
end
|
||||
```
|
||||
In the example above, we have two local declarations named `toString` in the local context, and
|
||||
we want the `toString f` to be resolved to `Foo.toString f`.
|
||||
-/
|
||||
let matchAuxRecDecl? (localDecl : LocalDecl) (fullDeclName : Name) (givenNameView : MacroScopesView) : Option LocalDecl := do
|
||||
let fullDeclView := extractMacroScopes fullDeclName
|
||||
/- First cleanup private name annotations -/
|
||||
let fullDeclView := { fullDeclView with name := (privateToUserName? fullDeclView.name).getD fullDeclView.name }
|
||||
let fullDeclName := fullDeclView.review
|
||||
let localDeclNameView := extractMacroScopes localDecl.userName
|
||||
/- If the current namespace is a prefix of the full declaration name,
|
||||
we use a relaxed matching test where we must satisfy the following conditions
|
||||
- The local declaration is a suffix of the given name.
|
||||
- The given name is a suffix of the full declaration.
|
||||
|
||||
Recall the `let rec`/`where` declaration naming convention. For example, suppose we have
|
||||
```
|
||||
def Foo.Bla.f ... :=
|
||||
... go ...
|
||||
where
|
||||
go ... := ...
|
||||
```
|
||||
The current namespace is `Foo.Bla`, and the full name for `go` is `Foo.Bla.f.g`, but we want to
|
||||
refer to it using just `go`. It is also accepted to refer to it using `f.go`, `Bla.f.go`, etc.
|
||||
|
||||
-/
|
||||
if currNamespace.isPrefixOf fullDeclName then
|
||||
/- Relaxed mode that allows us to access `let rec` declarations using shorter names -/
|
||||
guard (localDeclNameView.isSuffixOf givenNameView)
|
||||
guard (givenNameView.isSuffixOf fullDeclView)
|
||||
return localDecl
|
||||
else
|
||||
/-
|
||||
It is the standard algorithm we are using at `resolveGlobalName` for processing namespaces.
|
||||
|
||||
The current solution also has a limitation when using `def _root_` in a mutual block.
|
||||
The non `def _root_` declarations may update the namespace. See the following example:
|
||||
```
|
||||
mutual
|
||||
def Foo.f ... := ...
|
||||
def _root_.g ... := ...
|
||||
let rec h := ...
|
||||
...
|
||||
end
|
||||
```
|
||||
`def Foo.f` updates the namespace. Then, even when processing `def _root_.g ...`
|
||||
the condition `currNamespace.isPrefixOf fullDeclName` does not hold.
|
||||
This is not a big problem because we are planning to modify how we handle the mutual block in the future.
|
||||
|
||||
Note that we don't check for `localDecl.userName == givenName` here.
|
||||
-/
|
||||
let rec go (ns : Name) : Option LocalDecl := do
|
||||
if { givenNameView with name := ns ++ givenNameView.name }.review == fullDeclName then
|
||||
return localDecl
|
||||
match ns with
|
||||
| .str pre .. => go pre
|
||||
| _ => failure
|
||||
return (← go currNamespace)
|
||||
/- Traverse the local context backwards looking for match `givenNameView`.
|
||||
If `skipAuxDecl` we ignore `auxDecl` local declarations. -/
|
||||
let findLocalDecl? (givenNameView : MacroScopesView) (skipAuxDecl : Bool) : Option LocalDecl :=
|
||||
let givenName := givenNameView.review
|
||||
let localDecl? := lctx.decls.findSomeRev? fun localDecl? => do
|
||||
let localDecl ← localDecl?
|
||||
if localDecl.isAuxDecl then
|
||||
guard (!skipAuxDecl)
|
||||
if let some fullDeclName := auxDeclToFullName.find? localDecl.fvarId then
|
||||
matchAuxRecDecl? localDecl fullDeclName givenNameView
|
||||
else
|
||||
matchLocalDecl? localDecl givenName
|
||||
else
|
||||
matchLocalDecl? localDecl givenName
|
||||
if localDecl?.isSome || skipAuxDecl then
|
||||
localDecl?
|
||||
else
|
||||
-- Search auxDecls again trying an exact match of the given name
|
||||
lctx.decls.findSomeRev? fun localDecl? => do
|
||||
let localDecl ← localDecl?
|
||||
guard localDecl.isAuxDecl
|
||||
matchLocalDecl? localDecl givenName
|
||||
/-
|
||||
We use the parameter `globalDeclFound` to decide whether we should skip auxiliary declarations or not.
|
||||
We set it to true if we found a global declaration `n` as we iterate over the `loop`.
|
||||
Without this workaround, we would not be able to elaborate an example such as
|
||||
```
|
||||
def foo.aux := 1
|
||||
def foo : Nat → Nat
|
||||
| n => foo.aux -- should not be interpreted as `(foo).aux`
|
||||
```
|
||||
See test `aStructPerfIssue.lean` for another example.
|
||||
We skip auxiliary declarations when `projs` is not empty and `globalDeclFound` is true.
|
||||
Remark: we did not use to have the `globalDeclFound` parameter. Without this extra check we failed
|
||||
to elaborate
|
||||
```
|
||||
example : Nat :=
|
||||
let n := 0
|
||||
n.succ + (m |>.succ) + m.succ
|
||||
where
|
||||
m := 1
|
||||
```
|
||||
See issue #1850.
|
||||
-/
|
||||
let rec loop (n : Name) (projs : List String) (globalDeclFound : Bool) := do
|
||||
let givenNameView := { view with name := n }
|
||||
let mut globalDeclFoundNext := globalDeclFound
|
||||
unless globalDeclFound do
|
||||
let r ← resolveGlobalName givenNameView.review
|
||||
let r := r.filter fun (_, fieldList) => fieldList.isEmpty
|
||||
unless r.isEmpty do
|
||||
globalDeclFoundNext := true
|
||||
/-
|
||||
Note that we use `globalDeclFound` instead of `globalDeclFoundNext` in the following test.
|
||||
Reason: a local should shadow a global with the same name.
|
||||
Consider the following example. See issue #3079
|
||||
```
|
||||
def foo : Nat := 1
|
||||
|
||||
def bar : Nat :=
|
||||
foo.add 1 -- should be 11
|
||||
where
|
||||
foo := 10
|
||||
```
|
||||
-/
|
||||
match findLocalDecl? givenNameView (skipAuxDecl := globalDeclFound && !projs.isEmpty) with
|
||||
| some decl => return some (decl.toExpr, projs)
|
||||
| none => match n with
|
||||
| .str pre s => loop pre (s::projs) globalDeclFoundNext
|
||||
| _ => return none
|
||||
loop view.name [] (globalDeclFound := false)
|
||||
|
||||
/-- Return true iff `stx` is a `Syntax.ident`, and it is a local variable. -/
|
||||
def isLocalIdent? (stx : Syntax) : TermElabM (Option Expr) :=
|
||||
match stx with
|
||||
|
||||
@@ -18,8 +18,10 @@ import Lean.Util.Path
|
||||
import Lean.Util.FindExpr
|
||||
import Lean.Util.Profile
|
||||
import Lean.Util.InstantiateLevelParams
|
||||
import Lean.Util.FoldConsts
|
||||
import Lean.PrivateName
|
||||
import Lean.LoadDynlib
|
||||
import Init.Dynamic
|
||||
|
||||
/-!
|
||||
# Note [Environment Branches]
|
||||
@@ -65,6 +67,12 @@ paths back together.
|
||||
-/
|
||||
|
||||
namespace Lean
|
||||
register_builtin_option debug.skipKernelTC : Bool := {
|
||||
defValue := false
|
||||
group := "debug"
|
||||
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
|
||||
}
|
||||
|
||||
/-- Opaque environment extension state. -/
|
||||
opaque EnvExtensionStateSpec : (α : Type) × Inhabited α := ⟨Unit, ⟨()⟩⟩
|
||||
def EnvExtensionState : Type := EnvExtensionStateSpec.fst
|
||||
@@ -252,6 +260,28 @@ inductive Exception where
|
||||
| excessiveMemory
|
||||
| deepRecursion
|
||||
| interrupted
|
||||
deriving Nonempty
|
||||
|
||||
/-- Basic `Exception` formatting without `MessageData` dependency. -/
|
||||
private def Exception.toRawString : Kernel.Exception → String
|
||||
| unknownConstant _ constName => s!"(kernel) unknown constant '{constName}'"
|
||||
| alreadyDeclared _ constName => s!"(kernel) constant has already been declared '{constName}'"
|
||||
| declTypeMismatch _ _ _ => s!"(kernel) declaration type mismatch"
|
||||
| declHasMVars _ constName _ => s!"(kernel) declaration has metavariables '{constName}'"
|
||||
| declHasFVars _ constName _ => s!"(kernel) declaration has free variables '{constName}'"
|
||||
| funExpected _ _ e => s!"(kernel) function expected: {e}"
|
||||
| typeExpected _ _ e => s!"(kernel) type expected: {e}"
|
||||
| letTypeMismatch _ _ n _ _ => s!"(kernel) let-declaration type mismatch '{n}'"
|
||||
| exprTypeMismatch _ _ e _ => s!"(kernel) type mismatch at {e}"
|
||||
| appTypeMismatch _ _ e fnType argType =>
|
||||
s!"application type mismatch: {e}\nargument has type {argType}\nbut function has type {fnType}"
|
||||
| invalidProj _ _ e => s!"(kernel) invalid projection {e}"
|
||||
| thmTypeIsNotProp _ constName type => s!"(kernel) type of theorem '{constName}' is not a proposition: {type}"
|
||||
| other msg => s!"(kernel) {msg}"
|
||||
| deterministicTimeout => "(kernel) deterministic timeout"
|
||||
| excessiveMemory => "(kernel) excessive memory consumption detected"
|
||||
| deepRecursion => "(kernel) deep recursion detected"
|
||||
| interrupted => "(kernel) interrupted"
|
||||
|
||||
namespace Environment
|
||||
|
||||
@@ -346,6 +376,7 @@ structure AsyncConstantInfo where
|
||||
sig : Task ConstantVal
|
||||
/-- The final, complete constant info, potentially filled asynchronously. -/
|
||||
constInfo : Task ConstantInfo
|
||||
deriving Inhabited
|
||||
|
||||
namespace AsyncConstantInfo
|
||||
|
||||
@@ -365,21 +396,25 @@ end AsyncConstantInfo
|
||||
|
||||
/--
|
||||
Information about the current branch of the environment representing asynchronous elaboration.
|
||||
|
||||
Use `Environment.enterAsync` instead of `mkRaw`.
|
||||
-/
|
||||
structure AsyncContext where
|
||||
private structure AsyncContext where mkRaw ::
|
||||
/--
|
||||
Name of the declaration asynchronous elaboration was started for. All constants added to this
|
||||
environment branch must have the name as a prefix, after erasing macro scopes and private name
|
||||
prefixes.
|
||||
-/
|
||||
declPrefix : Name
|
||||
/-- Whether we are in `realizeConst`, used to restrict env ext modifications. -/
|
||||
realizing : Bool
|
||||
deriving Nonempty
|
||||
|
||||
/--
|
||||
Checks whether a declaration named `n` may be added to the environment in the given context. See
|
||||
also `AsyncContext.declPrefix`.
|
||||
-/
|
||||
def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
|
||||
private def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
|
||||
ctx.declPrefix.isPrefixOf <| privateToUserName n.eraseMacroScopes
|
||||
|
||||
/--
|
||||
@@ -394,28 +429,50 @@ structure AsyncConst where
|
||||
exts? : Option (Task (Array EnvExtensionState))
|
||||
|
||||
/-- Data structure holding a sequence of `AsyncConst`s optimized for efficient access. -/
|
||||
structure AsyncConsts where
|
||||
toArray : Array AsyncConst := #[]
|
||||
private structure AsyncConsts where
|
||||
size : Nat
|
||||
revList : List AsyncConst
|
||||
/-- Map from declaration name to const for fast direct access. -/
|
||||
private map : NameMap AsyncConst := {}
|
||||
map : NameMap AsyncConst
|
||||
/-- Trie of declaration names without private name prefixes for fast longest-prefix access. -/
|
||||
private normalizedTrie : NameTrie AsyncConst := {}
|
||||
normalizedTrie : NameTrie AsyncConst
|
||||
deriving Inhabited
|
||||
|
||||
def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
|
||||
{ aconsts with
|
||||
toArray := aconsts.toArray.push aconst
|
||||
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
|
||||
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
|
||||
}
|
||||
|
||||
def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
private def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
aconsts.map.find? declName
|
||||
|
||||
/-- Finds the constant in the collection that is a prefix of `declName`, if any. -/
|
||||
def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
-- as macro scopes are a strict suffix,
|
||||
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName.eraseMacroScopes)
|
||||
private def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
-- 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
|
||||
/--
|
||||
Saved `Environment`, untyped to avoid cyclic reference. Import environment for imported constants.
|
||||
-/
|
||||
env : NonScalar
|
||||
/-- Saved options. Empty for imported constants. -/
|
||||
opts : Options
|
||||
/--
|
||||
`realizeConst _ c ..` adds a mapping from `c` to a task of the realization results: the newly
|
||||
added constants (incl. extension data in `AsyncConst.exts?`), a function for replaying the
|
||||
changes onto a derived kernel environment, and auxiliary data (always `SnapshotTree` in builtin
|
||||
uses, but untyped to avoid cyclic module references).
|
||||
-/
|
||||
constsRef : IO.Ref (NameMap (Task (List AsyncConst × (Kernel.Environment → Kernel.Environment) × Dynamic)))
|
||||
|
||||
/--
|
||||
Elaboration-specific extension of `Kernel.Environment` that adds tracking of asynchronously
|
||||
@@ -443,19 +500,32 @@ structure Environment where
|
||||
-/
|
||||
checked : Task Kernel.Environment := .pure checkedWithoutAsync
|
||||
/--
|
||||
Container of asynchronously elaborated declarations, i.e.
|
||||
`checked = checkedWithoutAsync ⨃ asyncConsts`.
|
||||
Container of asynchronously elaborated declarations. For consistency, `updateBaseAfterKernelAdd`
|
||||
makes sure this contains constants added even synchronously, i.e. this is a superset of
|
||||
`checkedWithoutAsync` except for imported constants.
|
||||
-/
|
||||
private asyncConsts : AsyncConsts := {}
|
||||
private asyncConsts : AsyncConsts := default
|
||||
/-- Information about this asynchronous branch of the environment, if any. -/
|
||||
private asyncCtx? : Option AsyncContext := none
|
||||
/--
|
||||
Realized constants belonging to imported declarations. Must be initialized by calling
|
||||
`enableRealizationsForImports`.
|
||||
-/
|
||||
private realizedImportedConsts? : Option RealizationContext
|
||||
/--
|
||||
Realized constants belonging to local declarations. This is a map from local declarations, which
|
||||
need to be registered synchronously using `enableRealizationsForConst`, to their realization
|
||||
context incl. a ref of realized constants.
|
||||
-/
|
||||
private realizedLocalConsts : NameMap RealizationContext := {}
|
||||
deriving Nonempty
|
||||
|
||||
namespace Environment
|
||||
|
||||
-- used only when the kernel calls into the interpreter, and in `Lean.Kernel.Exception.mkCtx`
|
||||
@[export lean_elab_environment_of_kernel_env]
|
||||
def ofKernelEnv (env : Kernel.Environment) : Environment :=
|
||||
{ checkedWithoutAsync := env }
|
||||
{ checkedWithoutAsync := env, realizedImportedConsts? := none }
|
||||
|
||||
@[export lean_elab_environment_to_kernel_env]
|
||||
def toKernelEnv (env : Environment) : Kernel.Environment :=
|
||||
@@ -469,6 +539,10 @@ private def modifyCheckedAsync (env : Environment) (f : Kernel.Environment → K
|
||||
private def setCheckedSync (env : Environment) (newChecked : Kernel.Environment) : Environment :=
|
||||
{ env with checked := .pure newChecked, checkedWithoutAsync := newChecked }
|
||||
|
||||
/-- True while inside `realizeConst`'s `realize`. -/
|
||||
def isRealizing (env : Environment) : Bool :=
|
||||
env.asyncCtx?.any (·.realizing)
|
||||
|
||||
/--
|
||||
Checks whether the given declaration name may potentially added, or have been added, to the current
|
||||
environment branch, which is the case either if this is the main branch or if the declaration name
|
||||
@@ -550,7 +624,7 @@ private def findNoAsync (env : Environment) (n : Name) : Option ConstantInfo :=
|
||||
|
||||
/--
|
||||
Looks up the given declaration name in the environment, avoiding forcing any in-progress elaboration
|
||||
tasks.
|
||||
tasks unless necessary.
|
||||
-/
|
||||
def findAsync? (env : Environment) (n : Name) : Option AsyncConstantInfo := do
|
||||
-- Check declarations already added to the kernel environment (e.g. because they were imported)
|
||||
@@ -574,6 +648,45 @@ 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
|
||||
that while we check that the function isn't called too *early*, i.e. before the declaration is
|
||||
actually added to the environment, we cannot automatically check that it isn't called too *late*,
|
||||
i.e. before all environment extensions that may be relevant to realizations have been set. We do
|
||||
check that we are not calling it from a different branch than `c` was added on, which would be
|
||||
definitely too late.
|
||||
-/
|
||||
def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
|
||||
BaseIO Environment := do
|
||||
if env.findAsync? c |>.isNone then
|
||||
panic! s!"Environment.enableRealizationsForConst: declaration {c} not found in environment"
|
||||
if let some asyncCtx := env.asyncCtx? then
|
||||
if !asyncCtx.mayContain c then
|
||||
panic! s!"Environment.enableRealizationsForConst: {c} is outside current context {asyncCtx.declPrefix}"
|
||||
if env.realizedLocalConsts.contains c then
|
||||
return env
|
||||
return { env with realizedLocalConsts := env.realizedLocalConsts.insert c {
|
||||
-- safety: `RealizationContext` is private
|
||||
env := unsafe unsafeCast env
|
||||
opts
|
||||
constsRef := (← IO.mkRef {}) } }
|
||||
|
||||
/--
|
||||
Looks up the given declaration name in the environment, blocking on the corresponding elaboration
|
||||
task if not yet complete.
|
||||
@@ -590,9 +703,14 @@ def find? (env : Environment) (n : Name) : Option ConstantInfo :=
|
||||
def dbgFormatAsyncState (env : Environment) : BaseIO String :=
|
||||
return s!"\
|
||||
asyncCtx.declPrefix: {repr <| env.asyncCtx?.map (·.declPrefix)}\
|
||||
\nasyncConsts: {repr <| env.asyncConsts.toArray.map (·.constInfo.name)}\
|
||||
\ncheckedWithoutAsync.constants.map₂: {repr <|
|
||||
env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
|
||||
\nasyncConsts: {repr <| env.asyncConsts.revList.reverse.map (·.constInfo.name)}\
|
||||
\nrealizedLocalConsts: {repr (← env.realizedLocalConsts.toList.mapM fun (n, ctx) => do
|
||||
let consts := (← ctx.constsRef.get).toList
|
||||
return (n, consts.map (·.1)))}
|
||||
\nrealizedImportedConsts?: {repr <| (← env.realizedImportedConsts?.mapM fun ctx => do
|
||||
return (← ctx.constsRef.get).toList.map fun (n, m?) =>
|
||||
(n, m?.get.1.map (fun c : AsyncConst => c.constInfo.name.toString) |> toString))}
|
||||
\ncheckedWithoutAsync.constants.map₂: {repr <| env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
|
||||
|
||||
/-- Returns debug output about the synchronous state of the environment. -/
|
||||
def dbgFormatCheckedSyncState (env : Environment) : BaseIO String :=
|
||||
@@ -614,6 +732,13 @@ structure PromiseCheckedResult where
|
||||
asyncEnv : Environment
|
||||
private checkedEnvPromise : IO.Promise Kernel.Environment
|
||||
|
||||
/-- Creates an async context for the given declaration name, normalizing it for use as a prefix. -/
|
||||
private def enterAsync (declName : Name) (realizing := false) (env : Environment) : Environment :=
|
||||
{ env with asyncCtx? := some {
|
||||
declPrefix := privateToUserName declName.eraseMacroScopes
|
||||
-- `realizing` is sticky
|
||||
realizing := realizing || env.asyncCtx?.any (·.realizing) } }
|
||||
|
||||
/--
|
||||
Starts an asynchronous modification of the kernel environment. The environment is split into a
|
||||
"main" branch that will block on access to the kernel environment until
|
||||
@@ -626,10 +751,8 @@ def promiseChecked (env : Environment) : BaseIO PromiseCheckedResult := do
|
||||
checked := checkedEnvPromise.result?.bind (sync := true) fun
|
||||
| some kenv => .pure kenv
|
||||
| none => env.checked }
|
||||
asyncEnv := { env with
|
||||
-- Do not allow adding new constants
|
||||
asyncCtx? := some { declPrefix := `__reserved__Environment_promiseChecked }
|
||||
}
|
||||
-- Do not allow adding new constants
|
||||
asyncEnv := env.enterAsync `__reserved__Environment_promiseChecked
|
||||
checkedEnvPromise
|
||||
}
|
||||
|
||||
@@ -664,28 +787,14 @@ structure AddConstAsyncResult where
|
||||
private extensionsPromise : IO.Promise (Array EnvExtensionState)
|
||||
private checkedEnvPromise : IO.Promise Kernel.Environment
|
||||
|
||||
/--
|
||||
Starts the asynchronous addition of a constant to the environment. The environment is split into a
|
||||
"main" branch that holds a reference to the constant to be added but will block on access until the
|
||||
corresponding information has been added on the "async" environment branch and committed there; see
|
||||
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
|
||||
information.
|
||||
-/
|
||||
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
|
||||
IO AddConstAsyncResult := do
|
||||
assert! env.asyncMayContain constName
|
||||
let sigPromise ← IO.Promise.new
|
||||
let infoPromise ← IO.Promise.new
|
||||
let extensionsPromise ← IO.Promise.new
|
||||
let checkedEnvPromise ← IO.Promise.new
|
||||
|
||||
-- fallback info in case promises are dropped unfulfilled
|
||||
let fallbackVal := {
|
||||
/-- Creates fallback info to be used in case promises are dropped unfulfilled. -/
|
||||
private def mkFallbackConstInfo (constName : Name) (kind : ConstantKind) : ConstantInfo :=
|
||||
let fallbackVal : ConstantVal := {
|
||||
name := constName
|
||||
levelParams := []
|
||||
type := mkApp2 (mkConst ``sorryAx [0]) (mkSort 0) (mkConst ``true)
|
||||
type := mkApp2 (mkConst ``sorryAx [1]) (mkSort 0) (mkConst ``true)
|
||||
}
|
||||
let fallbackInfo := match kind with
|
||||
match kind with
|
||||
| .defn => .defnInfo { fallbackVal with
|
||||
value := mkApp2 (mkConst ``sorryAx [0]) fallbackVal.type (mkConst ``true)
|
||||
hints := .abbrev
|
||||
@@ -697,16 +806,38 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
|
||||
| .axiom => .axiomInfo { fallbackVal with
|
||||
isUnsafe := false
|
||||
}
|
||||
| k => panic! s!"AddConstAsyncResult.addConstAsync: unsupported constant kind {repr k}"
|
||||
| k => panic! s!"Environment.mkFallbackConstInfo: unsupported constant kind {repr k}"
|
||||
|
||||
/--
|
||||
Starts the asynchronous addition of a constant to the environment. The environment is split into a
|
||||
"main" branch that holds a reference to the constant to be added but will block on access until the
|
||||
corresponding information has been added on the "async" environment branch and committed there; see
|
||||
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
|
||||
information.
|
||||
-/
|
||||
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true)
|
||||
(checkMayContain := true) :
|
||||
IO AddConstAsyncResult := do
|
||||
if checkMayContain then
|
||||
if let some ctx := env.asyncCtx? then
|
||||
if !ctx.mayContain constName then
|
||||
throw <| .userError s!"cannot add declaration {constName} to environment as it is \
|
||||
restricted to the prefix {ctx.declPrefix}"
|
||||
let sigPromise ← IO.Promise.new
|
||||
let infoPromise ← IO.Promise.new
|
||||
let extensionsPromise ← IO.Promise.new
|
||||
let checkedEnvPromise ← IO.Promise.new
|
||||
|
||||
let fallbackConstInfo := mkFallbackConstInfo constName kind
|
||||
|
||||
let asyncConst := {
|
||||
constInfo := {
|
||||
name := constName
|
||||
kind
|
||||
sig := sigPromise.resultD fallbackVal
|
||||
constInfo := infoPromise.resultD fallbackInfo
|
||||
sig := sigPromise.resultD fallbackConstInfo.toConstantVal
|
||||
constInfo := infoPromise.resultD fallbackConstInfo
|
||||
}
|
||||
exts? := guard reportExts *> some (extensionsPromise.resultD #[])
|
||||
exts? := guard reportExts *> some (extensionsPromise.resultD env.toKernelEnv.extensions)
|
||||
}
|
||||
return {
|
||||
constName, kind
|
||||
@@ -715,9 +846,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
|
||||
checked := checkedEnvPromise.result?.bind (sync := true) fun
|
||||
| some kenv => .pure kenv
|
||||
| none => env.checked }
|
||||
asyncEnv := { env with
|
||||
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
|
||||
}
|
||||
asyncEnv := env.enterAsync constName
|
||||
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
|
||||
}
|
||||
|
||||
@@ -773,6 +902,13 @@ def AddConstAsyncResult.commitCheckEnv (res : AddConstAsyncResult) (env : Enviro
|
||||
def contains (env : Environment) (n : Name) : Bool :=
|
||||
env.findAsync? n |>.isSome
|
||||
|
||||
/--
|
||||
Checks whether the given declaration is known on the current branch, in which case `findAsync?` will
|
||||
not block.
|
||||
-/
|
||||
def containsOnBranch (env : Environment) (n : Name) : Bool :=
|
||||
(env.asyncConsts.find? n |>.isSome) || env.checkedWithoutAsync.constants.contains n
|
||||
|
||||
def header (env : Environment) : EnvironmentHeader :=
|
||||
-- can be assumed to be in sync with `env.checked`; see `setMainModule`, the only modifier of the header
|
||||
env.checkedWithoutAsync.header
|
||||
@@ -783,7 +919,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 :=
|
||||
@@ -880,6 +1019,9 @@ inductive EnvExtension.AsyncMode where
|
||||
| async
|
||||
deriving Inhabited
|
||||
|
||||
abbrev ReplayFn (σ : Type) :=
|
||||
(oldState : σ) → (newState : σ) → (newConsts : List Name) → σ → σ
|
||||
|
||||
/--
|
||||
Environment extension, can only be generated by `registerEnvExtension` that allocates a unique index
|
||||
for this extension into each environment's extension state's array.
|
||||
@@ -888,6 +1030,13 @@ structure EnvExtension (σ : Type) where private mk ::
|
||||
idx : Nat
|
||||
mkInitial : IO σ
|
||||
asyncMode : EnvExtension.AsyncMode
|
||||
/--
|
||||
Optional function that, given state before and after realization and newly added constants,
|
||||
replays this change onto a state from another (derived) environment. This function is used only
|
||||
when making changes to an extension inside a `realizeConst` call, in which case it must be
|
||||
present.
|
||||
-/
|
||||
replay? : Option (ReplayFn σ)
|
||||
deriving Inhabited
|
||||
|
||||
namespace EnvExtension
|
||||
@@ -949,19 +1098,21 @@ from different environment branches are reconciled.
|
||||
Note that in modes `sync` and `async`, `f` will be called twice, on the local and on the `checked`
|
||||
state.
|
||||
-/
|
||||
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ → σ) : Environment := Id.run do
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
match ext.asyncMode with
|
||||
| .mainOnly =>
|
||||
if let some asyncCtx := env.asyncCtx? then
|
||||
let _ : Inhabited Environment := ⟨env⟩
|
||||
panic! s!"Environment.modifyState: environment extension is marked as `mainOnly` but used in \
|
||||
async context '{asyncCtx.declPrefix}'"
|
||||
else
|
||||
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
{if asyncCtx.realizing then "realization" else "async"} context '{asyncCtx.declPrefix}'"
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| .local =>
|
||||
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| _ =>
|
||||
if ext.replay?.isNone then
|
||||
if let some asyncCtx := env.asyncCtx?.filter (·.realizing) then
|
||||
panic! s!"Environment.modifyState: environment extension must set `replay?` field to be \
|
||||
used in realization context '{asyncCtx.declPrefix}'"
|
||||
env.modifyCheckedAsync fun env =>
|
||||
{ env with extensions := unsafe ext.modifyStateImpl env.extensions f }
|
||||
|
||||
@@ -992,6 +1143,24 @@ recommended and should be considered only for important optimizations.
|
||||
opaque getState {σ : Type} [Inhabited σ] (ext : EnvExtension σ) (env : Environment)
|
||||
(asyncMode := ext.asyncMode) : σ
|
||||
|
||||
-- `unsafe` fails to infer `Nonempty` here
|
||||
private unsafe def findStateAsyncUnsafe {σ : Type} [Inhabited σ]
|
||||
(ext : EnvExtension σ) (env : Environment) (declPrefix : Name) : σ :=
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
|
||||
ext.getStateImpl exts.get
|
||||
else
|
||||
ext.getStateImpl env.checkedWithoutAsync.extensions
|
||||
|
||||
/--
|
||||
Returns the final extension state on the environment branch corresponding to the passed declaration
|
||||
name, if any, or otherwise the state on the current branch. In other words, at most one environment
|
||||
branch will be blocked on.
|
||||
-/
|
||||
@[implemented_by findStateAsyncUnsafe]
|
||||
opaque findStateAsync {σ : Type} [Inhabited σ] (ext : EnvExtension σ)
|
||||
(env : Environment) (declPrefix : Name) : σ
|
||||
|
||||
end EnvExtension
|
||||
|
||||
/-- Environment extensions can only be registered during initialization.
|
||||
@@ -1002,12 +1171,13 @@ end EnvExtension
|
||||
Note that by default, extension state is *not* stored in .olean files and will not propagate across `import`s.
|
||||
For that, you need to register a persistent environment extension. -/
|
||||
def registerEnvExtension {σ : Type} (mkInitial : IO σ)
|
||||
(replay? : Option (ReplayFn σ) := none)
|
||||
(asyncMode : EnvExtension.AsyncMode := .mainOnly) : IO (EnvExtension σ) := do
|
||||
unless (← initializing) do
|
||||
throw (IO.userError "failed to register environment, extensions can only be registered during initialization")
|
||||
let exts ← EnvExtension.envExtensionsRef.get
|
||||
let idx := exts.size
|
||||
let ext : EnvExtension σ := { idx, mkInitial, asyncMode }
|
||||
let ext : EnvExtension σ := { idx, mkInitial, asyncMode, replay? }
|
||||
-- safety: `EnvExtensionState` is opaque, so we can upcast to it
|
||||
EnvExtension.envExtensionsRef.modify fun exts => exts.push (unsafe unsafeCast ext)
|
||||
pure ext
|
||||
@@ -1019,7 +1189,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
|
||||
let initializing ← IO.initializing
|
||||
if initializing then throw (IO.userError "environment objects cannot be created during initialization")
|
||||
let exts ← mkInitialExtensionStates
|
||||
pure {
|
||||
return {
|
||||
checkedWithoutAsync := {
|
||||
const2ModIdx := {}
|
||||
constants := {}
|
||||
@@ -1027,6 +1197,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
|
||||
extraConstNames := {}
|
||||
extensions := exts
|
||||
}
|
||||
realizedImportedConsts? := none
|
||||
}
|
||||
|
||||
structure PersistentEnvExtensionState (α : Type) (σ : Type) where
|
||||
@@ -1117,8 +1288,9 @@ def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
|
||||
{ s with state := state }
|
||||
|
||||
/-- Get the current state of the given extension in the given environment. -/
|
||||
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment) : σ :=
|
||||
(ext.toEnvExtension.getState env).state
|
||||
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment)
|
||||
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
|
||||
(ext.toEnvExtension.getState (asyncMode := asyncMode) env).state
|
||||
|
||||
/-- Set the current state of the given extension in the given environment. -/
|
||||
def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (s : σ) : Environment :=
|
||||
@@ -1128,23 +1300,11 @@ def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
|
||||
def modifyState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
ext.toEnvExtension.modifyState env fun ps => { ps with state := f (ps.state) }
|
||||
|
||||
-- `unsafe` fails to infer `Nonempty` here
|
||||
private unsafe def findStateAsyncUnsafe {α β σ : Type} [Inhabited σ]
|
||||
(ext : PersistentEnvExtension α β σ) (env : Environment) (declPrefix : Name) : σ :=
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
|
||||
ext.toEnvExtension.getStateImpl exts.get |>.state
|
||||
else
|
||||
ext.toEnvExtension.getStateImpl env.checkedWithoutAsync.extensions |>.state
|
||||
@[inherit_doc EnvExtension.findStateAsync]
|
||||
def findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
|
||||
(env : Environment) (declPrefix : Name) : σ :=
|
||||
ext.toEnvExtension.findStateAsync env declPrefix |>.state
|
||||
|
||||
/--
|
||||
Returns the final extension state on the environment branch corresponding to the passed declaration
|
||||
name, if any, or otherwise the state on the current branch. In other words, at most one environment
|
||||
branch will be blocked on.
|
||||
-/
|
||||
@[implemented_by findStateAsyncUnsafe]
|
||||
opaque findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
|
||||
(env : Environment) (declPrefix : Name) : σ
|
||||
|
||||
end PersistentEnvExtension
|
||||
|
||||
@@ -1158,11 +1318,14 @@ structure PersistentEnvExtensionDescr (α β σ : Type) where
|
||||
exportEntriesFn : σ → Array α
|
||||
statsFn : σ → Format := fun _ => Format.nil
|
||||
asyncMode : EnvExtension.AsyncMode := .mainOnly
|
||||
replay? : Option (ReplayFn σ) := none
|
||||
|
||||
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
if pExts.any (fun ext => ext.name == descr.name) then throw (IO.userError s!"invalid environment extension, '{descr.name}' has already been used")
|
||||
let ext ← registerEnvExtension (asyncMode := descr.asyncMode) do
|
||||
let replay? := descr.replay?.map fun replay =>
|
||||
fun oldState newState newConsts s => { s with state := replay oldState.state newState.state newConsts s.state }
|
||||
let ext ← registerEnvExtension (asyncMode := descr.asyncMode) (replay? := replay?) do
|
||||
let initial ← descr.mkInitial
|
||||
let s : PersistentEnvExtensionState α σ := {
|
||||
importedEntries := #[],
|
||||
@@ -1195,6 +1358,18 @@ structure SimplePersistentEnvExtensionDescr (α σ : Type) where
|
||||
addImportedFn : Array (Array α) → σ
|
||||
toArrayFn : List α → Array α := fun es => es.toArray
|
||||
asyncMode : EnvExtension.AsyncMode := .mainOnly
|
||||
replay? : Option ((newEntries : List α) → (newState : σ) → σ → List α × σ) := none
|
||||
|
||||
/--
|
||||
Returns a function suitable for `SimplePersistentEnvExtensionDescr.replay?` that replays all new
|
||||
entries onto the state using `addEntryFn`. `p` should filter out entries that have already been
|
||||
added to the state by a prior replay of the same realizable constant.
|
||||
-/
|
||||
def SimplePersistentEnvExtension.replayOfFilter (p : σ → α → Bool)
|
||||
(addEntryFn : σ → α → σ) : List α → σ → σ → List α × σ :=
|
||||
fun newEntries _ s =>
|
||||
let newEntries := newEntries.filter (p s)
|
||||
(newEntries, newEntries.foldl (init := s) addEntryFn)
|
||||
|
||||
def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr : SimplePersistentEnvExtensionDescr α σ) : IO (SimplePersistentEnvExtension α σ) :=
|
||||
registerPersistentEnvExtension {
|
||||
@@ -1206,6 +1381,10 @@ def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr :
|
||||
exportEntriesFn := fun s => descr.toArrayFn s.1.reverse,
|
||||
statsFn := fun s => format "number of local entries: " ++ format s.1.length
|
||||
asyncMode := descr.asyncMode
|
||||
replay? := descr.replay?.map fun replay oldState newState _ (entries, s) =>
|
||||
let newEntries := newState.1.take (newState.1.length - oldState.1.length)
|
||||
let (newEntries, s) := replay newEntries newState.2 s
|
||||
(entries ++ newEntries, s)
|
||||
}
|
||||
|
||||
namespace SimplePersistentEnvExtension
|
||||
@@ -1219,8 +1398,9 @@ def getEntries {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension
|
||||
(PersistentEnvExtension.getState ext env).1
|
||||
|
||||
/-- Get the current state of the given `SimplePersistentEnvExtension`. -/
|
||||
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment) : σ :=
|
||||
(PersistentEnvExtension.getState ext env).2
|
||||
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment)
|
||||
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
|
||||
(PersistentEnvExtension.getState (asyncMode := asyncMode) ext env).2
|
||||
|
||||
/-- Set the current state of the given `SimplePersistentEnvExtension`. This change is *not* persisted across files. -/
|
||||
def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (s : σ) : Environment :=
|
||||
@@ -1230,6 +1410,11 @@ def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : En
|
||||
def modifyState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
PersistentEnvExtension.modifyState ext env (fun ⟨entries, s⟩ => (entries, f s))
|
||||
|
||||
@[inherit_doc PersistentEnvExtension.findStateAsync]
|
||||
def findStateAsync {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ)
|
||||
(env : Environment) (declPrefix : Name) : σ :=
|
||||
PersistentEnvExtension.findStateAsync ext env declPrefix |>.2
|
||||
|
||||
end SimplePersistentEnvExtension
|
||||
|
||||
/-- Environment extension for tagging declarations.
|
||||
@@ -1329,8 +1514,12 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
|
||||
|
||||
def mkModuleData (env : Environment) : IO ModuleData := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
let entries := pExts.map fun pExt =>
|
||||
let state := pExt.getState env
|
||||
let entries := pExts.map fun pExt => Id.run do
|
||||
-- get state from `checked` at the end if `async`; it would otherwise panic
|
||||
let mut asyncMode := pExt.toEnvExtension.asyncMode
|
||||
if asyncMode matches .async then
|
||||
asyncMode := .sync
|
||||
let state := pExt.getState (asyncMode := asyncMode) env
|
||||
(pExt.name, pExt.exportEntriesFn state)
|
||||
let kenv := env.toKernelEnv
|
||||
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
|
||||
@@ -1403,7 +1592,9 @@ where
|
||||
let pExtDescrs ← persistentEnvExtensionsRef.get
|
||||
if h : i < pExtDescrs.size then
|
||||
let extDescr := pExtDescrs[i]
|
||||
let s := extDescr.toEnvExtension.getState env
|
||||
-- `local` as `async` does not allow for `getState` but it's all safe here as there is only
|
||||
-- one branch so far.
|
||||
let s := extDescr.toEnvExtension.getState (asyncMode := .local) env
|
||||
let prevSize := (← persistentEnvExtensionsRef.get).size
|
||||
let prevAttrSize ← getNumBuiltinAttributes
|
||||
let newState ← extDescr.addImportedFn s.importedEntries { env := env, opts := opts }
|
||||
@@ -1522,6 +1713,7 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
moduleData := s.moduleData
|
||||
}
|
||||
}
|
||||
realizedImportedConsts? := none
|
||||
}
|
||||
env ← setImportedEntries env s.moduleData
|
||||
if leakEnv then
|
||||
@@ -1583,6 +1775,9 @@ builtin_initialize namespacesExt : SimplePersistentEnvExtension Name NameSSet
|
||||
let map := mkStateFromImportedEntries (fun map name => map.insert name ()) map as
|
||||
SMap.fromHashMap map |>.switch
|
||||
addEntryFn := fun s n => s.insert n
|
||||
-- Namespaces from local helper constants can be disregarded in other environment branches. We
|
||||
-- do *not* want `getNamespaceSet` to have to wait on all prior branches.
|
||||
asyncMode := .local
|
||||
}
|
||||
|
||||
@[inherit_doc Kernel.Environment.enableDiag]
|
||||
@@ -1616,8 +1811,18 @@ def getNamespaceSet (env : Environment) : NameSSet :=
|
||||
namespacesExt.getState env
|
||||
|
||||
@[export lean_elab_environment_update_base_after_kernel_add]
|
||||
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) : Environment :=
|
||||
{ env with checked := .pure kernel, checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions } }
|
||||
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) (decl : Declaration) : Environment :=
|
||||
{ env with
|
||||
checked := .pure kernel
|
||||
checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions }
|
||||
-- make constants available in `asyncConsts` as well; see its docstring
|
||||
asyncConsts := decl.getNames.foldl (init := env.asyncConsts) fun asyncConsts n =>
|
||||
if asyncConsts.find? n |>.isNone then
|
||||
asyncConsts.add {
|
||||
constInfo := .ofConstantInfo (kernel.find? n |>.get!)
|
||||
exts? := none
|
||||
}
|
||||
else asyncConsts }
|
||||
|
||||
@[export lean_display_stats]
|
||||
def displayStats (env : Environment) : IO Unit := do
|
||||
@@ -1666,6 +1871,107 @@ def hasUnsafe (env : Environment) (e : Expr) : Bool :=
|
||||
| _ => false;
|
||||
c?.isSome
|
||||
|
||||
/-- Plumbing function for `Lean.Meta.realizeConst`; see documentation there. -/
|
||||
def realizeConst (env : Environment) (forConst : Name) (constName : Name)
|
||||
(realize : Environment → Options → BaseIO (Environment × Dynamic)) :
|
||||
IO (Environment × Dynamic) := do
|
||||
let mut env := env
|
||||
-- find `RealizationContext` for `forConst` in `realizedImportedConsts?` or `realizedLocalConsts`
|
||||
let ctx ← if env.checkedWithoutAsync.const2ModIdx.contains forConst then
|
||||
env.realizedImportedConsts?.getDM <|
|
||||
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
|
||||
else
|
||||
match env.realizedLocalConsts.find? forConst with
|
||||
| some ctx => pure ctx
|
||||
| none =>
|
||||
throw <| .userError s!"trying to realize {constName} but `enableRealizationsForConst` must be called for '{forConst}' first"
|
||||
let prom ← IO.Promise.new
|
||||
-- ensure `prom` is not left unresolved from stray exceptions
|
||||
BaseIO.toIO do
|
||||
-- atomically check whether we are the first branch to realize `constName`
|
||||
let existingConsts? ← ctx.constsRef.modifyGet fun m => match m.find? constName with
|
||||
| some prom' => (some prom', m)
|
||||
| none => (none, m.insert constName prom.result!)
|
||||
let (consts, replay, dyn) ← if let some existingConsts := existingConsts? then
|
||||
pure existingConsts.get
|
||||
else
|
||||
-- safety: `RealizationContext` is private
|
||||
let realizeEnv : Environment := unsafe unsafeCast ctx.env
|
||||
let realizeEnv := { realizeEnv with
|
||||
-- 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
|
||||
let realizeEnv := realizeEnv.enterAsync (realizing := true) forConst
|
||||
-- skip kernel in `realize`, we'll re-typecheck anyway
|
||||
let realizeOpts := debug.skipKernelTC.set ctx.opts true
|
||||
let (realizeEnv', dyn) ← realize realizeEnv realizeOpts
|
||||
-- We could check that `c` was indeed added here but in practice `realize` has already
|
||||
-- reported an error so we don't.
|
||||
|
||||
-- find new constants incl. nested realizations, add current extension state, and compute
|
||||
-- closure
|
||||
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 }
|
||||
else c
|
||||
let exts ← EnvExtension.envExtensionsRef.get
|
||||
let replay := (maybeAddToKernelEnv realizeEnv realizeEnv' consts · exts)
|
||||
prom.resolve (consts, replay, dyn)
|
||||
pure (consts, replay, dyn)
|
||||
return ({ env with
|
||||
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
|
||||
-- Adds `consts` if they haven't already been added by a previous branch. Note that this
|
||||
-- conditional is deterministic because of the linearizing effect of `env.checked`.
|
||||
maybeAddToKernelEnv (oldEnv newEnv : Environment) (consts : List AsyncConst)
|
||||
(kenv : Kernel.Environment)
|
||||
(exts : Array (EnvExtension EnvExtensionState)) : Kernel.Environment := Id.run do
|
||||
let mut kenv := kenv
|
||||
for c in consts do
|
||||
if kenv.find? c.constInfo.name |>.isSome then
|
||||
continue
|
||||
let info := c.constInfo.toConstantInfo
|
||||
if info.isUnsafe then
|
||||
-- Checking unsafe declarations is not necessary for consistency, and it is necessary to
|
||||
-- avoid checking them in the case of the old code generator, which adds ill-typed constants
|
||||
-- to the kernel environment. We can delete this branch after removing the old code
|
||||
-- generator.
|
||||
kenv := kenv.add info
|
||||
continue
|
||||
let decl := match info with
|
||||
| .thmInfo thm => .thmDecl thm
|
||||
| .defnInfo defn => .defnDecl defn
|
||||
| _ => panic! s!"Environment.realizeConst: {c.constInfo.name} must be definition/theorem"
|
||||
-- realized kernel additions cannot be interrupted - which would be bad anyway as they can be
|
||||
-- reused between snapshots
|
||||
match kenv.addDeclCore 0 decl none with
|
||||
| .ok kenv' => kenv := kenv'
|
||||
| .error e =>
|
||||
let _ : Inhabited Kernel.Environment := ⟨kenv⟩
|
||||
panic! s!"Environment.realizeConst: failed to add {c.constInfo.name} to environment\n{e.toRawString}"
|
||||
for ext in exts do
|
||||
if let some replay := ext.replay? then
|
||||
kenv := { kenv with
|
||||
-- safety: like in `modifyState`, but that one takes an elab env instead of a kernel env
|
||||
extensions := unsafe (ext.modifyStateImpl kenv.extensions <|
|
||||
replay
|
||||
(ext.getStateImpl oldEnv.toKernelEnv.extensions)
|
||||
(ext.getStateImpl newEnv.toKernelEnv.extensions)
|
||||
(consts.map (·.constInfo.name))) }
|
||||
return kenv
|
||||
|
||||
end Environment
|
||||
|
||||
namespace Kernel
|
||||
@@ -1721,4 +2027,13 @@ def mkDefinitionValInferrringUnsafe [Monad m] [MonadEnv m] (name : Name) (levelP
|
||||
let safety := if env.hasUnsafe type || env.hasUnsafe value then DefinitionSafety.unsafe else DefinitionSafety.safe
|
||||
return { name, levelParams, type, value, hints, safety }
|
||||
|
||||
def getMaxHeight (env : Environment) (e : Expr) : UInt32 :=
|
||||
e.foldConsts 0 fun constName max =>
|
||||
match env.find? constName with
|
||||
| ConstantInfo.defnInfo val =>
|
||||
match val.hints with
|
||||
| ReducibilityHints.regular h => if h > max then h else max
|
||||
| _ => max
|
||||
| _ => max
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -1272,11 +1272,23 @@ This operation traverses the expression tree.
|
||||
@[extern "lean_expr_has_loose_bvar"]
|
||||
opaque hasLooseBVar (e : @& Expr) (bvarIdx : @& Nat) : Bool
|
||||
|
||||
/-- Return true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter, or in the range if `tryRange == true`. -/
|
||||
def hasLooseBVarInExplicitDomain : Expr → Nat → Bool → Bool
|
||||
| Expr.forallE _ d b bi, bvarIdx, tryRange =>
|
||||
(bi.isExplicit && hasLooseBVar d bvarIdx) || hasLooseBVarInExplicitDomain b (bvarIdx+1) tryRange
|
||||
| e, bvarIdx, tryRange => tryRange && hasLooseBVar e bvarIdx
|
||||
/--
|
||||
Returns true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter,
|
||||
or in the range if `considerRange == true`.
|
||||
Additionally, if the bound variable appears in an implicit parameter,
|
||||
it transitively looks for that implicit parameter.
|
||||
-/
|
||||
-- This should be kept in sync with `lean::has_loose_bvars_in_domain`
|
||||
def hasLooseBVarInExplicitDomain (e : Expr) (bvarIdx : Nat) (considerRange : Bool) : Bool :=
|
||||
match e with
|
||||
| Expr.forallE _ d b bi =>
|
||||
(hasLooseBVar d bvarIdx
|
||||
&& (bi.isExplicit
|
||||
-- "Transitivity": bvar occurs in current implicit argument,
|
||||
-- so we search for the current argument in the body.
|
||||
|| hasLooseBVarInExplicitDomain b 0 considerRange))
|
||||
|| hasLooseBVarInExplicitDomain b (bvarIdx+1) considerRange
|
||||
| e => considerRange && hasLooseBVar e bvarIdx
|
||||
|
||||
/--
|
||||
Lower the loose bound variables `>= s` in `e` by `d`.
|
||||
@@ -1297,16 +1309,16 @@ opaque liftLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
|
||||
It marks any parameter with an explicit binder annotation if there is another explicit arguments that depends on it or
|
||||
the resulting type if `considerRange == true`.
|
||||
|
||||
Remark: we use this function to infer the bind annotations of inductive datatype constructors, and structure projections.
|
||||
When the `{}` annotation is used in these commands, we set `considerRange == false`.
|
||||
Remark: we use this function to infer the binder annotations of structure projections.
|
||||
-/
|
||||
def inferImplicit : Expr → Nat → Bool → Expr
|
||||
| Expr.forallE n d b bi, i+1, considerRange =>
|
||||
-- This should be kept in synch with `lean::infer_implicit`
|
||||
def inferImplicit (e : Expr) (numParams : Nat) (considerRange : Bool) : Expr :=
|
||||
match e, numParams with
|
||||
| Expr.forallE n d b bi, i + 1 =>
|
||||
let b := inferImplicit b i considerRange
|
||||
let newInfo := if bi.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else bi
|
||||
mkForall n newInfo d b
|
||||
| e, 0, _ => e
|
||||
| e, _, _ => e
|
||||
| e, _ => e
|
||||
|
||||
/--
|
||||
Instantiates the loose bound variables in `e` using the `subst` array,
|
||||
|
||||
@@ -184,7 +184,7 @@ structure SnapshotTree where
|
||||
element : Snapshot
|
||||
/-- The asynchronously available children of the snapshot tree node. -/
|
||||
children : Array (SnapshotTask SnapshotTree)
|
||||
deriving Inhabited
|
||||
deriving Inhabited, TypeName
|
||||
|
||||
/--
|
||||
Helper class for projecting a heterogeneous hierarchy of snapshot classes to a homogeneous
|
||||
|
||||
@@ -177,6 +177,55 @@ created from a quotation, the ref usually has to be changed to a less variable s
|
||||
simple example and the implementation of tactic `have` for a complex example.
|
||||
-/
|
||||
|
||||
/-
|
||||
# Note [Incremental Cancellation]
|
||||
|
||||
With incrementality, there is a tension between telling the elaboration of the previous document
|
||||
version(s) to stop processing as soon as possible in order to free resources for use in the current
|
||||
elaboration run and having the old version continue in case its results can be used as is so as not
|
||||
to duplicate effort.
|
||||
|
||||
Before parallelism, we were able to use a single cancellation token for the entire elaboration of a
|
||||
specific document version. We could trigger the cancellation token as soon as we started elaborating
|
||||
a new version because the generated exceptions would prevent the elaborator from storing any
|
||||
half-elaborated state in snapshots that might then be picked up for reuse. This was a simple and
|
||||
sound solution, though it did mean we may have cancelled some work eagerly that could have been
|
||||
reused.
|
||||
|
||||
This approach is no longer sound with parallelism: a tactic may have spawned async tasks (e.g.
|
||||
kernel checking of a helper definition) and then completed, creating a snapshot that references the
|
||||
result of the task e.g. via an asynchronous constant in the environment. If we then interrupted
|
||||
elaboration of that document version throughout all its tasks, we might end up with a snapshot that
|
||||
looks eligible for reuse but references data (eventually) resulting from cancellation. We could
|
||||
(asynchronously) wait for it to complete and then check whether it completed because of cancellation
|
||||
and redo its work in that case but that would be as wasteful as mentioned above and add new latency
|
||||
on top.
|
||||
|
||||
Instead, we now make sure we cancel only the parts of elaboration we have ruled out for reuse, at
|
||||
the earliest point where we can decide that. We do this by storing cancellation tokens in the
|
||||
snapshot tree such that we can trigger all tokens of tasks belonging to a specific subtree
|
||||
(`SnapshotTask.cancelRec`; async tasks belonging to the subtree are usually collected via
|
||||
`Core.getAndEmptySnapshotTasks`). Thus when traversing the old snapshot tree, we need to be careful
|
||||
about cancelling any children we decide not to descend into. This is automated in e.g.
|
||||
`withNarrowedTacticReuse` but not in other places that do not lend themselves to abstraction into
|
||||
combinators. Note that we can still cancel parsing and elaboration below the changed command eagerly
|
||||
as we never consider them for reuse.
|
||||
|
||||
This approach is still not optimal in the sense that async tasks in later snapshots not part of the
|
||||
current subtree are considered for cancellation only when elaboration reaches that point. Thus if
|
||||
inside a single proof we have some significant work done synchronously by one tactic and then
|
||||
significant work done asynchronously by a later tactic and neither tactic is eligible for reuse, the
|
||||
second task will only be cancelled after redoing the synchronous work up to the point of the second
|
||||
tactic. However, as tactics such as `bv_decide` that do significant kernel work do so synchronously
|
||||
at the moment in order to post-process any failures and as the most significant async work, that of
|
||||
checking/compiling/linting/... the top-level definition, is interrupted immediately when the mutual
|
||||
def elaborator notices that the body syntax has changed, this should not be a significant issue in
|
||||
practice. If we do want to optimize this, instead of cancelling subtrees of the snapshot tree, we
|
||||
would likely have to store an asynchronously resolved list of cancellation tokens associated with
|
||||
the tactic snapshot at hand *and all further snapshots* so that we can cancel them eagerly instead
|
||||
of waiting for elaboration to visit those later snapshots.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace Lean.Language.Lean
|
||||
@@ -425,6 +474,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 := {
|
||||
@@ -471,7 +521,7 @@ where
|
||||
|
||||
parseCmd (old? : Option CommandParsedSnapshot) (parserState : Parser.ModuleParserState)
|
||||
(cmdState : Command.State) (prom : IO.Promise CommandParsedSnapshot) (sync : Bool)
|
||||
(cancelTk : IO.CancelToken) : LeanProcessingM Unit := do
|
||||
(parseCancelTk : IO.CancelToken) : LeanProcessingM Unit := do
|
||||
let ctx ← read
|
||||
|
||||
let unchanged old newParserState : BaseIO Unit :=
|
||||
@@ -519,13 +569,21 @@ where
|
||||
if stx.eqWithInfo old.stx then
|
||||
-- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged`
|
||||
return (← unchanged old parserState)
|
||||
-- on first change, make sure to cancel old invocation
|
||||
-- TODO: cancel nested tasks on invalidation
|
||||
old.elabSnap.cancelTk?.forM (·.set)
|
||||
-- On first change, immediately cancel old invocation for all subsequent commands. This
|
||||
-- includes setting the global parse cancellation token, which is stored in
|
||||
-- `next?` below. Thus we can be sure that no further commands will start to elaborate in the
|
||||
-- old invocation from this point on.
|
||||
old.nextCmdSnap?.forM (·.cancelRec)
|
||||
-- For the current command, we depend on the elaborator to either reuse parts of `old` or
|
||||
-- cancel them as soon as reuse can be ruled out.
|
||||
|
||||
-- check for cancellation, most likely during elaboration of previous command, before starting
|
||||
-- processing of next command
|
||||
if (← cancelTk.isSet) then
|
||||
if (← parseCancelTk.isSet) then
|
||||
if let some old := old? then
|
||||
-- all of `old` is discarded, so cancel all of it
|
||||
toSnapshotTree old |>.children.forM (·.cancelRec)
|
||||
|
||||
-- this is a bit ugly as we don't want to adjust our API with `Option`s just for cancellation
|
||||
-- (as no-one should look at this result in that case) but anything containing `Environment`
|
||||
-- is not `Inhabited`
|
||||
@@ -563,19 +621,22 @@ where
|
||||
let nextCmdSnap? := next?.map ({
|
||||
stx? := none
|
||||
reportingRange? := some ⟨parserState.pos, ctx.input.endPos⟩
|
||||
cancelTk? := parseCancelTk
|
||||
task := ·.result!
|
||||
})
|
||||
let diagnostics ← Snapshot.Diagnostics.ofMessageLog msgLog
|
||||
|
||||
-- use per-command cancellation token for elaboration so that
|
||||
let elabCmdCancelTk ← IO.CancelToken.new
|
||||
prom.resolve {
|
||||
diagnostics, finishedSnap, nextCmdSnap?
|
||||
stx := stx', parserState := parserState'
|
||||
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some cancelTk }
|
||||
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
|
||||
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result! }
|
||||
}
|
||||
let cmdState ← doElab stx cmdState beginPos
|
||||
{ old? := old?.map fun old => ⟨old.stx, old.elabSnap⟩, new := elabPromise }
|
||||
finishedPromise cancelTk ctx
|
||||
finishedPromise elabCmdCancelTk ctx
|
||||
let traceTask ←
|
||||
if (← isTracingEnabledForCore `Elab.snapshotTree cmdState.scopes.head!.opts) then
|
||||
-- We want to trace all of `CommandParsedSnapshot` but `traceTask` is part of it, so let's
|
||||
@@ -609,7 +670,7 @@ where
|
||||
}
|
||||
if let some next := next? then
|
||||
-- We're definitely off the fast-forwarding path now
|
||||
parseCmd none parserState cmdState next (sync := false) cancelTk ctx
|
||||
parseCmd none parserState cmdState next (sync := false) elabCmdCancelTk ctx
|
||||
|
||||
doElab (stx : Syntax) (cmdState : Command.State) (beginPos : String.Pos)
|
||||
(snap : SnapshotBundle DynamicSnapshot) (finishedPromise : IO.Promise CommandFinishedSnapshot)
|
||||
|
||||
@@ -159,14 +159,16 @@ end LocalDecl
|
||||
|
||||
/-- A LocalContext is an ordered set of local variable declarations.
|
||||
It is used to store the free variables (also known as local constants) that
|
||||
are in scope.
|
||||
are in scope. It also maps free variables corresponding to auxiliary declarations
|
||||
(recursive references and `where` and `let rec` bindings) to their fully-qualified global names.
|
||||
|
||||
When inspecting a goal or expected type in the infoview, the local
|
||||
context is all of the variables above the `⊢` symbol.
|
||||
-/
|
||||
structure LocalContext where
|
||||
fvarIdToDecl : PersistentHashMap FVarId LocalDecl := {}
|
||||
decls : PersistentArray (Option LocalDecl) := {}
|
||||
fvarIdToDecl : PersistentHashMap FVarId LocalDecl := {}
|
||||
decls : PersistentArray (Option LocalDecl) := {}
|
||||
auxDeclToFullName : FVarIdMap Name := {}
|
||||
deriving Inhabited
|
||||
|
||||
namespace LocalContext
|
||||
@@ -186,10 +188,10 @@ It should not be used directly since the argument `(fvarId : FVarId)` is
|
||||
assumed to be unique. You can create a unique fvarId with `mkFreshFVarId`. -/
|
||||
def mkLocalDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (bi : BinderInfo := BinderInfo.default) (kind : LocalDeclKind := .default) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
let idx := decls.size
|
||||
let decl := LocalDecl.cdecl idx fvarId userName type bi kind
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl }
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
|
||||
|
||||
-- `mkLocalDecl` without `kind`
|
||||
@[export lean_local_ctx_mk_local_decl]
|
||||
@@ -199,23 +201,34 @@ private def mkLocalDeclExported (lctx : LocalContext) (fvarId : FVarId) (userNam
|
||||
/-- Low level API for let declarations. Do not use directly.-/
|
||||
def mkLetDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep := false) (kind : LocalDeclKind := default) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
let idx := decls.size
|
||||
let decl := LocalDecl.ldecl idx fvarId userName type value nonDep kind
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl }
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
|
||||
|
||||
@[export lean_local_ctx_mk_let_decl]
|
||||
private def mkLetDeclExported (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep : Bool) : LocalContext :=
|
||||
mkLetDecl lctx fvarId userName type value nonDep
|
||||
|
||||
/-- Low level API for auxiliary declarations. Do not use directly. -/
|
||||
def mkAuxDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (fullName : Name) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
let idx := decls.size
|
||||
let decl := LocalDecl.cdecl idx fvarId userName type .default .auxDecl
|
||||
let auxDeclToFullName := auxDeclToFullName.insert fvarId fullName
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
|
||||
|
||||
/-- Low level API for adding a local declaration.
|
||||
Do not use directly. -/
|
||||
def addDecl (lctx : LocalContext) (newDecl : LocalDecl) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
let idx := decls.size
|
||||
let newDecl := newDecl.setIndex idx
|
||||
{ fvarIdToDecl := map.insert newDecl.fvarId newDecl, decls := decls.push newDecl }
|
||||
{ fvarIdToDecl := map.insert newDecl.fvarId newDecl
|
||||
decls := decls.push newDecl
|
||||
auxDeclToFullName }
|
||||
|
||||
@[export lean_local_ctx_find]
|
||||
def find? (lctx : LocalContext) (fvarId : FVarId) : Option LocalDecl :=
|
||||
@@ -260,18 +273,26 @@ private partial def popTailNoneAux (a : PArray (Option LocalDecl)) : PArray (Opt
|
||||
@[export lean_local_ctx_erase]
|
||||
def erase (lctx : LocalContext) (fvarId : FVarId) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
match map.find? fvarId with
|
||||
| none => lctx
|
||||
| some decl => { fvarIdToDecl := map.erase fvarId, decls := popTailNoneAux (decls.set decl.index none) }
|
||||
| some decl =>
|
||||
{ fvarIdToDecl := map.erase fvarId
|
||||
decls := popTailNoneAux (decls.set decl.index none)
|
||||
auxDeclToFullName :=
|
||||
if decl.isAuxDecl then auxDeclToFullName.erase fvarId else auxDeclToFullName }
|
||||
|
||||
def pop (lctx : LocalContext): LocalContext :=
|
||||
def pop (lctx : LocalContext) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
if _ : decls.size = 0 then lctx
|
||||
else match decls[decls.size - 1] with
|
||||
| none => lctx -- unreachable
|
||||
| some decl => { fvarIdToDecl := map.erase decl.fvarId, decls := popTailNoneAux decls.pop }
|
||||
| some decl =>
|
||||
{ fvarIdToDecl := map.erase decl.fvarId
|
||||
decls := popTailNoneAux decls.pop
|
||||
auxDeclToFullName :=
|
||||
if decl.isAuxDecl then auxDeclToFullName.erase decl.fvarId else auxDeclToFullName }
|
||||
|
||||
def findFromUserName? (lctx : LocalContext) (userName : Name) : Option LocalDecl :=
|
||||
lctx.decls.findSomeRev? fun decl =>
|
||||
@@ -298,18 +319,20 @@ def lastDecl (lctx : LocalContext) : Option LocalDecl :=
|
||||
def setUserName (lctx : LocalContext) (fvarId : FVarId) (userName : Name) : LocalContext :=
|
||||
let decl := lctx.get! fvarId
|
||||
let decl := decl.setUserName userName
|
||||
{ fvarIdToDecl := lctx.fvarIdToDecl.insert decl.fvarId decl,
|
||||
decls := lctx.decls.set decl.index decl }
|
||||
{ fvarIdToDecl := lctx.fvarIdToDecl.insert decl.fvarId decl,
|
||||
decls := lctx.decls.set decl.index decl,
|
||||
auxDeclToFullName := lctx.auxDeclToFullName }
|
||||
|
||||
def renameUserName (lctx : LocalContext) (fromName : Name) (toName : Name) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
match lctx.findFromUserName? fromName with
|
||||
| none => lctx
|
||||
| some decl =>
|
||||
let decl := decl.setUserName toName;
|
||||
{ fvarIdToDecl := map.insert decl.fvarId decl,
|
||||
decls := decls.set decl.index decl }
|
||||
decls := decls.set decl.index decl,
|
||||
auxDeclToFullName }
|
||||
|
||||
/--
|
||||
Low-level function for updating the local context.
|
||||
@@ -317,13 +340,14 @@ def renameUserName (lctx : LocalContext) (fromName : Name) (toName : Name) : Loc
|
||||
the `index` nor `fvarId` are modified. -/
|
||||
@[inline] def modifyLocalDecl (lctx : LocalContext) (fvarId : FVarId) (f : LocalDecl → LocalDecl) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls } =>
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
match lctx.find? fvarId with
|
||||
| none => lctx
|
||||
| some decl =>
|
||||
let decl := f decl
|
||||
{ fvarIdToDecl := map.insert decl.fvarId decl
|
||||
decls := decls.set decl.index decl }
|
||||
decls := decls.set decl.index decl
|
||||
auxDeclToFullName }
|
||||
|
||||
/--
|
||||
Set the kind of the given fvar.
|
||||
@@ -520,6 +544,7 @@ def LocalDecl.replaceFVarId (fvarId : FVarId) (e : Expr) (d : LocalDecl) : Local
|
||||
def LocalContext.replaceFVarId (fvarId : FVarId) (e : Expr) (lctx : LocalContext) : LocalContext :=
|
||||
let lctx := lctx.erase fvarId
|
||||
{ fvarIdToDecl := lctx.fvarIdToDecl.map (·.replaceFVarId fvarId e)
|
||||
decls := lctx.decls.map fun localDecl? => localDecl?.map (·.replaceFVarId fvarId e) }
|
||||
decls := lctx.decls.map fun localDecl? => localDecl?.map (·.replaceFVarId fvarId e),
|
||||
auxDeclToFullName := lctx.auxDeclToFullName }
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -241,6 +241,11 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
|
||||
| nCtx, ctx, compose d₁ d₂ => return (← formatAux nCtx ctx d₁) ++ (← formatAux nCtx ctx d₂)
|
||||
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, trace data header children => do
|
||||
let childFmts ← children.mapM (formatAux nCtx ctx)
|
||||
if data.cls.isAnonymous then
|
||||
-- Sequence of top-level traces collected by `addTraceAsMessages`, do not indent.
|
||||
return .joinSep childFmts.toList "\n"
|
||||
|
||||
let mut msg := f!"[{data.cls}]"
|
||||
if data.startTime != 0 then
|
||||
msg := f!"{msg} [{data.stopTime - data.startTime}]"
|
||||
@@ -250,7 +255,6 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
|
||||
if maxNum > 0 && children.size > maxNum then
|
||||
children := children.take maxNum |>.push <|
|
||||
ofFormat f!"{children.size - maxNum} more entries... (increase `maxTraceChildren` to see more)"
|
||||
let childFmts ← children.mapM (formatAux nCtx ctx)
|
||||
return .nest 2 (.joinSep (msg::childFmts.toList) "\n")
|
||||
| nCtx, ctx?, ofLazy pp _ => do
|
||||
let dyn ← pp (ctx?.map (mkPPContext nCtx))
|
||||
|
||||
@@ -1664,6 +1664,21 @@ def withLocalDeclsDND [Inhabited α] (declInfos : Array (Name × Expr)) (k : (xs
|
||||
withLocalDeclsD
|
||||
(declInfos.map (fun (name, typeCtor) => (name, fun _ => pure typeCtor))) k
|
||||
|
||||
private def withAuxDeclImp (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr → MetaM α) : MetaM α := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
let ctx ← read
|
||||
let lctx := ctx.lctx.mkAuxDecl fvarId shortDeclName type declName
|
||||
let fvar := mkFVar fvarId
|
||||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||||
withNewFVar fvar type k
|
||||
|
||||
/--
|
||||
Declare an auxiliary local declaration `shortDeclName : type` for elaborating recursive
|
||||
declaration `declName`, update the mapping `auxDeclToFullName`, and then execute `k`.
|
||||
-/
|
||||
def withAuxDecl (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr → n α) : n α :=
|
||||
map1MetaM (fun k => withAuxDeclImp shortDeclName type declName k) k
|
||||
|
||||
private def withNewBinderInfosImp (bs : Array (FVarId × BinderInfo)) (k : MetaM α) : MetaM α := do
|
||||
let lctx := bs.foldl (init := (← getLCtx)) fun lctx (fvarId, bi) =>
|
||||
lctx.setBinderInfo fvarId bi
|
||||
@@ -2203,10 +2218,110 @@ def instantiateMVarsIfMVarApp (e : Expr) : MetaM Expr := do
|
||||
else
|
||||
return e
|
||||
|
||||
private partial def setAllDiagRanges (snap : Language.SnapshotTree) (pos endPos : Position) :
|
||||
BaseIO Language.SnapshotTree := do
|
||||
let msgLog := snap.element.diagnostics.msgLog
|
||||
let msgLog := { msgLog with unreported := msgLog.unreported.map fun diag =>
|
||||
{ diag with pos, endPos } }
|
||||
return {
|
||||
element.diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgLog)
|
||||
children := (← snap.children.mapM fun task => return { task with
|
||||
stx? := none
|
||||
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 (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
|
||||
arguments, if any. `realize` must return with `constName` added to the environment,
|
||||
at which point all callers of `realizeConst` with this `constName` will be unblocked
|
||||
and have access to an updated version of their own environment containing any new constants
|
||||
`realize` added, including recursively realized constants. Traces, diagnostics, and raw std stream
|
||||
output are reported at all callers via `Core.logSnapshotTask` (so that the location of generated
|
||||
diagnostics is deterministic). Note that, as `realize` is run using the options at declaration time
|
||||
of `forConst`, trace options must be set prior to that (or, for imported constants, on the cmdline)
|
||||
in order to be active. The environment extension state at the end of `realize` is available to each
|
||||
caller via `EnvExtension.findStateAsync` for `constName`. If `realize` throws an exception or fails
|
||||
to add `constName` to the environment, an appropriate diagnostic is reported to all callers but no
|
||||
constants are added to the environment.
|
||||
-/
|
||||
def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
|
||||
MetaM Unit := do
|
||||
let env ← getEnv
|
||||
-- If `constName` is already known on this branch, avoid the trace node. We should not use
|
||||
-- `contains` as it could block as well as find realizations on other branches, which would lack
|
||||
-- the relevant local environment extension state when accessed on this branch.
|
||||
if env.containsOnBranch constName then
|
||||
return
|
||||
withTraceNode `Meta.realizeConst (fun _ => return constName) do
|
||||
let coreCtx ← readThe Core.Context
|
||||
let coreCtx := {
|
||||
-- these fields should be invariant throughout the file
|
||||
fileName := coreCtx.fileName, fileMap := coreCtx.fileMap
|
||||
-- heartbeat limits inside `realizeAndReport` should be measured from this point on
|
||||
initHeartbeats := (← IO.getNumHeartbeats)
|
||||
}
|
||||
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
|
||||
let coreCtx := { coreCtx with options := opts }
|
||||
let act :=
|
||||
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get opts) do
|
||||
-- catch all exceptions
|
||||
let _ : MonadExceptOf _ MetaM := MonadAlwaysExcept.except
|
||||
try
|
||||
realize
|
||||
if !(← getEnv).contains constName then
|
||||
throwError "Lean.Meta.realizeConst: {constName} was not added to the environment"
|
||||
finally
|
||||
addTraceAsMessages
|
||||
let res? ← act |>.run' |>.run coreCtx { env } |>.toBaseIO
|
||||
match res? with
|
||||
| .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
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Meta.isLevelDefEq.postponed
|
||||
registerTraceClass `Meta.realizeConst
|
||||
|
||||
export Meta (MetaM)
|
||||
|
||||
|
||||
@@ -784,6 +784,7 @@ def mkMatcherAuxDefinition (name : Name) (type : Expr) (value : Expr) : MetaM (E
|
||||
modifyEnv fun env => matcherExt.modifyState env fun s => s.insert (result.value, compile) name
|
||||
addMatcherInfo name mi
|
||||
setInlineAttribute name
|
||||
enableRealizationsForConst name
|
||||
if compile then
|
||||
compileDecl decl
|
||||
return (mkMatcherConst name, some addMatcher)
|
||||
|
||||
@@ -663,13 +663,15 @@ where
|
||||
else
|
||||
k altsNew
|
||||
|
||||
/--
|
||||
Create conditional equations and splitter for the given match auxiliary declaration. -/
|
||||
private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns := withLCtx {} {} do
|
||||
withTraceNode `Meta.Match.matchEqs (fun _ => return m!"mkEquationsFor '{matchDeclName}'") do
|
||||
withConfig (fun c => { c with etaStruct := .none }) do
|
||||
/-
|
||||
Creates conditional equations and splitter for the given match auxiliary declaration.
|
||||
|
||||
See also `getEquationsFor`.
|
||||
-/
|
||||
@[export lean_get_match_equations_for]
|
||||
def getEquationsForImpl (matchDeclName : Name) : MetaM MatchEqns := do
|
||||
/-
|
||||
Remark: user have requested the `split` tactic to be available for writing code.
|
||||
Remark: users have requested the `split` tactic to be available for writing code.
|
||||
Thus, the `splitter` declaration must be a definition instead of a theorem.
|
||||
Moreover, the `splitter` is generated on demand, and we currently
|
||||
can't import the same definition from different modules. Thus, we must
|
||||
@@ -677,6 +679,12 @@ private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns :=
|
||||
-/
|
||||
let baseName := mkPrivateName (← getEnv) matchDeclName
|
||||
let splitterName := baseName ++ `splitter
|
||||
-- NOTE: `go` will generate both splitter and equations but we use the splitter as the "key" for
|
||||
-- `realizeConst` as well as for looking up the resultant environment extension sate via
|
||||
-- `findStateAsync`.
|
||||
realizeConst matchDeclName splitterName (go baseName splitterName)
|
||||
return matchEqnsExt.findStateAsync (← getEnv) splitterName |>.map.find! matchDeclName
|
||||
where go baseName splitterName := withConfig (fun c => { c with etaStruct := .none }) do
|
||||
let constInfo ← getConstInfo matchDeclName
|
||||
let us := constInfo.levelParams.map mkLevelParam
|
||||
let some matchInfo ← getMatcherInfo? matchDeclName | throwError "'{matchDeclName}' is not a matcher function"
|
||||
@@ -758,14 +766,6 @@ private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns :=
|
||||
setInlineAttribute splitterName
|
||||
let result := { eqnNames, splitterName, splitterAltNumParams }
|
||||
registerMatchEqns matchDeclName result
|
||||
return result
|
||||
|
||||
/- See header at `MatchEqsExt.lean` -/
|
||||
@[export lean_get_match_equations_for]
|
||||
def getEquationsForImpl (matchDeclName : Name) : MetaM MatchEqns := do
|
||||
match matchEqnsExt.getState (← getEnv) |>.map.find? matchDeclName with
|
||||
| some matchEqns => return matchEqns
|
||||
| none => mkEquationsFor matchDeclName
|
||||
|
||||
builtin_initialize registerTraceClass `Meta.Match.matchEqs
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ structure MatchEqnsExtState where
|
||||
|
||||
/- We generate the equations and splitter on demand, and do not save them on .olean files. -/
|
||||
builtin_initialize matchEqnsExt : EnvExtension MatchEqnsExtState ←
|
||||
registerEnvExtension (pure {})
|
||||
-- Using `local` allows us to use the extension in `realizeConst` without specifying `replay?`.
|
||||
-- The resulting state can still be accessed on the generated declarations using `findStateAsync`;
|
||||
-- see below
|
||||
registerEnvExtension (pure {}) (asyncMode := .local)
|
||||
|
||||
def registerMatchEqns (matchDeclName : Name) (matchEqns : MatchEqns) : CoreM Unit := do
|
||||
modifyEnv fun env => matchEqnsExt.modifyState env fun { map, eqns } => {
|
||||
@@ -41,7 +44,7 @@ opaque getEquationsFor (matchDeclName : Name) : MetaM MatchEqns
|
||||
/--
|
||||
Returns `true` if `declName` is the name of a `match` equational theorem.
|
||||
-/
|
||||
def isMatchEqnTheorem (env : Environment) (declName : Name) : Bool :=
|
||||
matchEqnsExt.getState env |>.eqns.contains declName
|
||||
def isMatchEqnTheorem (env : Environment) (declName : Name) : Bool := Id.run do
|
||||
matchEqnsExt.findStateAsync env declName |>.eqns.contains declName
|
||||
|
||||
end Lean.Meta.Match
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
|
||||
Additional helper methods that require `MetaM` infrastructure.
|
||||
Authors: Leonardo de Moura, Kyle Miller
|
||||
-/
|
||||
prelude
|
||||
import Lean.AddDecl
|
||||
import Lean.Structure
|
||||
import Lean.Meta.AppBuilder
|
||||
|
||||
/-!
|
||||
# Structure methods that require `MetaM` infrastructure
|
||||
-/
|
||||
|
||||
namespace Lean.Meta
|
||||
|
||||
/--
|
||||
If `struct` is an application of the form `S ..` with `S` a constant for a structure,
|
||||
returns the name of the structure, otherwise throws an error.
|
||||
-/
|
||||
def getStructureName (struct : Expr) : MetaM Name :=
|
||||
match struct.getAppFn with
|
||||
| Expr.const declName .. => do
|
||||
@@ -19,4 +26,87 @@ def getStructureName (struct : Expr) : MetaM Name :=
|
||||
return declName
|
||||
| _ => throwError "expected structure"
|
||||
|
||||
/--
|
||||
Structure projection declaration for `mkProjections`.
|
||||
-/
|
||||
structure StructProjDecl where
|
||||
ref : Syntax
|
||||
projName : Name
|
||||
|
||||
/--
|
||||
Adds projection functions to the environment for the one-constructor inductive type named `n`.
|
||||
- The `projName`s in each `StructProjDecl` are used for the names of the declarations added to the environment.
|
||||
- If `instImplicit` is true, then generates projections with `self` being instance implicit.
|
||||
|
||||
Notes:
|
||||
- This function supports everything that `Expr.proj` supports (see `lean::type_checker::infer_proj`).
|
||||
This means we can generate projections for inductive types with one-constructor,
|
||||
even if it is an indexed family (which is not supported by the `structure` command).
|
||||
- We throw errors in the cases that `Expr.proj` is not type-correct.
|
||||
-/
|
||||
def mkProjections (n : Name) (projDecls : Array StructProjDecl) (instImplicit : Bool) : MetaM Unit :=
|
||||
withLCtx {} {} do
|
||||
let indVal ← getConstInfoInduct n
|
||||
if indVal.numCtors != 1 then
|
||||
throwError "cannot generate projections for '{.ofConstName n}', does not have exactly one constructor"
|
||||
let ctorVal ← getConstInfoCtor indVal.ctors.head!
|
||||
let isPredicate ← isPropFormerType indVal.type
|
||||
let lvls := indVal.levelParams.map mkLevelParam
|
||||
forallBoundedTelescope ctorVal.type indVal.numParams fun params ctorType => do
|
||||
if params.size != indVal.numParams then
|
||||
throwError "projection generation failed, '{.ofConstName n}' is an ill-formed inductive datatype"
|
||||
let selfType := mkAppN (.const n lvls) params
|
||||
let selfBI : BinderInfo := if instImplicit then .instImplicit else .default
|
||||
withLocalDecl `self selfBI selfType fun self => do
|
||||
let projArgs := params.push self
|
||||
-- Make modifications to parameter binder infos that apply to all projections
|
||||
let mut lctx ← getLCtx
|
||||
for param in params do
|
||||
let fvarId := param.fvarId!
|
||||
let decl ← fvarId.getDecl
|
||||
if !decl.binderInfo.isInstImplicit && !decl.type.isOutParam then
|
||||
/- We reset the implicit binder to have it be inferred by `Expr.inferImplicit`.
|
||||
However, outparams must be implicit. -/
|
||||
lctx := lctx.setBinderInfo fvarId .default
|
||||
else if decl.binderInfo.isInstImplicit && instImplicit then
|
||||
lctx := lctx.setBinderInfo fvarId .implicit
|
||||
-- Construct the projection functions:
|
||||
let mut ctorType := ctorType
|
||||
for h : i in [0:projDecls.size] do
|
||||
let {ref, projName} := projDecls[i]
|
||||
unless ctorType.isForall do
|
||||
throwErrorAt ref "\
|
||||
failed to generate projection '{projName}' for '{.ofConstName n}', \
|
||||
not enough constructor fields"
|
||||
let resultType := ctorType.bindingDomain!.consumeTypeAnnotations
|
||||
let isProp ← isProp resultType
|
||||
if isPredicate && !isProp then
|
||||
throwErrorAt ref "\
|
||||
failed to generate projection '{projName}' for the 'Prop'-valued type '{.ofConstName n}', \
|
||||
field must be a proof, but it has type\
|
||||
{indentExpr resultType}"
|
||||
let projType := lctx.mkForall projArgs resultType
|
||||
let projType := projType.inferImplicit indVal.numParams (considerRange := true)
|
||||
let projVal := lctx.mkLambda projArgs <| Expr.proj n i self
|
||||
let cval : ConstantVal := { name := projName, levelParams := indVal.levelParams, type := projType }
|
||||
withRef ref do
|
||||
if isProp then
|
||||
let env ← getEnv
|
||||
addDecl <|
|
||||
if env.hasUnsafe projType || env.hasUnsafe projVal then
|
||||
-- Theorems cannot be unsafe, using opaque instead.
|
||||
Declaration.opaqueDecl { cval with value := projVal, isUnsafe := true }
|
||||
else
|
||||
Declaration.thmDecl { cval with value := projVal }
|
||||
else
|
||||
let decl ← mkDefinitionValInferrringUnsafe projName indVal.levelParams projType projVal ReducibilityHints.abbrev
|
||||
-- Projections have special compiler support. No need to compile.
|
||||
addDecl <| Declaration.defnDecl decl
|
||||
-- Recall: we want instance projections to be in "reducible canonical form"
|
||||
if !instImplicit then
|
||||
setReducibleAttribute projName
|
||||
modifyEnv fun env => addProjectionFnInfo env projName ctorVal.name indVal.numParams i instImplicit
|
||||
let proj := mkApp (mkAppN (.const projName lvls) params) self
|
||||
ctorType := ctorType.bindingBody!.instantiate1 proj
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -120,6 +120,8 @@ where
|
||||
else
|
||||
collect (b.instantiate1 (← mkFreshExprMVar d)) (argIdx+1) targetIdx implicits targets'
|
||||
| _ =>
|
||||
unless targetIdx = targets.size do
|
||||
throwError "extra targets for '{elimInfo.elimExpr}'"
|
||||
return (implicits, targets')
|
||||
|
||||
structure CustomEliminator where
|
||||
|
||||
@@ -7,15 +7,13 @@ prelude
|
||||
import Lean.Meta.Tactic.Util
|
||||
|
||||
namespace Lean.Meta
|
||||
/--
|
||||
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
|
||||
If no local declarations require renaming, the original goal is returned unchanged.
|
||||
-/
|
||||
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
mvarId.checkNotAssigned `expose_names
|
||||
|
||||
/-- Returns a copy of the local context in which all declarations have clear, accessible names. -/
|
||||
private def getLCtxWithExposedNames : MetaM LocalContext := do
|
||||
let mut map : Std.HashMap Name FVarId := {}
|
||||
let mut toRename := #[]
|
||||
for localDecl in (← getLCtx) do
|
||||
let mut lctx ← getLCtx
|
||||
for localDecl in lctx do
|
||||
let userName := localDecl.userName
|
||||
if userName.hasMacroScopes then
|
||||
toRename := toRename.push localDecl.fvarId
|
||||
@@ -25,9 +23,8 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
|
||||
toRename := toRename.push fvarId
|
||||
map := map.insert userName localDecl.fvarId
|
||||
if toRename.isEmpty then
|
||||
return mvarId
|
||||
return lctx
|
||||
let mut next : Std.HashMap Name Nat := {}
|
||||
let mut lctx ← getLCtx
|
||||
-- Remark: Shadowed variables may be inserted later.
|
||||
toRename := toRename.qsort fun fvarId₁ fvarId₂ =>
|
||||
(lctx.get! fvarId₁).index < (lctx.get! fvarId₂).index
|
||||
@@ -49,8 +46,21 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
|
||||
next := next.insert baseName i
|
||||
map := map.insert userName fvarId
|
||||
lctx := lctx.modifyLocalDecl fvarId (·.setUserName userName)
|
||||
return lctx
|
||||
|
||||
/--
|
||||
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
|
||||
If no local declarations require renaming, the original goal is returned unchanged.
|
||||
-/
|
||||
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
mvarId.checkNotAssigned `expose_names
|
||||
let lctx ← getLCtxWithExposedNames
|
||||
let mvarNew ← mkFreshExprMVarAt lctx (← getLocalInstances) (← mvarId.getType) .syntheticOpaque (← mvarId.getTag)
|
||||
mvarId.assign mvarNew
|
||||
return mvarNew.mvarId!
|
||||
|
||||
/-- Creates a temporary local context where all names are exposed, and executes `k` -/
|
||||
def withExposedNames (k : MetaM α) : MetaM α := do
|
||||
withNewMCtxDepth <| withLCtx (← getLCtxWithExposedNames) (← getLocalInstances) k
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -28,6 +28,7 @@ import Lean.Meta.Tactic.Grind.Arith
|
||||
import Lean.Meta.Tactic.Grind.Ext
|
||||
import Lean.Meta.Tactic.Grind.MatchCond
|
||||
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
|
||||
import Lean.Meta.Tactic.Grind.Diseq
|
||||
|
||||
namespace Lean
|
||||
|
||||
|
||||
@@ -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,4 +46,13 @@ 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
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.search
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -59,7 +59,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
let .add a₁ x p₁ := c.p | c.throwUnexpected
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
if let some c' := (← get').dvdCnstrs[x]! then
|
||||
if let some c' := (← get').dvds[x]! then
|
||||
trace[grind.cutsat.dvd.solve] "{← c.pp}, {← c'.pp}"
|
||||
let d₂ := c'.d
|
||||
let .add a₂ _ p₂ := c'.p | c'.throwUnexpected
|
||||
@@ -76,7 +76,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
let β_d₁_p₂ := p₂.mul (β*d₁)
|
||||
let combine ← mkDvdCnstr (d₁*d₂) (.add d x (α_d₂_p₁.combine β_d₁_p₂)) (.solveCombine c c')
|
||||
trace[grind.cutsat.dvd.solve.combine] "{← combine.pp}"
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set x none}
|
||||
modify' fun s => { s with dvds := s.dvds.set x none}
|
||||
combine.assert
|
||||
let a₂_p₁ := p₁.mul a₂
|
||||
let a₁_p₂ := p₂.mul (-a₁)
|
||||
@@ -86,7 +86,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
else
|
||||
trace[grind.cutsat.dvd.update] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set x (some c) }
|
||||
modify' fun s => { s with dvds := s.dvds.set x (some c) }
|
||||
|
||||
builtin_grind_propagator propagateDvd ↓Dvd.dvd := fun e => do
|
||||
let_expr Dvd.dvd _ inst a b ← e | return ()
|
||||
|
||||
@@ -4,14 +4,18 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Diseq
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
private def _root_.Int.Linear.Poly.substVar (p : Poly) : GoalM (Option (Var × EqCnstr × Poly)) := do
|
||||
let some (a, x, c) ← p.findVarToSubst | return none
|
||||
let b := c.p.coeff x
|
||||
let p := p.mul (-b) |>.combine (c.p.mul a)
|
||||
return some (x, c, p)
|
||||
|
||||
def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
|
||||
let c ← if c.p.isSorted then
|
||||
@@ -19,6 +23,75 @@ def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
|
||||
else
|
||||
mkEqCnstr c.p.norm (.norm c)
|
||||
|
||||
def mkDiseqCnstr (p : Poly) (h : DiseqCnstrProof) : GoalM DiseqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
|
||||
def DiseqCnstr.norm (c : DiseqCnstr) : GoalM DiseqCnstr := do
|
||||
let c ← if c.p.isSorted then
|
||||
pure c
|
||||
else
|
||||
mkDiseqCnstr c.p.norm (.norm c)
|
||||
|
||||
/--
|
||||
Given an equation `c₁` containing the monomial `a*x`, and a disequality constraint `c₂`
|
||||
containing the monomial `b*x`, eliminate `x` by applying substitution.
|
||||
-/
|
||||
def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DiseqCnstr) : GoalM DiseqCnstr := do
|
||||
let p := c₁.p
|
||||
let q := c₂.p
|
||||
let p := p.mul b |>.combine (q.mul (-a))
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
mkDiseqCnstr p (.subst x c₁ c₂)
|
||||
|
||||
partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIncRecDepth do
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
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
|
||||
setInconsistent (.diseq c)
|
||||
return ()
|
||||
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
|
||||
|
||||
/--
|
||||
Selects the variable in the given linear polynomial whose coefficient has the smallest absolute value.
|
||||
-/
|
||||
@@ -39,18 +112,16 @@ where
|
||||
go k x p
|
||||
|
||||
partial def EqCnstr.applySubsts (c : EqCnstr) : GoalM EqCnstr := withIncRecDepth do
|
||||
let some (a, x, c₁) ← c.p.findVarToSubst | return c
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
let b := c₁.p.coeff x
|
||||
let p := c.p.mul (-b) |>.combine (c₁.p.mul a)
|
||||
let c ← mkEqCnstr p (.subst x c₁ c)
|
||||
applySubsts c
|
||||
|
||||
private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
let some c' := (← get').dvdCnstrs[y]! | return ()
|
||||
let some c' := (← get').dvds[y]! | return ()
|
||||
let b := c'.p.coeff x
|
||||
if b == 0 then return ()
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set y none }
|
||||
modify' fun s => { s with dvds := s.dvds.set y none }
|
||||
let c' ← c'.applyEq a x c b
|
||||
c'.assert
|
||||
|
||||
@@ -93,10 +164,31 @@ private def updateUppers (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Uni
|
||||
modify' fun s => { s with uppers := s.uppers.set y uppers' }
|
||||
updateLeCnstrs a x c todo
|
||||
|
||||
private def splitDiseqs (x : Var) (cs : PArray DiseqCnstr) : GoalM (PArray DiseqCnstr × Array (Int × DiseqCnstr)) := do
|
||||
let mut cs' := {}
|
||||
let mut todo := #[]
|
||||
for c in cs do
|
||||
let b := c.p.coeff x
|
||||
if b == 0 then
|
||||
cs' := cs'.push c
|
||||
else
|
||||
todo := todo.push (b, c)
|
||||
return (cs', todo)
|
||||
|
||||
private def updateDiseqs (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
let (diseqs', todo) ← splitDiseqs x (← get').diseqs[y]!
|
||||
modify' fun s => { s with diseqs := s.diseqs.set y diseqs' }
|
||||
for (b, c₂) in todo do
|
||||
let c₂ ← c₂.applyEq a x c b
|
||||
c₂.assert
|
||||
if (← inconsistent) then return ()
|
||||
|
||||
private def updateOccsAt (k : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
updateDvdCnstr k x c y
|
||||
updateLowers k x c y
|
||||
updateUppers k x c y
|
||||
updateDiseqs k x c y
|
||||
|
||||
private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
|
||||
let ys := (← get').occurs[x]!
|
||||
@@ -105,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
|
||||
@@ -151,14 +244,16 @@ private def exprAsPoly (a : Expr) : GoalM Poly := do
|
||||
|
||||
@[export lean_process_cutsat_eq]
|
||||
def processNewEqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {b}"
|
||||
let p₁ ← exprAsPoly a
|
||||
let p₂ ← exprAsPoly b
|
||||
let p := p₁.combine (p₂.mul (-1))
|
||||
let c ← mkEqCnstr p (.core p₁ p₂ (← mkEqProof a b))
|
||||
c.assert
|
||||
|
||||
@[export lean_process_new_cutsat_lit]
|
||||
@[export lean_process_cutsat_eq_lit]
|
||||
def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {ke}"
|
||||
let some k ← getIntValue? ke | return ()
|
||||
let p₁ ← exprAsPoly a
|
||||
let h ← mkEqProof a ke
|
||||
@@ -170,6 +265,20 @@ def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
|
||||
mkEqCnstr p (.core p₁ p₂ h)
|
||||
c.assert
|
||||
|
||||
@[export lean_process_cutsat_diseq]
|
||||
def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.diseq] "{a} ≠ {b}"
|
||||
let p₁ ← exprAsPoly a
|
||||
let some h ← mkDiseqProof? a b
|
||||
| throwError "internal `grind` error, failed to build disequality proof for{indentExpr a}\nand{indentExpr b}"
|
||||
let c ← if let some 0 ← getIntValue? b then
|
||||
mkDiseqCnstr p₁ (.expr h)
|
||||
else
|
||||
let p₂ ← exprAsPoly b
|
||||
let p := p₁.combine (p₂.mul (-1))
|
||||
mkDiseqCnstr p (.core p₁ p₂ h)
|
||||
c.assert
|
||||
|
||||
/-- Different kinds of terms internalized by this module. -/
|
||||
private inductive SupportedTermKind where
|
||||
| add | mul | num
|
||||
|
||||
@@ -59,11 +59,11 @@ def checkUppers : GoalM Unit := do
|
||||
assert! s.uppers.size == s.vars.size
|
||||
checkLeCnstrs s.uppers (isLower := false)
|
||||
|
||||
def checkDvdCnstrs : GoalM Unit := do
|
||||
def checkDvds : GoalM Unit := do
|
||||
let s ← get'
|
||||
assert! s.vars.size == s.dvdCnstrs.size
|
||||
assert! s.vars.size == s.dvds.size
|
||||
let mut x := 0
|
||||
for c? in s.dvdCnstrs do
|
||||
for c? in s.dvds do
|
||||
if let some c := c? then
|
||||
c.p.checkCnstrOf x
|
||||
assert! c.d > 1
|
||||
@@ -97,12 +97,23 @@ def checkElimStack : GoalM Unit := do
|
||||
for x in (← get').elimStack do
|
||||
assert! (← eliminated x)
|
||||
|
||||
def checkDiseqCnstrs : GoalM Unit := do
|
||||
let s ← get'
|
||||
assert! s.vars.size == s.diseqs.size
|
||||
let mut x := 0
|
||||
for cs in s.diseqs do
|
||||
for c in cs do
|
||||
c.p.checkCnstrOf x
|
||||
x := x + 1
|
||||
return ()
|
||||
|
||||
def checkInvariants : GoalM Unit := do
|
||||
checkVars
|
||||
checkDvdCnstrs
|
||||
checkDvds
|
||||
checkLowers
|
||||
checkUppers
|
||||
checkElimEqs
|
||||
checkElimStack
|
||||
checkDiseqCnstrs
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user