mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-13 07:34:08 +00:00
Compare commits
118 Commits
url-forwar
...
sofia/asyn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb2954d8d7 | ||
|
|
07f39d40dc | ||
|
|
7b3b323525 | ||
|
|
40a807527a | ||
|
|
5e40283a07 | ||
|
|
9837e4ccea | ||
|
|
94e5b66dfe | ||
|
|
8443600762 | ||
|
|
8b64425033 | ||
|
|
d96fd949ff | ||
|
|
d33aece210 | ||
|
|
9a7bab5f90 | ||
|
|
e2f87ed215 | ||
|
|
e42892cfb6 | ||
|
|
cc5c070328 | ||
|
|
f122454ef6 | ||
|
|
02f482129a | ||
|
|
0807f73171 | ||
|
|
27fa5b0bb5 | ||
|
|
8f9966ba74 | ||
|
|
eabd7309b7 | ||
|
|
795d13ddce | ||
|
|
2b23afdfab | ||
|
|
20b0bd0a20 | ||
|
|
979c2b4af0 | ||
|
|
b3cd5999e7 | ||
|
|
a4dcb25f69 | ||
|
|
85ce814689 | ||
|
|
9fc18b8ab4 | ||
|
|
852a3db447 | ||
|
|
d0d5d4ca39 | ||
|
|
b32f3e8930 | ||
|
|
34f5fba54d | ||
|
|
4c9601e60f | ||
|
|
42be7bb5c7 | ||
|
|
5f68c1662d | ||
|
|
2d14d51935 | ||
|
|
7cbeb14e46 | ||
|
|
cee2886154 | ||
|
|
35764213fc | ||
|
|
6b92cbdfa4 | ||
|
|
72bb7cf364 | ||
|
|
4881c3042e | ||
|
|
ec7add0b48 | ||
|
|
9b842b7554 | ||
|
|
fc718eac88 | ||
|
|
8b3c82cce2 | ||
|
|
0d1b7e6c88 | ||
|
|
d898c9ed17 | ||
|
|
c6abc3c036 | ||
|
|
d07862db2a | ||
|
|
8a79ef3633 | ||
|
|
b1c82f776b | ||
|
|
f278f31469 | ||
|
|
38b4062edb | ||
|
|
ae8dc414c3 | ||
|
|
7822ee4500 | ||
|
|
8f22c56420 | ||
|
|
0e122870be | ||
|
|
13c23877d4 | ||
|
|
7fba12f8f7 | ||
|
|
abb487a0c0 | ||
|
|
1091053824 | ||
|
|
545bd8a96c | ||
|
|
2f9618f76b | ||
|
|
fa36fcd448 | ||
|
|
9a3b4b2716 | ||
|
|
9fb5ab8450 | ||
|
|
a62c0bce77 | ||
|
|
3ce554abd7 | ||
|
|
257c347f9f | ||
|
|
ca1315e3ba | ||
|
|
197bc6cb66 | ||
|
|
02ca710872 | ||
|
|
3fbf080d72 | ||
|
|
4379002d05 | ||
|
|
5d50ec90f9 | ||
|
|
6ca699b1ff | ||
|
|
c2d56fa031 | ||
|
|
b6d590ccc3 | ||
|
|
719765ec5c | ||
|
|
11b0e7d89c | ||
|
|
37f3f0e1e2 | ||
|
|
85645958f9 | ||
|
|
a80169165e | ||
|
|
8dca311ba5 | ||
|
|
e6dfde1ad6 | ||
|
|
e532ce95ce | ||
|
|
e74b81169d | ||
|
|
cf8ffc28d3 | ||
|
|
d625aaa96f | ||
|
|
89e4f9815f | ||
|
|
9002cc8761 | ||
|
|
5a7d663624 | ||
|
|
efb398b040 | ||
|
|
4cbd1a439a | ||
|
|
20873d5d72 | ||
|
|
4c1830e5ae | ||
|
|
7b75db7c6e | ||
|
|
8d418201a6 | ||
|
|
850a4c897f | ||
|
|
186f5a6960 | ||
|
|
917715c862 | ||
|
|
50435417ac | ||
|
|
9deff2751f | ||
|
|
f3d93970dc | ||
|
|
d1577fda7a | ||
|
|
ca10fd7c4f | ||
|
|
a1cd945e82 | ||
|
|
9c372b9bc2 | ||
|
|
38214ac121 | ||
|
|
6d30aeefe5 | ||
|
|
112fa51e08 | ||
|
|
9b53e39804 | ||
|
|
ede1acfb44 | ||
|
|
0799e5c4e9 | ||
|
|
32a4c88986 | ||
|
|
4cf3c0ae67 |
@@ -37,6 +37,15 @@
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build-old",
|
||||
"type": "shell",
|
||||
"command": "make -C build/release -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4) LAKE_EXTRA_ARGS=--old",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "test",
|
||||
"type": "shell",
|
||||
|
||||
@@ -52,6 +52,7 @@ def sort_sections_order():
|
||||
return [
|
||||
"Language",
|
||||
"Library",
|
||||
"Tactics",
|
||||
"Compiler",
|
||||
"Pretty Printing",
|
||||
"Documentation",
|
||||
|
||||
@@ -112,3 +112,11 @@ repositories:
|
||||
branch: master
|
||||
dependencies:
|
||||
- mathlib4
|
||||
|
||||
- name: lean-fro.org
|
||||
url: https://github.com/leanprover/lean-fro.org
|
||||
toolchain-tag: false
|
||||
stable-branch: false
|
||||
branch: master
|
||||
dependencies:
|
||||
- verso
|
||||
|
||||
@@ -377,12 +377,28 @@ def execute_release_steps(repo, version, config):
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(red("Tests failed, but continuing with PR creation..."))
|
||||
print(red(f"Test error: {e}"))
|
||||
elif repo_name == "lean-fro.org":
|
||||
# Update lean-toolchain in examples/hero
|
||||
print(blue("Updating examples/hero/lean-toolchain..."))
|
||||
docs_toolchain = repo_path / "examples" / "hero" / "lean-toolchain"
|
||||
with open(docs_toolchain, "w") as f:
|
||||
f.write(f"leanprover/lean4:{version}\n")
|
||||
print(green(f"Updated examples/hero/lean-toolchain to leanprover/lean4:{version}"))
|
||||
|
||||
print(blue("Running `lake update`..."))
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
print(blue("Running `lake update` in examples/hero..."))
|
||||
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
|
||||
elif repo_name == "cslib":
|
||||
print(blue("Updating lakefile.toml..."))
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
|
||||
|
||||
print(blue("Updating docs/lakefile.toml..."))
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path / "docs")
|
||||
|
||||
# Update lean-toolchain in docs
|
||||
print(blue("Updating docs/lean-toolchain..."))
|
||||
docs_toolchain = "docs" / "lean-toolchain"
|
||||
docs_toolchain = repo_path / "docs" / "lean-toolchain"
|
||||
with open(docs_toolchain, "w") as f:
|
||||
f.write(f"leanprover/lean4:{version}\n")
|
||||
print(green(f"Updated docs/lean-toolchain to leanprover/lean4:{version}"))
|
||||
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 24)
|
||||
set(LEAN_VERSION_MINOR 25)
|
||||
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'")
|
||||
|
||||
@@ -42,5 +42,8 @@ public import Init.While
|
||||
public import Init.Syntax
|
||||
public import Init.Internal
|
||||
public import Init.Try
|
||||
public meta import Init.Try -- make sure `Try.Config` can be evaluated anywhere
|
||||
public import Init.BinderNameHint
|
||||
public import Init.Task
|
||||
public import Init.MethodSpecsSimp
|
||||
public import Init.LawfulBEqTactics
|
||||
|
||||
@@ -147,7 +147,7 @@ class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplica
|
||||
|
||||
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
|
||||
attribute [simp] pure_bind bind_assoc bind_pure_comp
|
||||
attribute [grind] pure_bind
|
||||
attribute [grind <=] pure_bind
|
||||
|
||||
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
|
||||
change x >>= (fun a => pure (id a)) = x
|
||||
|
||||
@@ -1580,6 +1580,7 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
|
||||
|
||||
gen_injective_theorems% Array
|
||||
gen_injective_theorems% BitVec
|
||||
gen_injective_theorems% ByteArray
|
||||
gen_injective_theorems% Char
|
||||
gen_injective_theorems% DoResultBC
|
||||
gen_injective_theorems% DoResultPR
|
||||
@@ -2546,7 +2547,3 @@ class Irrefl (r : α → α → Prop) : Prop where
|
||||
irrefl : ∀ a, ¬r a a
|
||||
|
||||
end Std
|
||||
|
||||
/-- Deprecated alias for `XorOp`. -/
|
||||
@[deprecated XorOp (since := "2025-07-30")]
|
||||
abbrev Xor := XorOp
|
||||
|
||||
@@ -176,9 +176,6 @@ theorem attach_map_val (xs : Array α) (f : α → β) :
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
|
||||
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
@@ -187,14 +184,11 @@ theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Array α} (H
|
||||
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
|
||||
cases xs; simp
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||||
(xs.attachWith p H).map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind ←]
|
||||
theorem mem_attach (xs : Array α) : ∀ x, x ∈ xs.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
@@ -401,9 +395,6 @@ theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[grind =]
|
||||
theorem attach_filterMap {xs : Array α} {f : α → Option β} :
|
||||
(xs.filterMap f).attach = xs.attach.filterMap
|
||||
|
||||
@@ -129,20 +129,11 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[deprecated Array.toArray_toList (since := "2025-02-17")]
|
||||
abbrev toArray_toList := @Array.toArray_toList
|
||||
|
||||
-- This does not need to be a simp lemma, as already after the `whnfR` the right hand side is `as`.
|
||||
theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
|
||||
|
||||
@[deprecated toList_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.toList_toArray := @List.toList_toArray
|
||||
|
||||
@[simp, grind =] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
|
||||
|
||||
@[deprecated size_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.size_toArray := @List.size_toArray
|
||||
|
||||
@[simp, grind =] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
|
||||
xs.toArray[i] = xs[i]'(by simpa using h) := rfl
|
||||
|
||||
@@ -412,10 +403,6 @@ that requires a proof the array is non-empty.
|
||||
def back? (xs : Array α) : Option α :=
|
||||
xs[xs.size - 1]?
|
||||
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), expose]
|
||||
def get? (xs : Array α) (i : Nat) : Option α :=
|
||||
if h : i < xs.size then some xs[i] else none
|
||||
|
||||
/--
|
||||
Swaps a new element with the element at the given index.
|
||||
|
||||
@@ -1812,7 +1799,6 @@ Examples:
|
||||
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 3 = #["apple", "pear", "orange"]`
|
||||
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 5 = #["apple", "pear", "orange"]`
|
||||
-/
|
||||
@[grind]
|
||||
def eraseIdxIfInBounds (xs : Array α) (i : Nat) : Array α :=
|
||||
if h : i < xs.size then xs.eraseIdx i h else xs
|
||||
|
||||
|
||||
@@ -24,29 +24,6 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]` instead.
|
||||
|
||||
Access an element from an array without needing a runtime bounds checks,
|
||||
using a `Nat` index and a proof that it is in bounds.
|
||||
|
||||
This function does not use `get_elem_tactic` to automatically find the proof that
|
||||
the index is in bounds. This is because the tactic itself needs to look up values in
|
||||
arrays.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]` instead" (since := "2025-02-17")]
|
||||
def get {α : Type u} (xs : @& Array α) (i : @& Nat) (h : LT.lt i xs.size) : α :=
|
||||
xs.toList.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]!` instead.
|
||||
|
||||
Access an element from an array, or panic if the index is out of bounds.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17"), expose]
|
||||
def get! {α : Type u} [Inhabited α] (xs : @& Array α) (i : @& Nat) : α :=
|
||||
Array.getD xs i default
|
||||
|
||||
theorem foldlM_toList.aux [Monad m]
|
||||
{f : β → α → m β} {xs : Array α} {i j} (H : xs.size ≤ i + j) {b} :
|
||||
foldlM.loop f xs xs.size (Nat.le_refl _) i j b = (xs.toList.drop j).foldlM f b := by
|
||||
@@ -108,9 +85,6 @@ abbrev push_toList := @toList_push
|
||||
|
||||
@[simp, grind =] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
|
||||
|
||||
@[deprecated toList_pop (since := "2025-02-17")]
|
||||
abbrev pop_toList := @Array.toList_pop
|
||||
|
||||
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
|
||||
|
||||
@[simp, grind =] theorem toList_append {xs ys : Array α} :
|
||||
|
||||
@@ -324,6 +324,13 @@ abbrev erase_mkArray_ne := @erase_replicate_ne
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdxIfInBounds -/
|
||||
|
||||
@[grind =]
|
||||
theorem eraseIdxIfInBounds_eq {xs : Array α} {i : Nat} :
|
||||
xs.eraseIdxIfInBounds i = if h : i < xs.size then xs.eraseIdx i else xs := by
|
||||
simp [eraseIdxIfInBounds]
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
|
||||
@@ -278,9 +278,6 @@ theorem find?_flatten_eq_none_iff {xss : Array (Array α)} {p : α → Bool} :
|
||||
xss.flatten.find? p = none ↔ ∀ ys ∈ xss, ∀ x ∈ ys, !p x := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
|
||||
|
||||
/--
|
||||
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
|
||||
some array in `xs` contains `a`, and no earlier element of that array satisfies `p`.
|
||||
@@ -306,9 +303,6 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
|
||||
⟨zs.toList, bs.toList.map Array.toList, by simpa using h⟩,
|
||||
by simpa using h₁, by simpa using h₂⟩
|
||||
|
||||
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
|
||||
|
||||
@[simp, grind =] theorem find?_flatMap {xs : Array α} {f : α → Array β} {p : β → Bool} :
|
||||
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
|
||||
cases xs
|
||||
@@ -318,17 +312,11 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
|
||||
(xs.flatMap f).find? p = none ↔ ∀ x ∈ xs, ∀ y ∈ f x, !p y := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
|
||||
|
||||
@[grind =]
|
||||
theorem find?_replicate :
|
||||
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate]
|
||||
|
||||
@[deprecated find?_replicate (since := "2025-03-18")]
|
||||
abbrev find?_mkArray := @find?_replicate
|
||||
|
||||
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
|
||||
find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
@@ -346,34 +334,19 @@ abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
|
||||
|
||||
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
|
||||
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [← List.toArray_replicate, Classical.or_iff_not_imp_left]
|
||||
|
||||
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
|
||||
|
||||
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α → Bool} (h) :
|
||||
((replicate n a).find? p).get h = a := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated get_find?_replicate (since := "2025-03-18")]
|
||||
abbrev get_find?_mkArray := @get_find?_replicate
|
||||
|
||||
@[grind =]
|
||||
theorem find?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) {p : β → Bool} :
|
||||
|
||||
@@ -80,9 +80,6 @@ theorem ne_empty_of_size_pos (h : 0 < xs.size) : xs ≠ #[] := by
|
||||
@[simp] theorem size_eq_zero_iff : xs.size = 0 ↔ xs = #[] :=
|
||||
⟨eq_empty_of_size_eq_zero, fun h => h ▸ rfl⟩
|
||||
|
||||
@[deprecated size_eq_zero_iff (since := "2025-02-24")]
|
||||
abbrev size_eq_zero := @size_eq_zero_iff
|
||||
|
||||
theorem eq_empty_iff_size_eq_zero : xs = #[] ↔ xs.size = 0 :=
|
||||
size_eq_zero_iff.symm
|
||||
|
||||
@@ -107,17 +104,10 @@ theorem exists_mem_of_size_eq_add_one {xs : Array α} (h : xs.size = n + 1) :
|
||||
theorem size_pos_iff {xs : Array α} : 0 < xs.size ↔ xs ≠ #[] :=
|
||||
Nat.pos_iff_ne_zero.trans (not_congr size_eq_zero_iff)
|
||||
|
||||
@[deprecated size_pos_iff (since := "2025-02-24")]
|
||||
abbrev size_pos := @size_pos_iff
|
||||
|
||||
theorem size_eq_one_iff {xs : Array α} : xs.size = 1 ↔ ∃ a, xs = #[a] := by
|
||||
cases xs
|
||||
simpa using List.length_eq_one_iff
|
||||
|
||||
@[deprecated size_eq_one_iff (since := "2025-02-24")]
|
||||
abbrev size_eq_one := @size_eq_one_iff
|
||||
|
||||
|
||||
/-! ## L[i] and L[i]? -/
|
||||
|
||||
theorem getElem?_eq_none_iff {xs : Array α} : xs[i]? = none ↔ xs.size ≤ i := by
|
||||
@@ -371,6 +361,7 @@ abbrev getElem?_mkArray := @getElem?_replicate
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
@[grind ←]
|
||||
theorem not_mem_empty (a : α) : ¬ a ∈ #[] := by simp
|
||||
|
||||
@[simp, grind =] theorem mem_push {xs : Array α} {x y : α} : x ∈ xs.push y ↔ x ∈ xs ∨ x = y := by
|
||||
@@ -542,18 +533,12 @@ theorem isEmpty_eq_false_iff_exists_mem {xs : Array α} :
|
||||
@[simp] theorem isEmpty_iff {xs : Array α} : xs.isEmpty ↔ xs = #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[deprecated isEmpty_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_true := @isEmpty_iff
|
||||
|
||||
@[grind →]
|
||||
theorem empty_of_isEmpty {xs : Array α} (h : xs.isEmpty) : xs = #[] := Array.isEmpty_iff.mp h
|
||||
|
||||
@[simp] theorem isEmpty_eq_false_iff {xs : Array α} : xs.isEmpty = false ↔ xs ≠ #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[deprecated isEmpty_eq_false_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_false := @isEmpty_eq_false_iff
|
||||
|
||||
theorem isEmpty_iff_size_eq_zero {xs : Array α} : xs.isEmpty ↔ xs.size = 0 := by
|
||||
rw [isEmpty_iff, size_eq_zero_iff]
|
||||
|
||||
@@ -1474,7 +1459,7 @@ theorem forall_mem_filter {p : α → Bool} {xs : Array α} {P : α → Prop} :
|
||||
(∀ (i) (_ : i ∈ xs.filter p), P i) ↔ ∀ (j) (_ : j ∈ xs), p j → P j := by
|
||||
simp
|
||||
|
||||
@[grind] theorem getElem_filter {xs : Array α} {p : α → Bool} {i : Nat} (h : i < (xs.filter p).size) :
|
||||
@[grind ←] theorem getElem_filter {xs : Array α} {p : α → Bool} {i : Nat} (h : i < (xs.filter p).size) :
|
||||
p (xs.filter p)[i] :=
|
||||
(mem_filter.mp (getElem_mem h)).2
|
||||
|
||||
@@ -2996,11 +2981,6 @@ theorem _root_.List.toArray_drop {l : List α} {k : Nat} :
|
||||
(l.drop k).toArray = l.toArray.extract k := by
|
||||
rw [List.drop_eq_extract, List.extract_toArray, List.size_toArray]
|
||||
|
||||
@[deprecated extract_size (since := "2025-02-27")]
|
||||
theorem take_size {xs : Array α} : xs.take xs.size = xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### shrink -/
|
||||
|
||||
@[simp] private theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
|
||||
@@ -3251,7 +3231,7 @@ rather than `(arr.push a).size` as the argument.
|
||||
simp [← foldrM_push, h]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem _root_.List.foldrM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
@[simp, grind! ←] theorem _root_.List.foldrM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
l.foldrM (fun x xs => xs.push <$> f x) xs = do return xs ++ (← l.reverse.mapM f).toArray := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
@@ -3264,7 +3244,7 @@ rather than `(arr.push a).size` as the argument.
|
||||
simp
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem _root_.List.foldlM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
@[simp, grind! ←] theorem _root_.List.foldlM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
l.foldlM (fun xs x => xs.push <$> f x) xs = do return xs ++ (← l.mapM f).toArray := by
|
||||
induction l generalizing xs <;> simp [*]
|
||||
|
||||
@@ -3338,7 +3318,7 @@ rather than `(arr.push a).size` as the argument.
|
||||
foldrM_push' h
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldl_push_eq_append {as : Array α} {bs : Array β} {f : α → β} (w : stop = as.size) :
|
||||
@[simp, grind! ←] theorem foldl_push_eq_append {as : Array α} {bs : Array β} {f : α → β} (w : stop = as.size) :
|
||||
as.foldl (fun acc a => acc.push (f a)) bs 0 stop = bs ++ as.map f := by
|
||||
subst w
|
||||
rcases as with ⟨as⟩
|
||||
@@ -3347,14 +3327,14 @@ rather than `(arr.push a).size` as the argument.
|
||||
induction as generalizing bs <;> simp [*]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldl_cons_eq_append {as : Array α} {bs : List β} {f : α → β} (w : stop = as.size) :
|
||||
@[simp, grind! ←] theorem foldl_cons_eq_append {as : Array α} {bs : List β} {f : α → β} (w : stop = as.size) :
|
||||
as.foldl (fun acc a => (f a) :: acc) bs 0 stop = (as.map f).reverse.toList ++ bs := by
|
||||
subst w
|
||||
rcases as with ⟨as⟩
|
||||
simp
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldr_cons_eq_append {as : Array α} {bs : List β} {f : α → β} (w : start = as.size) :
|
||||
@[simp, grind! ←] theorem foldr_cons_eq_append {as : Array α} {bs : List β} {f : α → β} (w : start = as.size) :
|
||||
as.foldr (fun a acc => (f a) :: acc) bs start 0 = (as.map f).toList ++ bs := by
|
||||
subst w
|
||||
rcases as with ⟨as⟩
|
||||
@@ -3368,7 +3348,7 @@ rather than `(arr.push a).size` as the argument.
|
||||
simp
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem _root_.List.foldr_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
@[simp, grind! ←] theorem _root_.List.foldr_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
l.foldr (fun x xs => xs.push (f x)) xs = xs ++ (l.reverse.map f).toArray := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@@ -3378,7 +3358,7 @@ rather than `(arr.push a).size` as the argument.
|
||||
induction l <;> simp [*]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem _root_.List.foldl_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
@[simp, grind! ←] theorem _root_.List.foldl_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
l.foldl (fun xs x => xs.push (f x)) xs = xs ++ (l.map f).toArray := by
|
||||
induction l generalizing xs <;> simp [*]
|
||||
|
||||
@@ -3396,28 +3376,28 @@ theorem _root_.List.foldr_push {l : List α} {as : Array α} : l.foldr (fun a bs
|
||||
rw [List.foldr_eq_foldl_reverse, List.foldl_push_eq_append']
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldr_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
@[simp, grind! ←] theorem foldr_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
xs.foldr (f · ++ ·) ys = (xs.map f).flatten ++ ys := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
induction xs <;> simp_all [Function.comp_def, flatten_toArray]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldl_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
@[simp, grind! ←] theorem foldl_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
xs.foldl (· ++ f ·) ys = ys ++ (xs.map f).flatten := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
induction xs generalizing ys <;> simp_all [Function.comp_def, flatten_toArray]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldr_flip_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
@[simp, grind! ←] theorem foldr_flip_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
xs.foldr (fun x acc => acc ++ f x) ys = ys ++ (xs.map f).reverse.flatten := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
induction xs generalizing ys <;> simp_all [Function.comp_def, flatten_toArray]
|
||||
|
||||
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
|
||||
@[simp, grind] theorem foldl_flip_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
@[simp, grind! ←] theorem foldl_flip_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
xs.foldl (fun acc y => f y ++ acc) ys = (xs.map f).reverse.flatten ++ ys:= by
|
||||
rcases xs with ⟨l⟩
|
||||
rcases ys with ⟨l'⟩
|
||||
@@ -3586,8 +3566,6 @@ theorem foldr_eq_foldl_reverse {xs : Array α} {f : α → β → β} {b} :
|
||||
subst w
|
||||
rw [foldr_eq_foldl_reverse, foldl_push_eq_append rfl, map_reverse]
|
||||
|
||||
@[deprecated foldr_push_eq_append (since := "2025-02-09")] abbrev foldr_flip_push_eq_append := @foldr_push_eq_append
|
||||
|
||||
theorem foldl_assoc {op : α → α → α} [ha : Std.Associative op] {xs : Array α} {a₁ a₂} :
|
||||
xs.foldl op (op a₁ a₂) = op a₁ (xs.foldl op a₂) := by
|
||||
rcases xs with ⟨l⟩
|
||||
@@ -4712,44 +4690,3 @@ namespace List
|
||||
simp_all
|
||||
|
||||
end List
|
||||
|
||||
/-! ### Deprecations -/
|
||||
namespace Array
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
|
||||
theorem get?_eq_getElem? (xs : Array α) (i : Nat) : xs.get? i = xs[i]? := rfl
|
||||
|
||||
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem!_eq_getD (since := "2025-02-12")]
|
||||
theorem get!_eq_getD [Inhabited α] (xs : Array α) : xs.get! n = xs.getD n default := rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead of `a.get! i`." (since := "2025-02-12")]
|
||||
theorem get!_eq_getD_getElem? [Inhabited α] (xs : Array α) (i : Nat) :
|
||||
xs.get! i = xs[i]?.getD default := by
|
||||
by_cases p : i < xs.size <;>
|
||||
simp [get!, getD_eq_getD_getElem?, p]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.get? i := by
|
||||
simp [← getElem?_toList]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
@[deprecated getElem?_set_self (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
|
||||
@[deprecated getElem?_set_ne (since := "2025-02-27")] abbrev get?_set_ne := @getElem?_set_ne
|
||||
@[deprecated getElem?_set (since := "2025-02-27")] abbrev get?_set := @getElem?_set
|
||||
@[deprecated get_set (since := "2025-02-27")] abbrev get_set := @getElem_set
|
||||
@[deprecated get_set_ne (since := "2025-02-27")] abbrev get_set_ne := @getElem_set_ne
|
||||
|
||||
end Array
|
||||
|
||||
@@ -9,8 +9,8 @@ prelude
|
||||
public import Init.Core
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Range.Polymorphic.Iterators
|
||||
import Init.Data.Range.Polymorphic.Nat
|
||||
public import Init.Data.Range.Polymorphic.Iterators
|
||||
public import Init.Data.Range.Polymorphic.Nat
|
||||
import Init.Data.Iterators.Consumers
|
||||
|
||||
public section
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.GetElem
|
||||
public import Init.Data.Array.Basic
|
||||
import Init.Data.Array.GetLit
|
||||
public import Init.Data.Slice.Basic
|
||||
|
||||
|
||||
@@ -29,10 +29,6 @@ set_option linter.missingDocs true
|
||||
|
||||
namespace BitVec
|
||||
|
||||
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
|
||||
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
|
||||
BitVec.ofNatLT i p
|
||||
|
||||
section Nat
|
||||
|
||||
/--
|
||||
@@ -874,4 +870,7 @@ def clzAuxRec {w : Nat} (x : BitVec w) (n : Nat) : BitVec w :=
|
||||
/-- Count the number of leading zeros. -/
|
||||
def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
|
||||
|
||||
/-- Count the number of trailing zeros. -/
|
||||
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -74,10 +74,6 @@ theorem some_eq_getElem?_iff {l : BitVec w} : some a = l[n]? ↔ ∃ h : n < w,
|
||||
theorem getElem_of_getElem? {l : BitVec w} : l[n]? = some a → ∃ h : n < w, l[n] = a :=
|
||||
getElem?_eq_some_iff.mp
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated getElem?_eq_some_iff (since := "2025-02-17")]
|
||||
abbrev getElem?_eq_some := @getElem?_eq_some_iff
|
||||
|
||||
theorem getElem?_eq_none_iff {l : BitVec w} : l[n]? = none ↔ w ≤ n := by
|
||||
simp
|
||||
|
||||
@@ -350,25 +346,14 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
|
||||
@[simp] theorem ofBool_xor_ofBool : ofBool b ^^^ ofBool b' = ofBool (b ^^ b') := by
|
||||
cases b <;> cases b' <;> rfl
|
||||
|
||||
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
|
||||
theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
|
||||
|
||||
@[simp, grind =] theorem getLsbD_ofNatLT {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||||
getLsbD (x#'lt) i = x.testBit i := by
|
||||
simp [getLsbD, BitVec.ofNatLT]
|
||||
|
||||
@[deprecated getLsbD_ofNatLT (since := "2025-02-13")]
|
||||
theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||||
getLsbD (x#'lt) i = x.testBit i := getLsbD_ofNatLT x lt i
|
||||
|
||||
@[simp, grind =] theorem getMsbD_ofNatLT {n x i : Nat} (h : x < 2^n) :
|
||||
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := by
|
||||
simp [getMsbD, getLsbD]
|
||||
|
||||
@[deprecated getMsbD_ofNatLT (since := "2025-02-13")]
|
||||
theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
|
||||
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := getMsbD_ofNatLT h
|
||||
|
||||
@[grind =]
|
||||
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
|
||||
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
|
||||
@@ -1100,6 +1085,10 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
|
||||
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
|
||||
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
|
||||
|
||||
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w ≤ w') : (b.setWidth w').toNat = b.toNat := by
|
||||
rw [BitVec.toNat_setWidth, Nat.mod_eq_of_lt]
|
||||
exact BitVec.toNat_lt_twoPow_of_le h
|
||||
|
||||
/-! ## extractLsb -/
|
||||
|
||||
@[simp, grind =]
|
||||
@@ -1287,6 +1276,17 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
|
||||
ext i hi
|
||||
omega
|
||||
|
||||
theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : start + len ≤ w') :
|
||||
(b.setWidth w').extractLsb' start len = b.extractLsb' start len := by
|
||||
ext i h_i
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ ≤ len₂) :
|
||||
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
|
||||
ext i hi
|
||||
simp [show i < len₂ by omega]
|
||||
|
||||
/-! ### allOnes -/
|
||||
|
||||
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||||
@@ -1530,6 +1530,12 @@ theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
|
||||
@[simp, grind =] theorem ofNat_and {x y : Nat} : BitVec.ofNat w (x &&& y) = BitVec.ofNat w x &&& BitVec.ofNat w y :=
|
||||
eq_of_toNat_eq (by simp [Nat.and_mod_two_pow])
|
||||
|
||||
theorem and_or_distrib_left {x y z : BitVec w} : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) :=
|
||||
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_left])
|
||||
|
||||
theorem and_or_distrib_right {x y z : BitVec w} : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) :=
|
||||
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_right])
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp, grind =] theorem toNat_xor (x y : BitVec v) :
|
||||
@@ -2180,6 +2186,10 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
have := lt_of_getLsbD ha
|
||||
omega
|
||||
|
||||
theorem setWidth_ushiftRight_eq_extractLsb {b : BitVec w} : (b >>> w').setWidth w'' = b.extractLsb' w' w'' := by
|
||||
ext i hi
|
||||
simp
|
||||
|
||||
/-! ### ushiftRight reductions from BitVec to Nat -/
|
||||
|
||||
@[simp, grind =]
|
||||
@@ -2970,10 +2980,9 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||||
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
|
||||
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||||
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
|
||||
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||||
x.extractLsb' start₁ (len₂ + len₁) := by
|
||||
ext i h
|
||||
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
|
||||
Nat.not_lt]
|
||||
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, ite_eq_left_iff, Nat.not_lt]
|
||||
intro hi
|
||||
congr 1
|
||||
omega
|
||||
@@ -3085,6 +3094,51 @@ theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
|
||||
simp [extractLsb'_append_eq_ite, show ¬ start < w by omega]
|
||||
|
||||
theorem extractLsb'_append_eq_left {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' w' w = a := by
|
||||
simp [BitVec.extractLsb'_append_eq_of_le]
|
||||
|
||||
theorem extractLsb'_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' 0 w' = b := by
|
||||
simp [BitVec.extractLsb'_append_eq_of_add_le]
|
||||
|
||||
theorem setWidth_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).setWidth w' = b := by
|
||||
ext i hi
|
||||
simp [getLsbD_append, hi]
|
||||
|
||||
theorem append_left_inj {s₁ s₂ : BitVec w} (t : BitVec w') : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ rfl⟩
|
||||
ext i hi
|
||||
simpa [getElem_append, dif_neg] using congrArg (·[i + w']'(by omega)) h
|
||||
|
||||
theorem append_right_inj (s : BitVec w) {t₁ t₂ : BitVec w'} : s ++ t₁ = s ++ t₂ ↔ t₁ = t₂ := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ rfl⟩
|
||||
ext i hi
|
||||
simpa [getElem_append, hi] using congrArg (·[i]) h
|
||||
|
||||
theorem setWidth_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} :
|
||||
(b ++ b').setWidth w'' = (b.setWidth w'' <<< w') ||| b'.setWidth w'' := by
|
||||
ext i hi
|
||||
simp only [getElem_setWidth, getElem_or, getElem_shiftLeft]
|
||||
rw [getLsbD_append]
|
||||
split <;> simp_all
|
||||
|
||||
theorem setWidth_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} :
|
||||
(b ++ b' ++ b'').setWidth w''' = (b.setWidth w''' <<< (w' + w'')) ||| (b'.setWidth w''' <<< w'') ||| b''.setWidth w''' := by
|
||||
rw [BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
|
||||
BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
|
||||
BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
|
||||
|
||||
theorem setWidth_append_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} {b''' : BitVec w'''} :
|
||||
(b ++ b' ++ b'' ++ b''').setWidth w'''' = (b.setWidth w'''' <<< (w' + w'' + w''')) ||| (b'.setWidth w'''' <<< (w'' + w''')) |||
|
||||
(b''.setWidth w'''' <<< w''') ||| b'''.setWidth w'''' := by
|
||||
simp only [BitVec.setWidth_append_eq_shiftLeft_setWidth_or, BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
|
||||
|
||||
theorem and_setWidth_allOnes (w' w : Nat) (b : BitVec (w' + w)) :
|
||||
b &&& (BitVec.allOnes w).setWidth (w' + w) = 0#w' ++ b.setWidth w := by
|
||||
ext i hi
|
||||
simp only [getElem_and, getElem_setWidth, getLsbD_allOnes]
|
||||
rw [BitVec.getElem_append]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
@[grind =]
|
||||
@@ -4041,6 +4095,9 @@ instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
|
||||
apply LawfulOrderLT.of_le
|
||||
simpa using fun _ _ => BitVec.lt_asymm
|
||||
|
||||
theorem length_pos_of_lt {b b' : BitVec w} (h : b < b') : 0 < w :=
|
||||
length_pos_of_ne (BitVec.ne_of_lt h)
|
||||
|
||||
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y → x % y < y := by
|
||||
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
|
||||
apply Nat.mod_lt
|
||||
@@ -4112,6 +4169,14 @@ theorem lt_of_msb_false_of_msb_true {x y : BitVec w} (hx : x.msb = false) (hy :
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem lt_add_one {b : BitVec w} (h : b ≠ allOnes w) : b < b + 1 := by
|
||||
simp only [ne_eq, ← toNat_inj, toNat_allOnes] at h
|
||||
simp only [BitVec.lt_def, ofNat_eq_ofNat, toNat_add, toNat_ofNat, Nat.add_mod_mod]
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
· exact Nat.lt_add_one _
|
||||
· have := b.toNat_lt_twoPow_of_le (Nat.le_refl _)
|
||||
omega
|
||||
|
||||
/-! ### udiv -/
|
||||
|
||||
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
|
||||
@@ -5257,7 +5322,7 @@ theorem replicate_succ' {x : BitVec w} :
|
||||
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
|
||||
simp [replicate_append_self]
|
||||
|
||||
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||||
theorem setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_setWidth]
|
||||
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
|
||||
@@ -5266,6 +5331,14 @@ theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y)
|
||||
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
|
||||
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
|
||||
|
||||
theorem setWidth_setWidth_eq_self {a : BitVec w} {w' : Nat} (h : a < BitVec.twoPow w w') : (a.setWidth w').setWidth w = a := by
|
||||
by_cases hw : w' < w
|
||||
· simp only [toNat_eq, toNat_setWidth]
|
||||
rw [Nat.mod_mod_of_dvd' (Nat.pow_dvd_pow _ (Nat.le_of_lt hw)), Nat.mod_eq_of_lt]
|
||||
rwa [BitVec.lt_def, BitVec.toNat_twoPow_of_lt hw] at h
|
||||
· rw [BitVec.lt_def, BitVec.toNat_twoPow_of_le (by omega)] at h
|
||||
simp at h
|
||||
|
||||
/-! ### intMin -/
|
||||
|
||||
@[grind =]
|
||||
@@ -5779,6 +5852,25 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
|
||||
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
|
||||
cases n <;> cases w <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem reverse_eq_zero_iff {x : BitVec w} :
|
||||
x.reverse = 0#w ↔ x = 0#w := by
|
||||
constructor
|
||||
· intro hrev
|
||||
ext i hi
|
||||
rw [← getLsbD_eq_getElem, getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
simp [hrev]
|
||||
· intro hzero
|
||||
ext i hi
|
||||
rw [← getLsbD_eq_getElem, getLsbD_eq_getMsbD, getMsbD_reverse]
|
||||
simp [hi, hzero]
|
||||
|
||||
@[simp]
|
||||
theorem reverse_reverse_eq {x : BitVec w} :
|
||||
x.reverse.reverse = x := by
|
||||
ext k hk
|
||||
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
|
||||
|
||||
/-! ### Inequalities (le / lt) -/
|
||||
|
||||
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
|
||||
@@ -5909,6 +6001,12 @@ theorem getElem_eq_true_of_lt_of_le {x : BitVec w} (hk' : k < w) (hlt: x.toNat <
|
||||
omega
|
||||
· simp [show w ≤ k + k' by omega] at hk'
|
||||
|
||||
theorem not_lt_iff {b : BitVec w} : ~~~b < b ↔ 0 < w ∧ b.msb = true := by
|
||||
refine ⟨fun h => ?_, fun ⟨hw, hb⟩ => ?_⟩
|
||||
· have := length_pos_of_lt h
|
||||
exact ⟨this, by rwa [← ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)] at h⟩
|
||||
· rwa [← ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)]
|
||||
|
||||
/-! ### Count leading zeros -/
|
||||
|
||||
theorem clzAuxRec_zero (x : BitVec w) :
|
||||
@@ -6182,16 +6280,70 @@ theorem toNat_lt_two_pow_sub_clz {x : BitVec w} :
|
||||
· simp [show w + 1 ≤ i by omega]
|
||||
· simp; omega
|
||||
|
||||
theorem clz_eq_reverse_ctz {x : BitVec w} :
|
||||
x.clz = (x.reverse).ctz := by
|
||||
simp [ctz]
|
||||
|
||||
/-! ### Deprecations -/
|
||||
/-! ### Count trailing zeros -/
|
||||
|
||||
set_option linter.missingDocs false
|
||||
|
||||
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
|
||||
abbrev toFin_uShiftRight := @toFin_ushiftRight
|
||||
theorem ctz_eq_reverse_clz {x : BitVec w} :
|
||||
x.ctz = (x.reverse).clz := by
|
||||
simp [ctz]
|
||||
|
||||
/-- The number of trailing zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
|
||||
@[simp]
|
||||
theorem ctz_lt_iff_ne_zero {x : BitVec w} :
|
||||
ctz x < w ↔ x ≠ 0#w := by
|
||||
simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq]
|
||||
rw [show BitVec.ofNat w w = w by simp, ← reverse_eq_zero_iff (x := x)]
|
||||
apply clz_lt_iff_ne_zero (x := x.reverse)
|
||||
|
||||
/-- If a bitvec is different than zero the bits at indexes lower than `ctz x` are false. -/
|
||||
theorem getLsbD_false_of_lt_ctz {x : BitVec w} (hi : i < x.ctz.toNat) :
|
||||
x.getLsbD i = false := by
|
||||
rw [getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
have hiff := ctz_lt_iff_ne_zero (x := x)
|
||||
by_cases hzero : x = 0#w
|
||||
· simp [hzero, getLsbD_reverse]
|
||||
· simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq, hzero, not_false_eq_true,
|
||||
iff_true] at hiff
|
||||
simp only [ctz] at hi
|
||||
have hi' : i < w := by simp [BitVec.lt_def] at hiff; omega
|
||||
simp only [hi', decide_true, Bool.true_and]
|
||||
have : (x.reverse.clzAuxRec (w - 1)).toNat ≤ w := by
|
||||
rw [show ((x.reverse.clzAuxRec (w - 1)).toNat ≤ w) =
|
||||
((x.reverse.clzAuxRec (w - 1)).toNat ≤ (BitVec.ofNat w w).toNat) by simp, ← le_def]
|
||||
apply clzAuxRec_le (x := x.reverse) (n := w - 1)
|
||||
let j := (x.reverse.clzAuxRec (w - 1)).toNat - 1 - i
|
||||
rw [show w - 1 - i = w - (x.reverse.clzAuxRec (w - 1)).toNat + j by
|
||||
subst j
|
||||
rw [Nat.sub_sub (n := (x.reverse.clzAuxRec (w - 1)).toNat),
|
||||
← Nat.add_sub_assoc (by exact Nat.one_add_le_iff.mpr hi)]
|
||||
omega]
|
||||
have hfalse : ∀ (i : Nat), w - 1 < i → x.reverse.getLsbD i = false := by
|
||||
intros i hj
|
||||
simp [show w ≤ i by omega]
|
||||
exact getLsbD_false_of_clzAuxRec (x := x.reverse) (n := w - 1) hfalse (j := j)
|
||||
|
||||
/-- If a bitvec is different than zero, the bit at index `ctz x`, i.e., the first bit after the
|
||||
trailing zeros, is true. -/
|
||||
theorem getLsbD_true_ctz_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
|
||||
x.getLsbD (ctz x).toNat = true := by
|
||||
simp only [ctz_eq_reverse_clz, clz]
|
||||
rw [getLsbD_eq_getMsbD, ← getLsbD_reverse]
|
||||
have := ctz_lt_iff_ne_zero (x := x)
|
||||
simp only [ctz_eq_reverse_clz, clz, natCast_eq_ofNat, lt_def, toNat_ofNat, Nat.mod_two_pow_self,
|
||||
ne_eq] at this
|
||||
simp only [this, hx, not_false_eq_true, decide_true, Bool.true_and]
|
||||
have hnotrev : ¬x.reverse = 0#w := by simp [reverse_eq_zero_iff, hx]
|
||||
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x.reverse) (n := w - 1) hnotrev
|
||||
intro i hi
|
||||
simp [show w ≤ i by omega]
|
||||
|
||||
/-- A nonzero bitvector is lower-bounded by its trailing zeroes. -/
|
||||
theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
|
||||
2 ^ (ctz x).toNat ≤ x.toNat := by
|
||||
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
|
||||
exact Nat.ge_two_pow_of_testBit hclz
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -7,6 +7,8 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.ByteArray.Bootstrap
|
||||
public import Init.Data.ByteArray.Extra
|
||||
public import Init.Data.ByteArray.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
@@ -11,16 +11,11 @@ public import Init.Data.UInt.Basic
|
||||
public import Init.Data.UInt.BasicAux
|
||||
import all Init.Data.UInt.BasicAux
|
||||
public import Init.Data.Option.Basic
|
||||
public import Init.Data.Array.Extract
|
||||
|
||||
@[expose] public section
|
||||
universe u
|
||||
|
||||
structure ByteArray where
|
||||
data : Array UInt8
|
||||
|
||||
attribute [extern "lean_byte_array_mk"] ByteArray.mk
|
||||
attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
deriving instance BEq for ByteArray
|
||||
@@ -30,29 +25,15 @@ attribute [ext] ByteArray
|
||||
instance : DecidableEq ByteArray :=
|
||||
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := #[] }
|
||||
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
|
||||
instance : EmptyCollection ByteArray where
|
||||
emptyCollection := ByteArray.empty
|
||||
|
||||
@[extern "lean_byte_array_push"]
|
||||
def push : ByteArray → UInt8 → ByteArray
|
||||
| ⟨bs⟩, b => ⟨bs.push b⟩
|
||||
|
||||
@[extern "lean_byte_array_size"]
|
||||
def size : (@& ByteArray) → Nat
|
||||
| ⟨bs⟩ => bs.size
|
||||
|
||||
@[extern "lean_sarray_size", simp]
|
||||
def usize (a : @& ByteArray) : USize :=
|
||||
a.size.toUSize
|
||||
@@ -106,11 +87,31 @@ def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff le
|
||||
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
|
||||
a.copySlice b empty 0 (e - b)
|
||||
|
||||
protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
|
||||
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
|
||||
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
|
||||
b.copySlice 0 a a.size b.size false
|
||||
|
||||
instance : Append ByteArray := ⟨ByteArray.append⟩
|
||||
@[simp]
|
||||
theorem size_data {a : ByteArray} :
|
||||
a.data.size = a.size := rfl
|
||||
|
||||
@[csimp]
|
||||
theorem append_eq_fastAppend : @ByteArray.append = @ByteArray.fastAppend := by
|
||||
funext a b
|
||||
ext1
|
||||
apply Array.ext'
|
||||
simp [ByteArray.fastAppend, copySlice, ← size_data, - Array.append_assoc]
|
||||
|
||||
-- Needs to come after the `csimp` lemma
|
||||
instance : Append ByteArray where
|
||||
append := ByteArray.append
|
||||
|
||||
@[simp]
|
||||
theorem append_eq {a b : ByteArray} : a.append b = a ++ b := rfl
|
||||
|
||||
@[simp]
|
||||
theorem fastAppend_eq {a b : ByteArray} : a.fastAppend b = a ++ b := by
|
||||
simp [← append_eq_fastAppend]
|
||||
|
||||
def toList (bs : ByteArray) : List UInt8 :=
|
||||
let rec loop (i : Nat) (r : List UInt8) :=
|
||||
@@ -350,13 +351,4 @@ def prevn : Iterator → Nat → Iterator
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
| b::bs, r => loop bs (r.push b)
|
||||
loop bs ByteArray.empty
|
||||
|
||||
instance : ToString ByteArray := ⟨fun bs => bs.toList.toString⟩
|
||||
|
||||
53
src/Init/Data/ByteArray/Bootstrap.lean
Normal file
53
src/Init/Data/ByteArray/Bootstrap.lean
Normal file
@@ -0,0 +1,53 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Prelude
|
||||
public import Init.Data.List.Basic
|
||||
|
||||
public section
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
@[simp]
|
||||
theorem data_push {a : ByteArray} {b : UInt8} : (a.push b).data = a.data.push b := rfl
|
||||
|
||||
@[expose]
|
||||
protected def append (a b : ByteArray) : ByteArray :=
|
||||
⟨⟨a.data.toList ++ b.data.toList⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem toList_data_append' {a b : ByteArray} :
|
||||
(a.append b).data.toList = a.data.toList ++ b.data.toList := by
|
||||
have ⟨⟨a⟩⟩ := a
|
||||
have ⟨⟨b⟩⟩ := b
|
||||
rfl
|
||||
|
||||
theorem ext : {x y : ByteArray} → x.data = y.data → x = y
|
||||
| ⟨_⟩, ⟨_⟩, rfl => rfl
|
||||
|
||||
end ByteArray
|
||||
|
||||
@[simp]
|
||||
theorem List.toList_data_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.data.toList = l := by
|
||||
rw [List.toByteArray]
|
||||
suffices ∀ a b, (List.toByteArray.loop a b).data.toList = b.data.toList ++ a by
|
||||
simpa using this l ByteArray.empty
|
||||
intro a b
|
||||
fun_induction List.toByteArray.loop a b with simp_all [toList_push]
|
||||
where
|
||||
toList_push {xs : Array UInt8} {x : UInt8} : (xs.push x).toList = xs.toList ++ [x] := by
|
||||
have ⟨xs⟩ := xs
|
||||
simp [Array.push, List.concat_eq_append]
|
||||
|
||||
theorem List.toByteArray_append' {l l' : List UInt8} :
|
||||
(l ++ l').toByteArray = l.toByteArray.append l'.toByteArray :=
|
||||
ByteArray.ext (ext (by simp))
|
||||
where
|
||||
ext : {x y : Array UInt8} → x.toList = y.toList → x = y
|
||||
| ⟨_⟩, ⟨_⟩, rfl => rfl
|
||||
259
src/Init/Data/ByteArray/Lemmas.lean
Normal file
259
src/Init/Data/ByteArray/Lemmas.lean
Normal file
@@ -0,0 +1,259 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Markus Himmel
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.Array.Extract
|
||||
|
||||
public section
|
||||
|
||||
-- At present the preferred normal form for empty byte arrays is `ByteArray.empty`
|
||||
@[simp]
|
||||
theorem emptyc_eq_empty : (∅ : ByteArray) = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem emptyWithCapacity_eq_empty : ByteArray.emptyWithCapacity 0 = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_empty : ByteArray.empty.data = #[] := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_extract {a : ByteArray} {b e : Nat} :
|
||||
(a.extract b e).data = a.data.extract b e := by
|
||||
simp [extract, copySlice]
|
||||
by_cases b ≤ e
|
||||
· rw [(by omega : b + (e - b) = e)]
|
||||
· rw [Array.extract_eq_empty_of_le (by omega), Array.extract_eq_empty_of_le (by omega)]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
|
||||
ext1
|
||||
simp [Nat.min_le_left]
|
||||
|
||||
theorem ByteArray.fastAppend_eq_copySlice {a b : ByteArray} :
|
||||
a.fastAppend b = b.copySlice 0 a a.size b.size false := rfl
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_append {l l' : List UInt8} :
|
||||
(l ++ l').toByteArray = l.toByteArray ++ l'.toByteArray := by
|
||||
simp [List.toByteArray_append']
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.toList_data_append {l l' : ByteArray} :
|
||||
(l ++ l').data.toList = l.data.toList ++ l'.data.toList := by
|
||||
simp [← append_eq]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.data_append {l l' : ByteArray} :
|
||||
(l ++ l').data = l.data ++ l'.data := by
|
||||
simp [← Array.toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_empty : ByteArray.empty.size = 0 := by
|
||||
simp [← ByteArray.size_data]
|
||||
|
||||
@[simp]
|
||||
theorem List.data_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.data = l.toArray := by
|
||||
rw [List.toByteArray]
|
||||
suffices ∀ a b, (List.toByteArray.loop a b).data = b.data ++ a.toArray by
|
||||
simpa using this l ByteArray.empty
|
||||
intro a b
|
||||
fun_induction List.toByteArray.loop a b with simp_all
|
||||
|
||||
@[simp]
|
||||
theorem List.size_toByteArray {l : List UInt8} :
|
||||
l.toByteArray.size = l.length := by
|
||||
simp [← ByteArray.size_data]
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp, grind =]
|
||||
theorem ByteArray.size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
|
||||
simp [← size_data]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_eq_zero_iff {a : ByteArray} : a.size = 0 ↔ a = ByteArray.empty := by
|
||||
refine ⟨fun h => ?_, fun h => h ▸ ByteArray.size_empty⟩
|
||||
ext1
|
||||
simp [← Array.size_eq_zero_iff, h]
|
||||
|
||||
theorem ByteArray.getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
|
||||
a[i] = a.data[i]'(by simpa [← size_data]) := rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
|
||||
(hlt : i < a.size) : (a ++ b)[i] = a[i] := by
|
||||
simp only [getElem_eq_getElem_data, data_append]
|
||||
rw [Array.getElem_append_left (by simpa)]
|
||||
|
||||
theorem ByteArray.getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
|
||||
(hle : a.size ≤ i) : (a ++ b)[i] = b[i - a.size]'(by simp_all; omega) := by
|
||||
simp only [getElem_eq_getElem_data, data_append]
|
||||
rw [Array.getElem_append_right (by simpa)]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
|
||||
l.toByteArray[i]'h = l[i]'(by simp_all) := by
|
||||
simp [ByteArray.getElem_eq_getElem_data]
|
||||
|
||||
theorem List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
|
||||
l[i]'h = l.toByteArray[i]'(by simp_all) := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.size_extract {a : ByteArray} {b e : Nat} :
|
||||
(a.extract b e).size = min e a.size - b := by
|
||||
simp [← size_data]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty ↔ min j b.size ≤ i := by
|
||||
rw [← size_eq_zero_iff, size_extract]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
|
||||
simp only [extract_eq_empty_iff]
|
||||
exact Nat.le_trans (Nat.min_le_left _ _) (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.append_eq_empty_iff {a b : ByteArray} :
|
||||
a ++ b = ByteArray.empty ↔ a = ByteArray.empty ∧ b = ByteArray.empty := by
|
||||
simp [← size_eq_zero_iff, size_append]
|
||||
|
||||
@[simp]
|
||||
theorem List.toByteArray_eq_empty {l : List UInt8} :
|
||||
l.toByteArray = ByteArray.empty ↔ l = [] := by
|
||||
simp [← ByteArray.size_eq_zero_iff]
|
||||
|
||||
theorem ByteArray.append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
|
||||
xs ++ ys₁ = xs ++ ys₂ ↔ ys₁ = ys₂ := by
|
||||
simp [ByteArray.ext_iff, Array.append_right_inj]
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.extract_append_extract {a : ByteArray} {i j k : Nat} :
|
||||
a.extract i j ++ a.extract j k = a.extract (min i j) (max j k) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
|
||||
(hi : i ≤ j) (hk : j ≤ k) :
|
||||
a.extract i k = a.extract i j ++ a.extract j k := by
|
||||
simp
|
||||
rw [Nat.min_eq_left hi, Nat.max_eq_right hk]
|
||||
|
||||
theorem ByteArray.append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
|
||||
simp only [ByteArray.ext_iff, ← ByteArray.size_data, ByteArray.data_append] at *
|
||||
exact Array.append_inj_left h hl
|
||||
|
||||
theorem ByteArray.extract_append_eq_right {a b : ByteArray} {i : Nat} (hi : i = a.size) :
|
||||
(a ++ b).extract i (a ++ b).size = b := by
|
||||
subst hi
|
||||
ext1
|
||||
simp [← size_data]
|
||||
|
||||
theorem ByteArray.extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
|
||||
(a ++ b).extract 0 i = a := by
|
||||
subst hi
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_left {a b : ByteArray} {i : Nat} :
|
||||
(a ++ b).extract i a.size = a.extract i a.size := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_add {a b : ByteArray} {i j : Nat} :
|
||||
(a ++ b).extract (a.size + i) (a.size + j) = b.extract i j := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append {as bs : ByteArray} {i j : Nat} :
|
||||
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
|
||||
(a ++ b).extract (k + i) (k + j) = b.extract i j := by
|
||||
cases h
|
||||
rw [extract_append_size_add]
|
||||
|
||||
theorem ByteArray.extract_extract {a : ByteArray} {i j k l : Nat} :
|
||||
(a.extract i j).extract k l = a.extract (i + k) (min (i + l) j) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
theorem ByteArray.getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
|
||||
start + i < xs.size := by
|
||||
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
|
||||
apply Nat.sub_le_sub_right; apply Nat.min_le_right
|
||||
|
||||
theorem ByteArray.getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
|
||||
(h) : (b.extract start stop)[i]'h = b[start + i]'(getElem_extract_aux h) := by
|
||||
simp [getElem_eq_getElem_data]
|
||||
|
||||
theorem ByteArray.extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
|
||||
a.extract i j = a.extract i' j ↔ min j a.size - i = min j a.size - i' := by
|
||||
simp [ByteArray.ext_iff, Array.extract_eq_extract_left]
|
||||
|
||||
theorem ByteArray.extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 ≤ a.size) :
|
||||
a.extract i (i + 1) = [a[i]].toByteArray := by
|
||||
ext
|
||||
· simp
|
||||
omega
|
||||
· rename_i j hj hj'
|
||||
obtain rfl : j = 0 := by simpa using hj'
|
||||
simp [ByteArray.getElem_eq_getElem_data]
|
||||
|
||||
theorem ByteArray.extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 ≤ a.size) :
|
||||
a.extract i (i + 2) = [a[i], a[i + 1]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_one (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 ≤ a.size) :
|
||||
a.extract i (i + 3) = [a[i], a[i + 1], a[i + 2]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_two (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 ≤ a.size) :
|
||||
a.extract i (i + 4) = [a[i], a[i + 1], a[i + 2], a[i + 3]].toByteArray := by
|
||||
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
|
||||
extract_add_one (by omega), extract_add_three (by omega)]
|
||||
simp [← List.toByteArray_append]
|
||||
|
||||
theorem ByteArray.append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
|
||||
ext1
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.toList_empty : ByteArray.empty.toList = [] := by
|
||||
simp [ByteArray.toList, ByteArray.toList.loop]
|
||||
|
||||
theorem ByteArray.copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
|
||||
ByteArray.copySlice src srcOff dest destOff len exact =
|
||||
dest.extract 0 destOff ++ src.extract srcOff (srcOff +len) ++ dest.extract (destOff + min len (src.data.size - srcOff)) dest.data.size := by
|
||||
ext1
|
||||
simp [copySlice]
|
||||
@@ -3,15 +3,11 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Core
|
||||
public import Init.Grind.Tactics
|
||||
|
||||
public section
|
||||
|
||||
namespace Function
|
||||
|
||||
/--
|
||||
@@ -51,6 +47,7 @@ theorem curry_apply {α β γ} (f : α × β → γ) (x : α) (y : β) : curry f
|
||||
rfl
|
||||
|
||||
/-- A function `f : α → β` is called injective if `f x = f y` implies `x = y`. -/
|
||||
@[expose]
|
||||
def Injective (f : α → β) : Prop :=
|
||||
∀ ⦃a₁ a₂⦄, f a₁ = f a₂ → a₁ = a₂
|
||||
|
||||
@@ -59,6 +56,7 @@ theorem Injective.comp {α β γ} {g : β → γ} {f : α → β} (hg : Injectiv
|
||||
|
||||
/-- A function `f : α → β` is called surjective if every `b : β` is equal to `f a`
|
||||
for some `a : α`. -/
|
||||
@[expose]
|
||||
def Surjective (f : α → β) : Prop :=
|
||||
∀ b, Exists fun a => f a = b
|
||||
|
||||
@@ -69,20 +67,22 @@ theorem Surjective.comp {α β γ} {g : β → γ} {f : α → β} (hg : Surject
|
||||
Exists.intro a (show g (f a) = c from Eq.trans (congrArg g ha) hb)
|
||||
|
||||
/-- `LeftInverse g f` means that `g` is a left inverse to `f`. That is, `g ∘ f = id`. -/
|
||||
@[grind]
|
||||
@[expose, grind]
|
||||
def LeftInverse {α β} (g : β → α) (f : α → β) : Prop :=
|
||||
∀ x, g (f x) = x
|
||||
|
||||
/-- `HasLeftInverse f` means that `f` has an unspecified left inverse. -/
|
||||
@[expose]
|
||||
def HasLeftInverse {α β} (f : α → β) : Prop :=
|
||||
Exists fun finv : β → α => LeftInverse finv f
|
||||
|
||||
/-- `RightInverse g f` means that `g` is a right inverse to `f`. That is, `f ∘ g = id`. -/
|
||||
@[grind]
|
||||
@[expose, grind]
|
||||
def RightInverse {α β} (g : β → α) (f : α → β) : Prop :=
|
||||
LeftInverse f g
|
||||
|
||||
/-- `HasRightInverse f` means that `f` has an unspecified right inverse. -/
|
||||
@[expose]
|
||||
def HasRightInverse {α β} (f : α → β) : Prop :=
|
||||
Exists fun finv : β → α => RightInverse finv f
|
||||
|
||||
|
||||
@@ -206,9 +206,6 @@ theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a
|
||||
| Int.ofNat (b+1), _ =>
|
||||
rcases a with ⟨a⟩ <;> simp [Int.ediv, -natCast_ediv]
|
||||
|
||||
@[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
|
||||
|
||||
@@ -17,6 +17,7 @@ import all Init.Data.Int.Gcd
|
||||
public import Init.Data.RArray
|
||||
public import Init.Data.AC
|
||||
import all Init.Data.AC
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
public section
|
||||
|
||||
@@ -54,7 +55,7 @@ def Expr.denote (ctx : Context) : Expr → Int
|
||||
inductive Poly where
|
||||
| num (k : Int)
|
||||
| add (k : Int) (v : Var) (p : Poly)
|
||||
deriving @[expose] BEq
|
||||
deriving @[expose] BEq, ReflBEq, LawfulBEq
|
||||
|
||||
@[expose]
|
||||
protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
|
||||
@@ -525,18 +526,6 @@ theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.deno
|
||||
simp [norm, toPoly', Expr.denote_toPoly'_go]
|
||||
|
||||
attribute [local simp] Expr.denote_norm
|
||||
|
||||
instance : LawfulBEq Poly where
|
||||
eq_of_beq {a} := by
|
||||
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
|
||||
next ih =>
|
||||
intro _ _ h
|
||||
exact ih h
|
||||
rfl := by
|
||||
intro a
|
||||
induction a <;> simp! [BEq.beq]
|
||||
assumption
|
||||
|
||||
attribute [local simp] Poly.denote'_eq_denote
|
||||
|
||||
theorem Expr.eq_of_norm_eq (ctx : Context) (e : Expr) (p : Poly) (h : e.norm.beq' p) : e.denote ctx = p.denote' ctx := by
|
||||
|
||||
@@ -1347,8 +1347,6 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
|
||||
| 0 => Int.mul_zero _
|
||||
| -[_+1] => Int.mul_neg_one _
|
||||
|
||||
@[deprecated mul_sign_self (since := "2025-02-24")] abbrev mul_sign := @mul_sign_self
|
||||
|
||||
@[simp] theorem sign_mul_self (i : Int) : sign i * i = natAbs i := by
|
||||
rw [Int.mul_comm, mul_sign_self]
|
||||
|
||||
|
||||
@@ -50,14 +50,9 @@ protected theorem pow_ne_zero {n : Int} {m : Nat} : n ≠ 0 → n ^ m ≠ 0 := b
|
||||
|
||||
instance {n : Int} {m : Nat} [NeZero n] : NeZero (n ^ m) := ⟨Int.pow_ne_zero (NeZero.ne _)⟩
|
||||
|
||||
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left
|
||||
|
||||
@[deprecated Nat.pow_le_pow_right (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
|
||||
|
||||
-- This can't be removed until the next update-stage0
|
||||
@[deprecated Nat.pow_pos (since := "2025-02-17")]
|
||||
abbrev pos_pow_of_pos := @Nat.pow_pos
|
||||
abbrev _root_.Nat.pos_pow_of_pos := @Nat.pow_pos
|
||||
|
||||
@[simp, norm_cast]
|
||||
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
|
||||
@@ -19,6 +19,7 @@ universe v u v' u'
|
||||
section ULiftT
|
||||
|
||||
/-- `ULiftT.{v, u}` shrinks a monad on `Type max u v` to a monad on `Type u`. -/
|
||||
@[expose] -- for codegen
|
||||
def ULiftT (n : Type max u v → Type v') (α : Type u) := n (ULift.{v} α)
|
||||
|
||||
/-- Returns the underlying `n`-monadic representation of a `ULiftT n α` value. -/
|
||||
|
||||
@@ -149,9 +149,6 @@ theorem attach_map_val {l : List α} {f : α → β} :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_eq_map _
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
-- The argument `l : List α` is explicit to allow rewriting from right to left.
|
||||
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
|
||||
attach_map_val.trans (List.map_id _)
|
||||
@@ -160,14 +157,11 @@ theorem attachWith_map_val {p : α → Prop} {f : α → β} {l : List α} (H :
|
||||
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
|
||||
rw [attachWith, map_pmap]; exact pmap_eq_map _
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} {l : List α} (H : ∀ a ∈ l, p a) :
|
||||
(l.attachWith p H).map Subtype.val = l :=
|
||||
(attachWith_map_val _).trans (List.map_id _)
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind ←]
|
||||
theorem mem_attach (l : List α) : ∀ x, x ∈ l.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val]; exact h)
|
||||
@@ -254,13 +248,6 @@ theorem getElem?_pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} (h
|
||||
· simp
|
||||
· simp only [pmap, getElem?_cons_succ, hl]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated List.getElem?_pmap (since := "2025-02-12")]
|
||||
theorem get?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (mem_of_get? H) := by
|
||||
simp only [get?_eq_getElem?]
|
||||
simp [getElem?_pmap]
|
||||
|
||||
-- The argument `f` is explicit to allow rewriting from right to left.
|
||||
@[simp, grind =]
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) {i : Nat}
|
||||
@@ -277,15 +264,6 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[deprecated getElem_pmap (since := "2025-02-13")]
|
||||
theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).length) :
|
||||
get (pmap f l h) ⟨n, hn⟩ =
|
||||
f (get l ⟨n, @length_pmap _ _ p f l h ▸ hn⟩)
|
||||
(h _ (getElem_mem (@length_pmap _ _ p f l h ▸ hn))) := by
|
||||
simp only [get_eq_getElem]
|
||||
simp [getElem_pmap]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem getElem?_attachWith {xs : List α} {i : Nat} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
|
||||
@@ -466,9 +444,6 @@ theorem map_attach_eq_pmap {l : List α} {f : { x // x ∈ l } → β} :
|
||||
apply pmap_congr_left
|
||||
simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[grind =]
|
||||
theorem attach_filterMap {l : List α} {f : α → Option β} :
|
||||
(l.filterMap f).attach = l.attach.filterMap
|
||||
|
||||
@@ -289,16 +289,6 @@ theorem cons_lex_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false :
|
||||
@[simp] theorem lex_nil [BEq α] {as : List α} : lex as [] lt = false := by
|
||||
cases as <;> simp [nil_lex_nil, cons_lex_nil]
|
||||
|
||||
@[deprecated nil_lex_nil (since := "2025-02-10")]
|
||||
theorem lex_nil_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
|
||||
@[deprecated nil_lex_cons (since := "2025-02-10")]
|
||||
theorem lex_nil_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
|
||||
@[deprecated cons_lex_nil (since := "2025-02-10")]
|
||||
theorem lex_cons_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
|
||||
@[deprecated cons_lex_cons (since := "2025-02-10")]
|
||||
theorem lex_cons_cons [BEq α] {a b} {as bs : List α} :
|
||||
lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt)) := rfl
|
||||
|
||||
/-! ## Alternative getters -/
|
||||
|
||||
/-! ### getLast -/
|
||||
@@ -475,21 +465,6 @@ We define the basic functional programming operations on `List`:
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
/--
|
||||
Applies a function to each element of the list, returning the resulting list of values.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].map f = [f a, f b, f c]`
|
||||
* `[].map Nat.succ = []`
|
||||
* `["one", "two", "three"].map (·.length) = [3, 3, 5]`
|
||||
* `["one", "two", "three"].map (·.reverse) = ["eno", "owt", "eerht"]`
|
||||
-/
|
||||
@[specialize] def map (f : α → β) : (l : List α) → List β
|
||||
| [] => []
|
||||
| a::as => f a :: map f as
|
||||
|
||||
@[simp, grind =] theorem map_nil {f : α → β} : map f [] = [] := rfl
|
||||
@[simp, grind =] theorem map_cons {f : α → β} {a : α} {l : List α} : map f (a :: l) = f a :: map f l := rfl
|
||||
|
||||
@@ -606,20 +581,6 @@ Appends two lists. Normally used via the `++` operator.
|
||||
|
||||
Appending lists takes time proportional to the length of the first list: `O(|xs|)`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3] ++ [4, 5] = [1, 2, 3, 4, 5]`.
|
||||
* `[] ++ [4, 5] = [4, 5]`.
|
||||
* `[1, 2, 3] ++ [] = [1, 2, 3]`.
|
||||
-/
|
||||
protected def append : (xs ys : List α) → List α
|
||||
| [], bs => bs
|
||||
| a::as, bs => a :: List.append as bs
|
||||
|
||||
/--
|
||||
Appends two lists. Normally used via the `++` operator.
|
||||
|
||||
Appending lists takes time proportional to the length of the first list: `O(|xs|)`.
|
||||
|
||||
This is a tail-recursive version of `List.append`.
|
||||
|
||||
Examples:
|
||||
@@ -691,18 +652,6 @@ theorem reverseAux_eq_append {as bs : List α} : reverseAux as bs = reverseAux a
|
||||
|
||||
/-! ### flatten -/
|
||||
|
||||
/--
|
||||
Concatenates a list of lists into a single list, preserving the order of the elements.
|
||||
|
||||
`O(|flatten L|)`.
|
||||
|
||||
Examples:
|
||||
* `[["a"], ["b", "c"]].flatten = ["a", "b", "c"]`
|
||||
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flatten = ["a", "b", "c", "d", "e", "f"]`
|
||||
-/
|
||||
def flatten : List (List α) → List α
|
||||
| [] => []
|
||||
| l :: L => l ++ flatten L
|
||||
|
||||
@[simp, grind =] theorem flatten_nil : List.flatten ([] : List (List α)) = [] := rfl
|
||||
@[simp, grind =] theorem flatten_cons : (l :: L).flatten = l ++ L.flatten := rfl
|
||||
@@ -721,20 +670,14 @@ Examples:
|
||||
|
||||
/-! ### flatMap -/
|
||||
|
||||
/--
|
||||
Applies a function that returns a list to each element of a list, and concatenates the resulting
|
||||
lists.
|
||||
|
||||
Examples:
|
||||
* `[2, 3, 2].flatMap List.range = [0, 1, 0, 1, 2, 0, 1]`
|
||||
* `["red", "blue"].flatMap String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
|
||||
-/
|
||||
@[inline] def flatMap {α : Type u} {β : Type v} (b : α → List β) (as : List α) : List β := flatten (map b as)
|
||||
|
||||
@[simp, grind =] theorem flatMap_nil {f : α → List β} : List.flatMap f [] = [] := by simp [List.flatMap]
|
||||
@[simp, grind =] theorem flatMap_cons {x : α} {xs : List α} {f : α → List β} :
|
||||
List.flatMap f (x :: xs) = f x ++ List.flatMap f xs := by simp [List.flatMap]
|
||||
|
||||
@[simp, grind _=_] theorem flatMap_append {xs ys : List α} {f : α → List β} :
|
||||
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
|
||||
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
|
||||
|
||||
/-! ### replicate -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -21,65 +21,6 @@ namespace List
|
||||
|
||||
/-! ## Alternative getters -/
|
||||
|
||||
/-! ### get? -/
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
|
||||
Also see `get`, `getD` and `get!`.
|
||||
-/
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), expose]
|
||||
def get? : (as : List α) → (i : Nat) → Option α
|
||||
| a::_, 0 => some a
|
||||
| _::as, n+1 => get? as n
|
||||
| _, _ => none
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
|
||||
theorem get?_nil : @get? α [] n = none := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
|
||||
theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
|
||||
theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `List.ext_getElem?`." (since := "2025-02-12")]
|
||||
theorem ext_get? : ∀ {l₁ l₂ : List α}, (∀ n, l₁.get? n = l₂.get? n) → l₁ = l₂
|
||||
| [], [], _ => rfl
|
||||
| _ :: _, [], h => nomatch h 0
|
||||
| [], _ :: _, h => nomatch h 0
|
||||
| a :: l₁, a' :: l₂, h => by
|
||||
have h0 : some a = some a' := h 0
|
||||
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
|
||||
|
||||
/-! ### get! -/
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function panics when executed, and returns
|
||||
`default`. See `get?` and `getD` for safer alternatives.
|
||||
-/
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12"), expose]
|
||||
def get! [Inhabited α] : (as : List α) → (i : Nat) → α
|
||||
| a::_, 0 => a
|
||||
| _::as, n+1 => get! as n
|
||||
| _, _ => panic! "invalid index"
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
|
||||
theorem get!_nil [Inhabited α] (n : Nat) : [].get! n = (default : α) := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
|
||||
theorem get!_cons_succ [Inhabited α] (l : List α) (a : α) (n : Nat) :
|
||||
(a::l).get! (n+1) = get! l n := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
|
||||
theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a := rfl
|
||||
|
||||
/-! ### getD -/
|
||||
|
||||
/--
|
||||
@@ -281,17 +222,6 @@ theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i
|
||||
cases i with simp [Nat.succ_sub_succ] <;> simp at h₁
|
||||
| succ i => apply ih; simp [h₁]
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2025-02-13")]
|
||||
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
|
||||
cases i; rename_i i h'
|
||||
induction as generalizing i with
|
||||
| nil => cases i with
|
||||
| zero => simp [List.get]
|
||||
| succ => simp +arith at h'
|
||||
| cons a as ih =>
|
||||
cases i with simp at h
|
||||
| succ i => apply ih; simp [h]
|
||||
|
||||
theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
induction h with
|
||||
| head => simp +arith
|
||||
|
||||
@@ -360,9 +360,6 @@ theorem find?_flatten_eq_none_iff {xs : List (List α)} {p : α → Bool} :
|
||||
xs.flatten.find? p = none ↔ ∀ ys ∈ xs, ∀ x ∈ ys, !p x := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
|
||||
|
||||
/--
|
||||
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
|
||||
some list in `xs` contains `a`, and no earlier element of that list satisfies `p`.
|
||||
@@ -403,9 +400,6 @@ theorem find?_flatten_eq_some_iff {xs : List (List α)} {p : α → Bool} {a :
|
||||
· exact h₁ l ml a m
|
||||
· exact h₂ a m
|
||||
|
||||
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
|
||||
|
||||
@[simp, grind =] theorem find?_flatMap {xs : List α} {f : α → List β} {p : β → Bool} :
|
||||
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
|
||||
simp [flatMap_def, findSome?_map]; rfl
|
||||
@@ -434,16 +428,10 @@ theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [Classical.or_iff_not_imp_left]
|
||||
|
||||
@[deprecated find?_replicate_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_replicate_eq_none := @find?_replicate_eq_none_iff
|
||||
|
||||
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
cases n <;> simp
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_replicate_eq_some := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α → Bool} (h) : ((replicate n a).find? p).get h = a := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
@@ -836,9 +824,6 @@ theorem of_findIdx?_eq_some {xs : List α} {p : α → Bool} (w : xs.findIdx? p
|
||||
simp_all only [findIdx?_cons]
|
||||
split at w <;> cases i <;> simp_all
|
||||
|
||||
@[deprecated of_findIdx?_eq_some (since := "2025-02-02")]
|
||||
abbrev findIdx?_of_eq_some := @of_findIdx?_eq_some
|
||||
|
||||
theorem of_findIdx?_eq_none {xs : List α} {p : α → Bool} (w : xs.findIdx? p = none) :
|
||||
∀ i : Nat, match xs[i]? with | some a => ¬ p a | none => true := by
|
||||
intro i
|
||||
@@ -854,9 +839,6 @@ theorem of_findIdx?_eq_none {xs : List α} {p : α → Bool} (w : xs.findIdx? p
|
||||
apply ih
|
||||
split at w <;> simp_all
|
||||
|
||||
@[deprecated of_findIdx?_eq_none (since := "2025-02-02")]
|
||||
abbrev findIdx?_of_eq_none := @of_findIdx?_eq_none
|
||||
|
||||
@[simp, grind _=_] theorem findIdx?_map {f : β → α} {l : List β} : findIdx? p (l.map f) = l.findIdx? (p ∘ f) := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
|
||||
@@ -28,14 +28,14 @@ For each `List` operation, we would like theorems describing the following, when
|
||||
* the length of the result `(f L).length`
|
||||
* the `i`-th element, described via `(f L)[i]` and/or `(f L)[i]?` (these should typically be `@[simp]`)
|
||||
* consequences for `f L` of the fact `x ∈ L` or `x ∉ L`
|
||||
* conditions characterising `x ∈ f L` (often but not always `@[simp]`)
|
||||
* conditions characterizing `x ∈ f L` (often but not always `@[simp]`)
|
||||
* injectivity statements, or congruence statements of the form `p L M → f L = f M`.
|
||||
* conditions characterising the result, i.e. of the form `f L = M ↔ p M` for some predicate `p`,
|
||||
* conditions characterizing the result, i.e. of the form `f L = M ↔ p M` for some predicate `p`,
|
||||
along with special cases of `M` (e.g. `List.append_eq_nil : L ++ M = [] ↔ L = [] ∧ M = []`)
|
||||
* negative characterisations are also useful, e.g. `List.cons_ne_nil`
|
||||
* negative characterizations are also useful, e.g. `List.cons_ne_nil`
|
||||
* interactions with all previously described `List` operations where possible
|
||||
(some of these should be `@[simp]`, particularly if the result can be described by a single operation)
|
||||
* characterising `(∀ (i) (_ : i ∈ f L), P i)`, for some predicate `P`
|
||||
* characterizing `(∀ (i) (_ : i ∈ f L), P i)`, for some predicate `P`
|
||||
|
||||
Of course for any individual operation, not all of these will be relevant or helpful, so some judgement is required.
|
||||
|
||||
@@ -107,9 +107,6 @@ theorem ne_nil_of_length_pos (_ : 0 < length l) : l ≠ [] := fun _ => nomatch l
|
||||
@[simp] theorem length_eq_zero_iff : length l = 0 ↔ l = [] :=
|
||||
⟨eq_nil_of_length_eq_zero, fun h => h ▸ rfl⟩
|
||||
|
||||
@[deprecated length_eq_zero_iff (since := "2025-02-24")]
|
||||
abbrev length_eq_zero := @length_eq_zero_iff
|
||||
|
||||
theorem eq_nil_iff_length_eq_zero : l = [] ↔ length l = 0 :=
|
||||
length_eq_zero_iff.symm
|
||||
|
||||
@@ -142,18 +139,12 @@ theorem exists_cons_of_length_eq_add_one :
|
||||
theorem length_pos_iff {l : List α} : 0 < length l ↔ l ≠ [] :=
|
||||
Nat.pos_iff_ne_zero.trans (not_congr length_eq_zero_iff)
|
||||
|
||||
@[deprecated length_pos_iff (since := "2025-02-24")]
|
||||
abbrev length_pos := @length_pos_iff
|
||||
|
||||
theorem ne_nil_iff_length_pos {l : List α} : l ≠ [] ↔ 0 < length l :=
|
||||
length_pos_iff.symm
|
||||
|
||||
theorem length_eq_one_iff {l : List α} : length l = 1 ↔ ∃ a, l = [a] :=
|
||||
⟨fun h => match l, h with | [_], _ => ⟨_, rfl⟩, fun ⟨_, h⟩ => by simp [h]⟩
|
||||
|
||||
@[deprecated length_eq_one_iff (since := "2025-02-24")]
|
||||
abbrev length_eq_one := @length_eq_one_iff
|
||||
|
||||
/-! ### cons -/
|
||||
|
||||
-- The arguments here are intentionally explicit.
|
||||
@@ -198,38 +189,6 @@ We simplify `l.get i` to `l[i.1]'i.2` and `l.get? i` to `l[i]?`.
|
||||
@[simp, grind =]
|
||||
theorem get_eq_getElem {l : List α} {i : Fin l.length} : l.get i = l[i.1]'i.2 := rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_none : ∀ {l : List α} {n}, length l ≤ n → l.get? n = none
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get?_eq_none (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_get : ∀ {l : List α} {n} (h : n < l.length), l.get? n = some (get l ⟨n, h⟩)
|
||||
| _ :: _, 0, _ => rfl
|
||||
| _ :: l, _+1, _ => get?_eq_get (l := l) _
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_some_iff : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
|
||||
⟨fun e =>
|
||||
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_eq_none hn ▸ e
|
||||
⟨this, by rwa [get?_eq_get this, Option.some.injEq] at e⟩,
|
||||
fun ⟨_, e⟩ => e ▸ get?_eq_get _⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_none_iff : l.get? n = none ↔ length l ≤ n :=
|
||||
⟨fun e => Nat.ge_of_not_lt (fun h' => by cases e ▸ get?_eq_some_iff.2 ⟨h', rfl⟩), get?_eq_none⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
|
||||
theorem get?_eq_getElem? {l : List α} {i : Nat} : l.get? i = l[i]? := by
|
||||
simp only [getElem?_def]; split
|
||||
· exact (get?_eq_get ‹_›)
|
||||
· exact (get?_eq_none_iff.2 <| Nat.not_lt.1 ‹_›)
|
||||
|
||||
/-! ### getElem!
|
||||
|
||||
We simplify `l[i]!` to `(l[i]?).getD default`.
|
||||
@@ -348,6 +307,18 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
theorem getElem?_concat_length {l : List α} {a : α} : (l ++ [a])[l.length]? = some a := by
|
||||
simp
|
||||
|
||||
theorem eq_getElem_of_length_eq_one : (l : List α) → (hl : l.length = 1) → l = [l[0]'(hl ▸ by decide)]
|
||||
| [_], _ => rfl
|
||||
|
||||
theorem eq_getElem_of_length_eq_two : (l : List α) → (hl : l.length = 2) → l = [l[0]'(hl ▸ by decide), l[1]'(hl ▸ by decide)]
|
||||
| [_, _], _ => rfl
|
||||
|
||||
theorem eq_getElem_of_length_eq_three : (l : List α) → (hl : l.length = 3) → l = [l[0]'(hl ▸ by decide), l[1]'(hl ▸ by decide), l[2]'(hl ▸ by decide)]
|
||||
| [_, _, _], _ => rfl
|
||||
|
||||
theorem eq_getElem_of_length_eq_four : (l : List α) → (hl : l.length = 4) → l = [l[0]'(hl ▸ by decide), l[1]'(hl ▸ by decide), l[2]'(hl ▸ by decide), l[3]'(hl ▸ by decide)]
|
||||
| [_, _, _, _], _ => rfl
|
||||
|
||||
/-! ### getD
|
||||
|
||||
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
|
||||
@@ -361,26 +332,9 @@ theorem getD_eq_getElem?_getD {l : List α} {i : Nat} {a : α} : getD l i a = (l
|
||||
theorem getD_cons_zero : getD (x :: xs) 0 d = x := by simp
|
||||
theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := by simp
|
||||
|
||||
/-! ### get!
|
||||
|
||||
We simplify `l.get! i` to `l[i]!`.
|
||||
-/
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
|
||||
theorem get!_eq_getD [Inhabited α] : ∀ (l : List α) i, l.get! i = l.getD i default
|
||||
| [], _ => rfl
|
||||
| _a::_, 0 => by simp [get!]
|
||||
| _a::l, n+1 => by simpa using get!_eq_getD l n
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12"), simp]
|
||||
theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! := by
|
||||
simp [get!_eq_getD]
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
@[simp] theorem not_mem_nil {a : α} : ¬ a ∈ [] := nofun
|
||||
@[simp, grind ←] theorem not_mem_nil {a : α} : ¬ a ∈ [] := nofun
|
||||
|
||||
@[simp, grind =] theorem mem_cons : a ∈ b :: l ↔ a = b ∨ a ∈ l :=
|
||||
⟨fun h => by cases h <;> simp [Membership.mem, *],
|
||||
@@ -569,15 +523,9 @@ theorem elem_eq_mem [BEq α] [LawfulBEq α] (a : α) (as : List α) :
|
||||
@[grind →]
|
||||
theorem nil_of_isEmpty {l : List α} (h : l.isEmpty) : l = [] := List.isEmpty_iff.mp h
|
||||
|
||||
@[deprecated isEmpty_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_true := @isEmpty_iff
|
||||
|
||||
@[simp] theorem isEmpty_eq_false_iff {l : List α} : l.isEmpty = false ↔ l ≠ [] := by
|
||||
cases l <;> simp
|
||||
|
||||
@[deprecated isEmpty_eq_false_iff (since := "2025-02-17")]
|
||||
abbrev isEmpty_eq_false := @isEmpty_eq_false_iff
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem {xs : List α} :
|
||||
xs.isEmpty = false ↔ ∃ x, x ∈ xs := by
|
||||
cases xs <;> simp
|
||||
@@ -2136,10 +2084,6 @@ theorem flatMap_singleton (f : α → List β) (x : α) : [x].flatMap f = f x :=
|
||||
simp only [findSome?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp, grind _=_] theorem flatMap_append {xs ys : List α} {f : α → List β} :
|
||||
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
|
||||
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
|
||||
|
||||
theorem flatMap_assoc {l : List α} {f : α → List β} {g : β → List γ} :
|
||||
(l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by
|
||||
induction l <;> simp [*]
|
||||
@@ -2873,9 +2817,6 @@ theorem getLast_eq_head_reverse {l : List α} (h : l ≠ []) :
|
||||
l.getLast h = l.reverse.head (by simp_all) := by
|
||||
rw [← head_reverse]
|
||||
|
||||
@[deprecated getLast_eq_iff_getLast?_eq_some (since := "2025-02-17")]
|
||||
abbrev getLast_eq_iff_getLast_eq_some := @getLast_eq_iff_getLast?_eq_some
|
||||
|
||||
@[simp] theorem getLast?_eq_none_iff {xs : List α} : xs.getLast? = none ↔ xs = [] := by
|
||||
rw [getLast?_eq_head?_reverse, head?_eq_none_iff, reverse_eq_nil_iff]
|
||||
|
||||
@@ -3679,10 +3620,6 @@ theorem get_cons_succ' {as : List α} {i : Fin as.length} :
|
||||
theorem get_mk_zero : ∀ {l : List α} (h : 0 < l.length), l.get ⟨0, h⟩ = l.head (length_pos_iff.mp h)
|
||||
| _::_, _ => rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[0]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
|
||||
|
||||
/--
|
||||
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
|
||||
`rw [h]` will give a "motive is not type correct" error, as it cannot rewrite the
|
||||
@@ -3692,18 +3629,6 @@ such a rewrite, with `rw [get_of_eq h]`.
|
||||
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
|
||||
get l i = get l' ⟨i, h ▸ i.2⟩ := by cases h; rfl
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get!_of_get? [Inhabited α] : ∀ {l : List α} {n}, get? l n = some a → get! l n = a
|
||||
| _a::_, 0, rfl => rfl
|
||||
| _::l, _+1, e => get!_of_get? (l := l) e
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
|
||||
theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l.get! n = (default : α)
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
|
||||
|
||||
theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
|
||||
@@ -3729,30 +3654,11 @@ theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
|
||||
obtain ⟨n, h, e⟩ := getElem_of_mem h
|
||||
exact ⟨⟨n, h⟩, e⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem?_of_mem (since := "2025-02-12")]
|
||||
theorem get?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, l.get? n = some a :=
|
||||
let ⟨⟨n, _⟩, e⟩ := get_of_mem h; ⟨n, e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem get_mem : ∀ (l : List α) n, get l n ∈ l
|
||||
| _ :: _, ⟨0, _⟩ => .head ..
|
||||
| _ :: l, ⟨_+1, _⟩ => .tail _ (get_mem l ..)
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated mem_of_getElem? (since := "2025-02-12")]
|
||||
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a ∈ l :=
|
||||
let ⟨_, e⟩ := get?_eq_some_iff.1 e; e ▸ get_mem ..
|
||||
|
||||
theorem mem_iff_get {a} {l : List α} : a ∈ l ↔ ∃ n, get l n = a :=
|
||||
⟨get_of_mem, fun ⟨_, e⟩ => e ▸ get_mem ..⟩
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated mem_iff_getElem? (since := "2025-02-12")]
|
||||
theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a := by
|
||||
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
|
||||
|
||||
end List
|
||||
|
||||
@@ -105,9 +105,6 @@ theorem length_leftpad {n : Nat} {a : α} {l : List α} :
|
||||
(leftpad n a l).length = max n l.length := by
|
||||
simp only [leftpad, length_append, length_replicate, Nat.sub_add_eq_max]
|
||||
|
||||
@[deprecated length_leftpad (since := "2025-02-24")]
|
||||
abbrev leftpad_length := @length_leftpad
|
||||
|
||||
theorem length_rightpad {n : Nat} {a : α} {l : List α} :
|
||||
(rightpad n a l).length = max n l.length := by
|
||||
simp [rightpad]
|
||||
|
||||
@@ -196,9 +196,6 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
|
||||
| zero => omega
|
||||
| succ j => simp
|
||||
|
||||
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
|
||||
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
|
||||
|
||||
@[grind =]
|
||||
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] =
|
||||
@@ -261,9 +258,6 @@ theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j)
|
||||
(l.insertIdx i x)[j]? = l[j - 1]? := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
|
||||
|
||||
@[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")]
|
||||
abbrev getElem?_insertIdx_of_ge := @getElem?_insertIdx_of_gt
|
||||
|
||||
end InsertIdx
|
||||
|
||||
end List
|
||||
|
||||
@@ -117,9 +117,6 @@ theorem take_set_of_le {a : α} {i j : Nat} {l : List α} (h : j ≤ i) :
|
||||
next h' => rw [getElem?_set_ne (by omega)]
|
||||
· rfl
|
||||
|
||||
@[deprecated take_set_of_le (since := "2025-02-04")]
|
||||
abbrev take_set_of_lt := @take_set_of_le
|
||||
|
||||
@[simp, grind =] theorem take_replicate {a : α} : ∀ {i n : Nat}, take i (replicate n a) = replicate (min i n) a
|
||||
| n, 0 => by simp
|
||||
| 0, m => by simp
|
||||
@@ -165,9 +162,6 @@ theorem take_eq_take_iff :
|
||||
| x :: xs, 0, j + 1 => by simp [succ_min_succ]
|
||||
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, take_eq_take_iff]
|
||||
|
||||
@[deprecated take_eq_take_iff (since := "2025-02-16")]
|
||||
abbrev take_eq_take := @take_eq_take_iff
|
||||
|
||||
@[grind =]
|
||||
theorem take_add {l : List α} {i j : Nat} : l.take (i + j) = l.take i ++ (l.drop i).take j := by
|
||||
suffices take (i + j) (take i l ++ drop i l) = take i l ++ take j (drop i l) by
|
||||
|
||||
@@ -171,7 +171,7 @@ theorem pairwise_append_comm {R : α → α → Prop} (s : ∀ {x y}, R x y →
|
||||
induction L with
|
||||
| nil => simp
|
||||
| cons l L IH =>
|
||||
simp only [flatten, pairwise_append, IH, mem_flatten, exists_imp, and_imp, forall_mem_cons,
|
||||
simp only [flatten_cons, pairwise_append, IH, mem_flatten, exists_imp, and_imp, forall_mem_cons,
|
||||
pairwise_cons, and_assoc, and_congr_right_iff]
|
||||
rw [and_comm, and_congr_left_iff]
|
||||
intros; exact ⟨fun h l' b c d e => h c d e l' b, fun h c d e l' b => h l' b c d e⟩
|
||||
|
||||
@@ -151,11 +151,11 @@ theorem subset_replicate {n : Nat} {a : α} {l : List α} (h : n ≠ 0) : l ⊆
|
||||
|
||||
/-! ### Sublist and isSublist -/
|
||||
|
||||
@[simp, grind] theorem nil_sublist : ∀ l : List α, [] <+ l
|
||||
@[simp, grind ←] theorem nil_sublist : ∀ l : List α, [] <+ l
|
||||
| [] => .slnil
|
||||
| a :: l => (nil_sublist l).cons a
|
||||
|
||||
@[simp, grind] theorem Sublist.refl : ∀ l : List α, l <+ l
|
||||
@[simp, grind ←] theorem Sublist.refl : ∀ l : List α, l <+ l
|
||||
| [] => .slnil
|
||||
| a :: l => (Sublist.refl l).cons₂ a
|
||||
|
||||
@@ -172,7 +172,7 @@ theorem Sublist.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ <+ l₂) (h₂ : l
|
||||
|
||||
instance : Trans (@Sublist α) Sublist Sublist := ⟨Sublist.trans⟩
|
||||
|
||||
attribute [simp, grind] Sublist.cons
|
||||
attribute [simp, grind ←] Sublist.cons
|
||||
|
||||
theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
|
||||
|
||||
@@ -664,20 +664,20 @@ theorem IsSuffix.isInfix : l₁ <:+ l₂ → l₁ <:+: l₂ := fun ⟨t, h⟩ =>
|
||||
|
||||
grind_pattern IsSuffix.isInfix => l₁ <:+ l₂, IsInfix
|
||||
|
||||
@[simp, grind] theorem nil_prefix {l : List α} : [] <+: l := ⟨l, rfl⟩
|
||||
@[simp, grind ←] theorem nil_prefix {l : List α} : [] <+: l := ⟨l, rfl⟩
|
||||
|
||||
@[simp, grind] theorem nil_suffix {l : List α} : [] <:+ l := ⟨l, append_nil _⟩
|
||||
@[simp, grind ←] theorem nil_suffix {l : List α} : [] <:+ l := ⟨l, append_nil _⟩
|
||||
|
||||
@[simp, grind] theorem nil_infix {l : List α} : [] <:+: l := nil_prefix.isInfix
|
||||
@[simp, grind ←] theorem nil_infix {l : List α} : [] <:+: l := nil_prefix.isInfix
|
||||
|
||||
theorem prefix_refl (l : List α) : l <+: l := ⟨[], append_nil _⟩
|
||||
@[simp, grind] theorem prefix_rfl {l : List α} : l <+: l := prefix_refl l
|
||||
@[simp, grind ←] theorem prefix_rfl {l : List α} : l <+: l := prefix_refl l
|
||||
|
||||
theorem suffix_refl (l : List α) : l <:+ l := ⟨[], rfl⟩
|
||||
@[simp, grind] theorem suffix_rfl {l : List α} : l <:+ l := suffix_refl l
|
||||
@[simp, grind ←] theorem suffix_rfl {l : List α} : l <:+ l := suffix_refl l
|
||||
|
||||
theorem infix_refl (l : List α) : l <:+: l := prefix_rfl.isInfix
|
||||
@[simp, grind] theorem infix_rfl {l : List α} : l <:+: l := infix_refl l
|
||||
@[simp, grind ←] theorem infix_rfl {l : List α} : l <:+: l := infix_refl l
|
||||
|
||||
@[simp] theorem suffix_cons (a : α) : ∀ l, l <:+ a :: l := suffix_append [a]
|
||||
|
||||
|
||||
@@ -165,9 +165,6 @@ theorem take_set {l : List α} {i j : Nat} {a : α} :
|
||||
| nil => simp
|
||||
| cons hd tl => cases j <;> simp_all
|
||||
|
||||
@[deprecated take_set (since := "2025-02-17")]
|
||||
abbrev set_take := @take_set
|
||||
|
||||
theorem drop_set {l : List α} {i j : Nat} {a : α} :
|
||||
(l.set j a).drop i = if j < i then l.drop i else (l.drop i).set (j - i) a := by
|
||||
induction i generalizing l j with
|
||||
|
||||
@@ -791,18 +791,6 @@ protected theorem pow_le_pow_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i
|
||||
| Or.inr h =>
|
||||
h.symm ▸ Nat.le_refl _
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated Nat.pow_le_pow_right (since := "2025-02-17")]
|
||||
abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated Nat.pow_pos (since := "2025-02-17")]
|
||||
abbrev pos_pow_of_pos := @Nat.pow_pos
|
||||
|
||||
@[simp] theorem zero_pow_of_pos (n : Nat) (h : 0 < n) : 0 ^ n = 0 := by
|
||||
cases n with
|
||||
| zero => cases h
|
||||
@@ -882,9 +870,6 @@ protected theorem ne_zero_of_lt (h : b < a) : a ≠ 0 := by
|
||||
exact absurd h (Nat.not_lt_zero _)
|
||||
apply Nat.noConfusion
|
||||
|
||||
@[deprecated Nat.ne_zero_of_lt (since := "2025-02-06")]
|
||||
theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := Nat.ne_zero_of_lt h
|
||||
|
||||
theorem pred_lt_of_lt {n m : Nat} (h : m < n) : pred n < n :=
|
||||
pred_lt (Nat.ne_zero_of_lt h)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ prelude
|
||||
public import Init.ByCases
|
||||
public import Init.Data.Prod
|
||||
public import Init.Data.RArray
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
public section
|
||||
|
||||
@@ -138,21 +139,7 @@ structure PolyCnstr where
|
||||
eq : Bool
|
||||
lhs : Poly
|
||||
rhs : Poly
|
||||
deriving BEq
|
||||
|
||||
-- TODO: implement LawfulBEq generator companion for BEq
|
||||
instance : LawfulBEq PolyCnstr where
|
||||
eq_of_beq {a b} h := by
|
||||
cases a; rename_i eq₁ lhs₁ rhs₁
|
||||
cases b; rename_i eq₂ lhs₂ rhs₂
|
||||
have h : eq₁ == eq₂ && (lhs₁ == lhs₂ && rhs₁ == rhs₂) := h
|
||||
simp at h
|
||||
have ⟨h₁, h₂, h₃⟩ := h
|
||||
rw [h₁, h₂, h₃]
|
||||
rfl {a} := by
|
||||
cases a; rename_i eq lhs rhs
|
||||
change (eq == eq && (lhs == lhs && rhs == rhs)) = true
|
||||
simp
|
||||
deriving BEq, ReflBEq, LawfulBEq
|
||||
|
||||
structure ExprCnstr where
|
||||
eq : Bool
|
||||
|
||||
@@ -75,9 +75,6 @@ theorem attach_map_val (o : Option α) (f : α → β) :
|
||||
(o.attach.map fun (i : {i // o = some i}) => f i) = o.map f := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
@[simp, grind =]theorem attach_map_subtype_val (o : Option α) :
|
||||
o.attach.map Subtype.val = o :=
|
||||
(attach_map_val _ _).trans (congrFun Option.map_id _)
|
||||
@@ -86,9 +83,6 @@ theorem attachWith_map_val {p : α → Prop} (f : α → β) (o : Option α) (H
|
||||
((o.attachWith p H).map fun (i : { i // p i}) => f i.val) = o.map f := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
@[simp, grind =] theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a, o = some a → p a) :
|
||||
(o.attachWith p H).map Subtype.val = o :=
|
||||
(attachWith_map_val _ _ _).trans (congrFun Option.map_id _)
|
||||
@@ -187,9 +181,6 @@ theorem toArray_attachWith {p : α → Prop} {o : Option α} {h} :
|
||||
o.attach.map f = o.pmap (fun a (h : o = some a) => f ⟨a, h⟩) (fun _ h => h) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[simp, grind =] theorem map_attachWith {l : Option α} {P : α → Prop} {H : ∀ (a : α), l = some a → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(l.attachWith P H).map f = l.attach.map fun ⟨x, h⟩ => f ⟨x, H _ h⟩ := by
|
||||
|
||||
@@ -52,7 +52,7 @@ theorem get_of_mem : ∀ {o : Option α} (h : isSome o), a ∈ o → o.get h = a
|
||||
theorem get_of_eq_some : ∀ {o : Option α} (h : isSome o), o = some a → o.get h = a
|
||||
| _, _, rfl => rfl
|
||||
|
||||
@[simp, grind] theorem not_mem_none (a : α) : a ∉ (none : Option α) := nofun
|
||||
@[simp, grind ←] theorem not_mem_none (a : α) : a ∉ (none : Option α) := nofun
|
||||
|
||||
theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.getD y) = x := by
|
||||
cases x; {contradiction}; rw [getD_some]
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Option
|
||||
o.toList.foldr f a = o.elim a (fun b => f b a) := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind ←]
|
||||
theorem pairwise_toList {P : α → α → Prop} {o : Option α} : o.toList.Pairwise P := by
|
||||
cases o <;> simp
|
||||
|
||||
|
||||
@@ -96,8 +96,7 @@ theorem Int8.toBitVec.inj : {x y : Int8} → x.toBitVec = y.toBitVec → x = y
|
||||
|
||||
/-- 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]
|
||||
def Int8.mk (i : UInt8) : Int8 := UInt8.toInt8 i
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision integer to an 8-bit integer, wrapping on overflow or underflow.
|
||||
|
||||
@@ -159,8 +158,7 @@ Converts an 8-bit signed integer to a natural number, mapping all negative numbe
|
||||
Use `Int8.toBitVec` to obtain the two's complement representation.
|
||||
-/
|
||||
@[inline] def Int8.toNatClampNeg (i : Int8) : Nat := i.toInt.toNat
|
||||
@[inline, deprecated Int8.toNatClampNeg (since := "2025-02-13"), inherit_doc Int8.toNatClampNeg]
|
||||
def Int8.toNat (i : Int8) : Nat := i.toInt.toNat
|
||||
|
||||
/-- Obtains the `Int8` whose 2's complement representation is the given `BitVec 8`. -/
|
||||
@[inline] def Int8.ofBitVec (b : BitVec 8) : Int8 := ⟨⟨b⟩⟩
|
||||
/--
|
||||
@@ -449,8 +447,7 @@ theorem Int16.toBitVec.inj : {x y : Int16} → x.toBitVec = y.toBitVec → x = y
|
||||
|
||||
/-- 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]
|
||||
def Int16.mk (i : UInt16) : Int16 := UInt16.toInt16 i
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision integer to a 16-bit signed integer, wrapping on overflow or underflow.
|
||||
|
||||
@@ -514,8 +511,7 @@ Converts a 16-bit signed integer to a natural number, mapping all negative numbe
|
||||
Use `Int16.toBitVec` to obtain the two's complement representation.
|
||||
-/
|
||||
@[inline] def Int16.toNatClampNeg (i : Int16) : Nat := i.toInt.toNat
|
||||
@[inline, deprecated Int16.toNatClampNeg (since := "2025-02-13"), inherit_doc Int16.toNatClampNeg]
|
||||
def Int16.toNat (i : Int16) : Nat := i.toInt.toNat
|
||||
|
||||
/-- Obtains the `Int16` whose 2's complement representation is the given `BitVec 16`. -/
|
||||
@[inline] def Int16.ofBitVec (b : BitVec 16) : Int16 := ⟨⟨b⟩⟩
|
||||
/--
|
||||
@@ -820,8 +816,7 @@ theorem Int32.toBitVec.inj : {x y : Int32} → x.toBitVec = y.toBitVec → x = y
|
||||
|
||||
/-- 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]
|
||||
def Int32.mk (i : UInt32) : Int32 := UInt32.toInt32 i
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision integer to a 32-bit integer, wrapping on overflow or underflow.
|
||||
|
||||
@@ -886,8 +881,7 @@ Converts a 32-bit signed integer to a natural number, mapping all negative numbe
|
||||
Use `Int32.toBitVec` to obtain the two's complement representation.
|
||||
-/
|
||||
@[inline] def Int32.toNatClampNeg (i : Int32) : Nat := i.toInt.toNat
|
||||
@[inline, deprecated Int32.toNatClampNeg (since := "2025-02-13"), inherit_doc Int32.toNatClampNeg]
|
||||
def Int32.toNat (i : Int32) : Nat := i.toInt.toNat
|
||||
|
||||
/-- Obtains the `Int32` whose 2's complement representation is the given `BitVec 32`. -/
|
||||
@[inline] def Int32.ofBitVec (b : BitVec 32) : Int32 := ⟨⟨b⟩⟩
|
||||
/--
|
||||
@@ -1207,8 +1201,7 @@ theorem Int64.toBitVec.inj : {x y : Int64} → x.toBitVec = y.toBitVec → x = y
|
||||
|
||||
/-- 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]
|
||||
def Int64.mk (i : UInt64) : Int64 := UInt64.toInt64 i
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision integer to a 64-bit integer, wrapping on overflow or underflow.
|
||||
|
||||
@@ -1278,8 +1271,7 @@ Converts a 64-bit signed integer to a natural number, mapping all negative numbe
|
||||
Use `Int64.toBitVec` to obtain the two's complement representation.
|
||||
-/
|
||||
@[inline] def Int64.toNatClampNeg (i : Int64) : Nat := i.toInt.toNat
|
||||
@[inline, deprecated Int64.toNatClampNeg (since := "2025-02-13"), inherit_doc Int64.toNatClampNeg]
|
||||
def Int64.toNat (i : Int64) : Nat := i.toInt.toNat
|
||||
|
||||
/-- Obtains the `Int64` whose 2's complement representation is the given `BitVec 64`. -/
|
||||
@[inline] def Int64.ofBitVec (b : BitVec 64) : Int64 := ⟨⟨b⟩⟩
|
||||
/--
|
||||
@@ -1613,8 +1605,7 @@ theorem ISize.toBitVec.inj : {x y : ISize} → x.toBitVec = y.toBitVec → x = y
|
||||
|
||||
/-- 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]
|
||||
def ISize.mk (i : USize) : ISize := USize.toISize i
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision integer to a word-sized signed integer, wrapping around on over- or
|
||||
underflow.
|
||||
@@ -1647,8 +1638,7 @@ Converts a word-sized signed integer to a natural number, mapping all negative n
|
||||
Use `ISize.toBitVec` to obtain the two's complement representation.
|
||||
-/
|
||||
@[inline] def ISize.toNatClampNeg (i : ISize) : Nat := i.toInt.toNat
|
||||
@[inline, deprecated ISize.toNatClampNeg (since := "2025-02-13"), inherit_doc ISize.toNatClampNeg]
|
||||
def ISize.toNat (i : ISize) : Nat := i.toInt.toNat
|
||||
|
||||
/-- Obtains the `ISize` whose 2's complement representation is the given `BitVec`. -/
|
||||
@[inline] def ISize.ofBitVec (b : BitVec System.Platform.numBits) : ISize := ⟨⟨b⟩⟩
|
||||
/--
|
||||
|
||||
@@ -10,7 +10,7 @@ public import Init.Core
|
||||
public import Init.Data.Slice.Array.Basic
|
||||
import Init.Data.Iterators.Combinators.Attach
|
||||
import Init.Data.Iterators.Combinators.FilterMap
|
||||
import Init.Data.Iterators.Combinators.ULift
|
||||
public import Init.Data.Iterators.Combinators.ULift
|
||||
public import Init.Data.Iterators.Consumers.Collect
|
||||
public import Init.Data.Iterators.Consumers.Loop
|
||||
public import Init.Data.Range.Polymorphic.Basic
|
||||
@@ -31,7 +31,6 @@ open Std Slice PRange Iterators
|
||||
|
||||
variable {shape : RangeShape} {α : Type u}
|
||||
|
||||
@[no_expose]
|
||||
instance {s : Subarray α} : ToIterator s Id α :=
|
||||
.of _
|
||||
(PRange.Internal.iter (s.internalRepresentation.start...<s.internalRepresentation.stop)
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
prelude
|
||||
public import Init.Data.String.Basic
|
||||
public import Init.Data.String.Bootstrap
|
||||
public import Init.Data.String.Decode
|
||||
public import Init.Data.String.Extra
|
||||
public import Init.Data.String.Lemmas
|
||||
public import Init.Data.String.Repr
|
||||
|
||||
@@ -9,11 +9,370 @@ prelude
|
||||
public import Init.Data.List.Basic
|
||||
public import Init.Data.Char.Basic
|
||||
public import Init.Data.String.Bootstrap
|
||||
public import Init.Data.ByteArray.Basic
|
||||
public import Init.Data.String.Decode
|
||||
import Init.Data.ByteArray.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
universe u
|
||||
|
||||
section
|
||||
|
||||
@[simp]
|
||||
theorem List.utf8Encode_nil : List.utf8Encode [] = ByteArray.empty := by simp [utf8Encode]
|
||||
|
||||
theorem List.utf8Encode_singleton {c : Char} : [c].utf8Encode = (String.utf8EncodeChar c).toByteArray := by
|
||||
simp [utf8Encode]
|
||||
|
||||
@[simp]
|
||||
theorem List.utf8Encode_append {l l' : List Char} :
|
||||
(l ++ l').utf8Encode = l.utf8Encode ++ l'.utf8Encode := by
|
||||
simp [utf8Encode]
|
||||
|
||||
theorem List.utf8Encode_cons {c : Char} {l : List Char} : (c :: l).utf8Encode = [c].utf8Encode ++ l.utf8Encode := by
|
||||
rw [← singleton_append, List.utf8Encode_append]
|
||||
|
||||
@[simp]
|
||||
theorem String.utf8EncodeChar_ne_nil {c : Char} : String.utf8EncodeChar c ≠ [] := by
|
||||
fun_cases String.utf8EncodeChar with simp
|
||||
|
||||
@[simp]
|
||||
theorem List.utf8Encode_eq_empty {l : List Char} : l.utf8Encode = ByteArray.empty ↔ l = [] := by
|
||||
simp [utf8Encode, ← List.eq_nil_iff_forall_not_mem]
|
||||
|
||||
theorem ByteArray.isValidUtf8_utf8Encode {l : List Char} : IsValidUtf8 l.utf8Encode :=
|
||||
.intro l rfl
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.isValidUtf8_empty : IsValidUtf8 ByteArray.empty :=
|
||||
.intro [] (by simp)
|
||||
|
||||
theorem Char.isValidUtf8_toByteArray_utf8EncodeChar {c : Char} :
|
||||
ByteArray.IsValidUtf8 (String.utf8EncodeChar c).toByteArray :=
|
||||
.intro [c] (by simp [List.utf8Encode_singleton])
|
||||
|
||||
theorem ByteArray.IsValidUtf8.append {b b' : ByteArray} (h : IsValidUtf8 b) (h' : IsValidUtf8 b') :
|
||||
IsValidUtf8 (b ++ b') := by
|
||||
rcases h with ⟨m, rfl⟩
|
||||
rcases h' with ⟨m', rfl⟩
|
||||
exact .intro (m ++ m') (by simp)
|
||||
|
||||
theorem ByteArray.isValidUtf8_utf8Encode_singleton_append_iff {b : ByteArray} {c : Char} :
|
||||
IsValidUtf8 ([c].utf8Encode ++ b) ↔ IsValidUtf8 b := by
|
||||
refine ⟨?_, fun h => IsValidUtf8.append isValidUtf8_utf8Encode h⟩
|
||||
rintro ⟨l, hl⟩
|
||||
match l with
|
||||
| [] => simp at hl
|
||||
| d::l =>
|
||||
obtain rfl : c = d := by
|
||||
replace hl := congrArg (fun l => utf8DecodeChar? l 0) hl
|
||||
simpa [List.utf8DecodeChar?_utf8Encode_singleton_append,
|
||||
List.utf8DecodeChar?_utf8Encode_cons] using hl
|
||||
rw [← List.singleton_append (l := l), List.utf8Encode_append,
|
||||
ByteArray.append_right_inj] at hl
|
||||
exact hl ▸ isValidUtf8_utf8Encode
|
||||
|
||||
@[expose]
|
||||
def ByteArray.utf8Decode? (b : ByteArray) : Option (Array Char) :=
|
||||
go (b.size + 1) 0 #[] (by simp) (by simp)
|
||||
where
|
||||
go (fuel : Nat) (i : Nat) (acc : Array Char) (hi : i ≤ b.size) (hf : b.size - i < fuel) : Option (Array Char) :=
|
||||
match fuel, hf with
|
||||
| fuel + 1, _ =>
|
||||
if i = b.size then
|
||||
some acc
|
||||
else
|
||||
match h : utf8DecodeChar? b i with
|
||||
| none => none
|
||||
| some c => go fuel (i + c.utf8Size) (acc.push c)
|
||||
(le_size_of_utf8DecodeChar?_eq_some h)
|
||||
(have := c.utf8Size_pos; have := le_size_of_utf8DecodeChar?_eq_some h; by omega)
|
||||
termination_by structural fuel
|
||||
|
||||
theorem ByteArray.utf8Decode?.go.congr {b b' : ByteArray} {fuel fuel' i i' : Nat} {acc acc' : Array Char} {hi hi' hf hf'}
|
||||
(hbb' : b = b') (hii' : i = i') (hacc : acc = acc') :
|
||||
ByteArray.utf8Decode?.go b fuel i acc hi hf = ByteArray.utf8Decode?.go b' fuel' i' acc' hi' hf' := by
|
||||
subst hbb' hii' hacc
|
||||
fun_induction ByteArray.utf8Decode?.go b fuel i acc hi hf generalizing fuel' with
|
||||
| case1 =>
|
||||
rw [go.eq_def]
|
||||
split
|
||||
simp
|
||||
| case2 =>
|
||||
rw [go.eq_def]
|
||||
split <;> split
|
||||
· simp_all
|
||||
· split <;> simp_all
|
||||
| case3 =>
|
||||
conv => rhs; rw [go.eq_def]
|
||||
split <;> split
|
||||
· simp_all
|
||||
· split
|
||||
· simp_all
|
||||
· rename_i c₁ hc₁ ih _ _ _ _ _ c₂ hc₂
|
||||
obtain rfl : c₁ = c₂ := by rw [← Option.some_inj, ← hc₁, ← hc₂]
|
||||
apply ih
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.utf8Decode?_empty : ByteArray.empty.utf8Decode? = some #[] := by
|
||||
simp [utf8Decode?, utf8Decode?.go]
|
||||
|
||||
private theorem ByteArray.isSome_utf8Decode?go_iff {b : ByteArray} {fuel i : Nat} {hi : i ≤ b.size} {hf} {acc : Array Char} :
|
||||
(ByteArray.utf8Decode?.go b fuel i acc hi hf).isSome ↔ IsValidUtf8 (b.extract i b.size) := by
|
||||
fun_induction ByteArray.utf8Decode?.go with
|
||||
| case1 => simp
|
||||
| case2 fuel i hi hf acc h₁ h₂ =>
|
||||
simp only [Option.isSome_none, Bool.false_eq_true, false_iff]
|
||||
rintro ⟨l, hl⟩
|
||||
have : l ≠ [] := by
|
||||
rintro rfl
|
||||
simp at hl
|
||||
omega
|
||||
rw [← l.cons_head_tail this] at hl
|
||||
rw [utf8DecodeChar?_eq_utf8DecodeChar?_extract, hl, List.utf8DecodeChar?_utf8Encode_cons] at h₂
|
||||
simp at h₂
|
||||
| case3 i acc hi fuel hf h₁ c h₂ ih =>
|
||||
rw [ih]
|
||||
have h₂' := h₂
|
||||
rw [utf8DecodeChar?_eq_utf8DecodeChar?_extract] at h₂'
|
||||
obtain ⟨l, hl⟩ := exists_of_utf8DecodeChar?_eq_some h₂'
|
||||
rw [ByteArray.extract_eq_extract_append_extract (i := i) (i + c.utf8Size) (by omega)
|
||||
(le_size_of_utf8DecodeChar?_eq_some h₂)] at hl ⊢
|
||||
rw [ByteArray.append_inj_left hl (by have := le_size_of_utf8DecodeChar?_eq_some h₂; simp; omega),
|
||||
← List.utf8Encode_singleton, isValidUtf8_utf8Encode_singleton_append_iff]
|
||||
|
||||
theorem ByteArray.isSome_utf8Decode?_iff {b : ByteArray} :
|
||||
b.utf8Decode?.isSome ↔ IsValidUtf8 b := by
|
||||
rw [utf8Decode?, isSome_utf8Decode?go_iff, extract_zero_size]
|
||||
|
||||
@[simp]
|
||||
theorem String.bytes_empty : "".bytes = ByteArray.empty := (rfl)
|
||||
|
||||
/--
|
||||
Appends two strings. Usually accessed via the `++` operator.
|
||||
|
||||
The internal implementation will perform destructive updates if the string is not shared.
|
||||
|
||||
Examples:
|
||||
* `"abc".append "def" = "abcdef"`
|
||||
* `"abc" ++ "def" = "abcdef"`
|
||||
* `"" ++ "" = ""`
|
||||
-/
|
||||
@[extern "lean_string_append", expose]
|
||||
def String.append (s t : String) : String where
|
||||
bytes := s.bytes ++ t.bytes
|
||||
isValidUtf8 := s.isValidUtf8.append t.isValidUtf8
|
||||
|
||||
instance : Append String where
|
||||
append s t := s.append t
|
||||
|
||||
@[simp]
|
||||
theorem String.bytes_append {s t : String} : (s ++ t).bytes = s.bytes ++ t.bytes := (rfl)
|
||||
|
||||
theorem String.bytes_inj {s t : String} : s.bytes = t.bytes ↔ s = t := by
|
||||
refine ⟨fun h => ?_, (· ▸ rfl)⟩
|
||||
rcases s with ⟨s⟩
|
||||
rcases t with ⟨t⟩
|
||||
subst h
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem String.empty_append {s : String} : "" ++ s = s := by
|
||||
simp [← String.bytes_inj]
|
||||
|
||||
@[simp]
|
||||
theorem String.append_empty {s : String} : s ++ "" = s := by
|
||||
simp [← String.bytes_inj]
|
||||
|
||||
@[simp] theorem List.bytes_asString {l : List Char} : l.asString.bytes = l.utf8Encode := by
|
||||
simp [List.asString, String.mk]
|
||||
|
||||
@[simp]
|
||||
theorem List.asString_nil : List.asString [] = "" := by
|
||||
simp [← String.bytes_inj]
|
||||
|
||||
@[simp]
|
||||
theorem List.asString_append {l₁ l₂ : List Char} : (l₁ ++ l₂).asString = l₁.asString ++ l₂.asString := by
|
||||
simp [← String.bytes_inj]
|
||||
|
||||
@[expose]
|
||||
def String.Internal.toArray (b : String) : Array Char :=
|
||||
b.bytes.utf8Decode?.get (b.bytes.isSome_utf8Decode?_iff.2 b.isValidUtf8)
|
||||
|
||||
@[simp]
|
||||
theorem String.Internal.toArray_empty : String.Internal.toArray "" = #[] := by
|
||||
simp [toArray]
|
||||
|
||||
@[extern "lean_string_data", expose]
|
||||
def String.data (b : String) : List Char :=
|
||||
(String.Internal.toArray b).toList
|
||||
|
||||
@[simp]
|
||||
theorem String.data_empty : "".data = [] := by
|
||||
simp [data]
|
||||
|
||||
/--
|
||||
Returns the length of a string in Unicode code points.
|
||||
|
||||
Examples:
|
||||
* `"".length = 0`
|
||||
* `"abc".length = 3`
|
||||
* `"L∃∀N".length = 4`
|
||||
-/
|
||||
@[extern "lean_string_length"]
|
||||
def String.length (b : String) : Nat :=
|
||||
b.data.length
|
||||
|
||||
@[simp]
|
||||
theorem String.Internal.size_toArray {b : String} : (String.Internal.toArray b).size = b.length :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem String.length_data {b : String} : b.data.length = b.length := (rfl)
|
||||
|
||||
theorem String.exists_eq_asString (s : String) :
|
||||
∃ l : List Char, s = l.asString := by
|
||||
rcases s with ⟨_, ⟨l, rfl⟩⟩
|
||||
refine ⟨l, by simp [← String.bytes_inj]⟩
|
||||
|
||||
private theorem ByteArray.utf8Decode?go_eq_utf8Decode?go_extract {b : ByteArray} {fuel i : Nat} {hi : i ≤ b.size} {hf} {acc : Array Char} :
|
||||
utf8Decode?.go b fuel i acc hi hf = (utf8Decode?.go (b.extract i b.size) fuel 0 #[] (by simp) (by simp [hf])).map (acc ++ ·) := by
|
||||
fun_cases utf8Decode?.go b fuel i acc hi hf with
|
||||
| case1 =>
|
||||
rw [utf8Decode?.go]
|
||||
simp only [size_extract, Nat.le_refl, Nat.min_eq_left, Nat.zero_add, List.push_toArray,
|
||||
List.nil_append]
|
||||
rw [if_pos (by omega)]
|
||||
simp
|
||||
| case2 fuel hf₁ h₁ h₂ hf₂ =>
|
||||
rw [utf8Decode?.go]
|
||||
simp only [size_extract, Nat.le_refl, Nat.min_eq_left, Nat.zero_add, List.push_toArray,
|
||||
List.nil_append]
|
||||
rw [if_neg (by omega)]
|
||||
rw [utf8DecodeChar?_eq_utf8DecodeChar?_extract] at h₂
|
||||
split <;> simp_all
|
||||
| case3 fuel hf₁ h₁ c h₂ hf₂ =>
|
||||
conv => rhs; rw [utf8Decode?.go]
|
||||
simp only [size_extract, Nat.le_refl, Nat.min_eq_left, Nat.zero_add, List.push_toArray,
|
||||
List.nil_append]
|
||||
rw [if_neg (by omega)]
|
||||
rw [utf8DecodeChar?_eq_utf8DecodeChar?_extract] at h₂
|
||||
split
|
||||
· simp_all
|
||||
· rename_i c' hc'
|
||||
obtain rfl : c = c' := by
|
||||
rw [← Option.some_inj, ← h₂, hc']
|
||||
have := c.utf8Size_pos
|
||||
conv => lhs; rw [ByteArray.utf8Decode?go_eq_utf8Decode?go_extract]
|
||||
conv => rhs; rw [ByteArray.utf8Decode?go_eq_utf8Decode?go_extract]
|
||||
simp only [size_extract, Nat.le_refl, Nat.min_eq_left, Option.map_map, ByteArray.extract_extract]
|
||||
have : (fun x => acc ++ x) ∘ (fun x => #[c] ++ x) = fun x => acc.push c ++ x := by funext; simp
|
||||
simp [(by omega : i + (b.size - i) = b.size), this]
|
||||
|
||||
theorem ByteArray.utf8Decode?_utf8Encode_singleton_append {l : ByteArray} {c : Char} :
|
||||
([c].utf8Encode ++ l).utf8Decode? = l.utf8Decode?.map (#[c] ++ ·) := by
|
||||
rw [utf8Decode?, utf8Decode?.go,
|
||||
if_neg (by simp [List.utf8Encode_singleton]; have := c.utf8Size_pos; omega)]
|
||||
split
|
||||
· simp_all [List.utf8DecodeChar?_utf8Encode_singleton_append]
|
||||
· rename_i d h
|
||||
obtain rfl : c = d := by simpa [List.utf8DecodeChar?_utf8Encode_singleton_append] using h
|
||||
rw [utf8Decode?go_eq_utf8Decode?go_extract, utf8Decode?]
|
||||
simp only [List.push_toArray, List.nil_append, Nat.zero_add]
|
||||
congr 1
|
||||
apply ByteArray.utf8Decode?.go.congr _ rfl rfl
|
||||
apply extract_append_eq_right
|
||||
simp [List.utf8Encode_singleton]
|
||||
|
||||
@[simp]
|
||||
theorem List.utf8Decode?_utf8Encode {l : List Char} :
|
||||
l.utf8Encode.utf8Decode? = some l.toArray := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons c l ih =>
|
||||
rw [← List.singleton_append, List.utf8Encode_append]
|
||||
simp only [ByteArray.utf8Decode?_utf8Encode_singleton_append, cons_append, nil_append,
|
||||
Option.map_eq_some_iff, Array.append_eq_toArray_iff, cons.injEq, true_and]
|
||||
refine ⟨l.toArray, ih, by simp⟩
|
||||
|
||||
@[simp]
|
||||
theorem ByteArray.utf8Encode_get_utf8Decode? {b : ByteArray} {h} :
|
||||
(b.utf8Decode?.get h).toList.utf8Encode = b := by
|
||||
obtain ⟨l, rfl⟩ := isSome_utf8Decode?_iff.1 h
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem List.data_asString {l : List Char} : l.asString.data = l := by
|
||||
simp [String.data, String.Internal.toArray]
|
||||
|
||||
@[simp]
|
||||
theorem String.asString_data {b : String} : b.data.asString = b := by
|
||||
obtain ⟨l, rfl⟩ := String.exists_eq_asString b
|
||||
rw [List.data_asString]
|
||||
|
||||
theorem List.asString_injective {l₁ l₂ : List Char} (h : l₁.asString = l₂.asString) : l₁ = l₂ := by
|
||||
simpa using congrArg String.data h
|
||||
|
||||
theorem List.asString_inj {l₁ l₂ : List Char} : l₁.asString = l₂.asString ↔ l₁ = l₂ :=
|
||||
⟨asString_injective, (· ▸ rfl)⟩
|
||||
|
||||
theorem String.data_injective {s₁ s₂ : String} (h : s₁.data = s₂.data) : s₁ = s₂ := by
|
||||
simpa using congrArg List.asString h
|
||||
|
||||
theorem String.data_inj {s₁ s₂ : String} : s₁.data = s₂.data ↔ s₁ = s₂ :=
|
||||
⟨data_injective, (· ▸ rfl)⟩
|
||||
|
||||
@[simp]
|
||||
theorem String.data_append {l₁ l₂ : String} : (l₁ ++ l₂).data = l₁.data ++ l₂.data := by
|
||||
apply List.asString_injective
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem String.utf8encode_data {b : String} : b.data.utf8Encode = b.bytes := by
|
||||
have := congrArg String.bytes (String.asString_data (b := b))
|
||||
rwa [← List.bytes_asString]
|
||||
|
||||
@[simp]
|
||||
theorem String.utf8ByteSize_empty : "".utf8ByteSize = 0 := (rfl)
|
||||
|
||||
@[simp]
|
||||
theorem String.utf8ByteSize_append {s t : String} :
|
||||
(s ++ t).utf8ByteSize = s.utf8ByteSize + t.utf8ByteSize := by
|
||||
simp [utf8ByteSize]
|
||||
|
||||
@[simp]
|
||||
theorem String.size_bytes {s : String} : s.bytes.size = s.utf8ByteSize := rfl
|
||||
|
||||
@[simp]
|
||||
theorem String.bytes_push {s : String} {c : Char} : (s.push c).bytes = s.bytes ++ [c].utf8Encode := by
|
||||
simp [push]
|
||||
|
||||
-- This is just to keep the proof of `set_next_add` below from breaking; if that lemma goes away
|
||||
-- or the proof is rewritten, it can be removed.
|
||||
private noncomputable def String.utf8ByteSize' : String → Nat
|
||||
| s => go s.data
|
||||
where
|
||||
go : List Char → Nat
|
||||
| [] => 0
|
||||
| c::cs => go cs + c.utf8Size
|
||||
|
||||
private theorem String.utf8ByteSize'_eq (s : String) : s.utf8ByteSize' = s.utf8ByteSize := by
|
||||
suffices ∀ l, utf8ByteSize'.go l = l.asString.utf8ByteSize by
|
||||
obtain ⟨m, rfl⟩ := s.exists_eq_asString
|
||||
rw [utf8ByteSize', this, asString_data]
|
||||
intro l
|
||||
induction l with
|
||||
| nil => simp [utf8ByteSize'.go]
|
||||
| cons c cs ih =>
|
||||
rw [utf8ByteSize'.go, ih, ← List.singleton_append, List.asString_append,
|
||||
utf8ByteSize_append, Nat.add_comm]
|
||||
congr
|
||||
rw [← size_bytes, List.bytes_asString, List.utf8Encode_singleton,
|
||||
List.size_toByteArray, length_utf8EncodeChar]
|
||||
|
||||
end
|
||||
|
||||
namespace String
|
||||
|
||||
instance : HAdd String.Pos String.Pos String.Pos where
|
||||
@@ -54,8 +413,6 @@ instance : LT String :=
|
||||
instance decidableLT (s₁ s₂ : @& String) : Decidable (s₁ < s₂) :=
|
||||
List.decidableLT s₁.data s₂.data
|
||||
|
||||
|
||||
|
||||
/--
|
||||
Non-strict inequality on strings, typically used via the `≤` operator.
|
||||
|
||||
@@ -69,32 +426,6 @@ instance : LE String :=
|
||||
instance decLE (s₁ s₂ : String) : Decidable (s₁ ≤ s₂) :=
|
||||
inferInstanceAs (Decidable (Not _))
|
||||
|
||||
/--
|
||||
Returns the length of a string in Unicode code points.
|
||||
|
||||
Examples:
|
||||
* `"".length = 0`
|
||||
* `"abc".length = 3`
|
||||
* `"L∃∀N".length = 4`
|
||||
-/
|
||||
@[extern "lean_string_length", expose]
|
||||
def length : (@& String) → Nat
|
||||
| ⟨s⟩ => s.length
|
||||
|
||||
/--
|
||||
Appends two strings. Usually accessed via the `++` operator.
|
||||
|
||||
The internal implementation will perform destructive updates if the string is not shared.
|
||||
|
||||
Examples:
|
||||
* `"abc".append "def" = "abcdef"`
|
||||
* `"abc" ++ "def" = "abcdef"`
|
||||
* `"" ++ "" = ""`
|
||||
-/
|
||||
@[extern "lean_string_append", expose]
|
||||
def append : String → (@& String) → String
|
||||
| ⟨a⟩, ⟨b⟩ => ⟨a ++ b⟩
|
||||
|
||||
/--
|
||||
Converts a string to a list of characters.
|
||||
|
||||
@@ -153,8 +484,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_utf8_get", expose]
|
||||
def get (s : @& String) (p : @& Pos) : Char :=
|
||||
match s with
|
||||
| ⟨s⟩ => utf8GetAux s 0 p
|
||||
utf8GetAux s.data 0 p
|
||||
|
||||
def utf8GetAux? : List Char → Pos → Pos → Option Char
|
||||
| [], _, _ => none
|
||||
@@ -175,7 +505,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_utf8_get_opt"]
|
||||
def get? : (@& String) → (@& Pos) → Option Char
|
||||
| ⟨s⟩, p => utf8GetAux? s 0 p
|
||||
| s, p => utf8GetAux? s.data 0 p
|
||||
|
||||
/--
|
||||
Returns the character at position `p` of a string. Panics if `p` is not a valid position.
|
||||
@@ -191,7 +521,7 @@ Examples
|
||||
@[extern "lean_string_utf8_get_bang", expose]
|
||||
def get! (s : @& String) (p : @& Pos) : Char :=
|
||||
match s with
|
||||
| ⟨s⟩ => utf8GetAux s 0 p
|
||||
| s => utf8GetAux s.data 0 p
|
||||
|
||||
def utf8SetAux (c' : Char) : List Char → Pos → Pos → List Char
|
||||
| [], _, _ => []
|
||||
@@ -214,7 +544,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_utf8_set"]
|
||||
def set : String → (@& Pos) → Char → String
|
||||
| ⟨s⟩, i, c => ⟨utf8SetAux c s 0 i⟩
|
||||
| s, i, c => (utf8SetAux c s.data 0 i).asString
|
||||
|
||||
/--
|
||||
Replaces the character at position `p` in the string `s` with the result of applying `f` to that
|
||||
@@ -270,7 +600,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_utf8_prev", expose]
|
||||
def prev : (@& String) → (@& Pos) → Pos
|
||||
| ⟨s⟩, p => utf8PrevAux s 0 p
|
||||
| s, p => utf8PrevAux s.data 0 p
|
||||
|
||||
/--
|
||||
Returns the first character in `s`. If `s = ""`, returns `(default : Char)`.
|
||||
@@ -336,7 +666,7 @@ Examples:
|
||||
@[extern "lean_string_utf8_get_fast", expose]
|
||||
def get' (s : @& String) (p : @& Pos) (h : ¬ s.atEnd p) : Char :=
|
||||
match s with
|
||||
| ⟨s⟩ => utf8GetAux s 0 p
|
||||
| s => utf8GetAux s.data 0 p
|
||||
|
||||
/--
|
||||
Returns the next position in a string after position `p`. The result is unspecified if `p` is not a
|
||||
@@ -360,16 +690,6 @@ def next' (s : @& String) (p : @& Pos) (h : ¬ s.atEnd p) : Pos :=
|
||||
let c := get s p
|
||||
p + c
|
||||
|
||||
theorem _root_.Char.utf8Size_pos (c : Char) : 0 < c.utf8Size := by
|
||||
repeat first | apply iteInduction (motive := (0 < ·)) <;> intros | decide
|
||||
|
||||
theorem _root_.Char.utf8Size_le_four (c : Char) : c.utf8Size ≤ 4 := by
|
||||
repeat first | apply iteInduction (motive := (· ≤ 4)) <;> intros | decide
|
||||
|
||||
theorem _root_.Char.utf8Size_eq (c : Char) : c.utf8Size = 1 ∨ c.utf8Size = 2 ∨ c.utf8Size = 3 ∨ c.utf8Size = 4 := by
|
||||
match c.utf8Size, c.utf8Size_pos, c.utf8Size_le_four with
|
||||
| 1, _, _ | 2, _, _ | 3, _, _ | 4, _, _ => simp
|
||||
|
||||
@[deprecated Char.utf8Size_pos (since := "2026-06-04")] abbrev one_le_csize := Char.utf8Size_pos
|
||||
|
||||
@[simp] theorem pos_lt_eq (p₁ p₂ : Pos) : (p₁ < p₂) = (p₁.1 < p₂.1) := rfl
|
||||
@@ -534,7 +854,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_utf8_extract", expose]
|
||||
def extract : (@& String) → (@& Pos) → (@& Pos) → String
|
||||
| ⟨s⟩, b, e => if b.byteIdx ≥ e.byteIdx then "" else ⟨go₁ s 0 b e⟩
|
||||
| s, b, e => if b.byteIdx ≥ e.byteIdx then "" else (go₁ s.data 0 b e).asString
|
||||
where
|
||||
go₁ : List Char → Pos → Pos → Pos → List Char
|
||||
| [], _, _, _ => []
|
||||
@@ -1030,37 +1350,31 @@ theorem utf8SetAux_of_gt (c' : Char) : ∀ (cs : List Char) {i p : Pos}, i > p
|
||||
theorem set_next_add (s : String) (i : Pos) (c : Char) (b₁ b₂)
|
||||
(h : (s.next i).1 + b₁ = s.endPos.1 + b₂) :
|
||||
((s.set i c).next i).1 + b₁ = (s.set i c).endPos.1 + b₂ := by
|
||||
simp [next, get, set, endPos, utf8ByteSize] at h ⊢
|
||||
simp [next, get, set, endPos, ← utf8ByteSize'_eq, utf8ByteSize'] at h ⊢
|
||||
rw [Nat.add_comm i.1, Nat.add_assoc] at h ⊢
|
||||
let rec foo : ∀ cs a b₁ b₂,
|
||||
(utf8GetAux cs a i).utf8Size + b₁ = utf8ByteSize.go cs + b₂ →
|
||||
(utf8GetAux (utf8SetAux c cs a i) a i).utf8Size + b₁ = utf8ByteSize.go (utf8SetAux c cs a i) + b₂
|
||||
(utf8GetAux cs a i).utf8Size + b₁ = utf8ByteSize'.go cs + b₂ →
|
||||
(utf8GetAux (utf8SetAux c cs a i) a i).utf8Size + b₁ = utf8ByteSize'.go (utf8SetAux c cs a i) + b₂
|
||||
| [], _, _, _, h => h
|
||||
| c'::cs, a, b₁, b₂, h => by
|
||||
unfold utf8SetAux
|
||||
apply iteInduction (motive := fun p => (utf8GetAux p a i).utf8Size + b₁ = utf8ByteSize.go p + b₂) <;>
|
||||
intro h' <;> simp [utf8GetAux, h', utf8ByteSize.go] at h ⊢
|
||||
apply iteInduction (motive := fun p => (utf8GetAux p a i).utf8Size + b₁ = utf8ByteSize'.go p + b₂) <;>
|
||||
intro h' <;> simp [utf8GetAux, h', utf8ByteSize'.go] at h ⊢
|
||||
next =>
|
||||
rw [Nat.add_assoc, Nat.add_left_comm] at h ⊢; rw [Nat.add_left_cancel h]
|
||||
next =>
|
||||
rw [Nat.add_assoc] at h ⊢
|
||||
refine foo cs (a + c') b₁ (c'.utf8Size + b₂) h
|
||||
exact foo s.1 0 _ _ h
|
||||
exact foo s.data 0 _ _ h
|
||||
|
||||
theorem mapAux_lemma (s : String) (i : Pos) (c : Char) (h : ¬s.atEnd i) :
|
||||
(s.set i c).endPos.1 - ((s.set i c).next i).1 < s.endPos.1 - i.1 :=
|
||||
(s.set i c).endPos.1 - ((s.set i c).next i).1 < s.endPos.1 - i.1 := by
|
||||
suffices (s.set i c).endPos.1 - ((s.set i c).next i).1 = s.endPos.1 - (s.next i).1 by
|
||||
rw [this]
|
||||
apply Nat.sub_lt_sub_left (Nat.gt_of_not_le (mt decide_eq_true h)) (lt_next ..)
|
||||
Nat.sub.elim (motive := (_ = ·)) _ _
|
||||
(fun _ k e =>
|
||||
have := set_next_add _ _ _ k 0 e.symm
|
||||
Nat.sub_eq_of_eq_add <| this.symm.trans <| Nat.add_comm ..)
|
||||
(fun h => by
|
||||
have ⟨k, e⟩ := Nat.le.dest h
|
||||
rw [Nat.succ_add] at e
|
||||
have : ((s.set i c).next i).1 = _ := set_next_add _ _ c 0 k.succ e.symm
|
||||
exact Nat.sub_eq_zero_of_le (this ▸ Nat.le_add_right ..))
|
||||
have := set_next_add s i c (s.endPos.byteIdx - (s.next i).byteIdx) 0
|
||||
have := set_next_add s i c 0 ((s.next i).byteIdx - s.endPos.byteIdx)
|
||||
omega
|
||||
|
||||
@[specialize] def mapAux (f : Char → Char) (i : Pos) (s : String) : String :=
|
||||
if h : s.atEnd i then s
|
||||
@@ -2044,40 +2358,51 @@ def stripSuffix (s : String) (suff : String) : String :=
|
||||
|
||||
end String
|
||||
|
||||
namespace Char
|
||||
|
||||
@[simp] theorem length_toString (c : Char) : c.toString.length = 1 := rfl
|
||||
|
||||
end Char
|
||||
|
||||
namespace String
|
||||
|
||||
@[ext]
|
||||
theorem ext {s₁ s₂ : String} (h : s₁.data = s₂.data) : s₁ = s₂ :=
|
||||
show ⟨s₁.data⟩ = (⟨s₂.data⟩ : String) from h ▸ rfl
|
||||
|
||||
theorem ext_iff {s₁ s₂ : String} : s₁ = s₂ ↔ s₁.data = s₂.data := ⟨fun h => h ▸ rfl, ext⟩
|
||||
data_injective h
|
||||
|
||||
@[simp] theorem default_eq : default = "" := rfl
|
||||
|
||||
@[simp] theorem length_mk (s : List Char) : (String.mk s).length = s.length := rfl
|
||||
@[simp]
|
||||
theorem String.mk_eq_asString (s : List Char) : String.mk s = List.asString s := rfl
|
||||
|
||||
@[simp] theorem length_empty : "".length = 0 := rfl
|
||||
@[simp]
|
||||
theorem _root_.List.length_asString (s : List Char) : (List.asString s).length = s.length := by
|
||||
rw [← length_data, List.data_asString]
|
||||
|
||||
@[simp] theorem length_singleton (c : Char) : (String.singleton c).length = 1 := rfl
|
||||
@[simp] theorem length_empty : "".length = 0 := by simp [← length_data, data_empty]
|
||||
|
||||
@[simp]
|
||||
theorem bytes_singleton {c : Char} : (String.singleton c).bytes = [c].utf8Encode := by
|
||||
simp [singleton]
|
||||
|
||||
theorem singleton_eq {c : Char} : String.singleton c = [c].asString := by
|
||||
simp [← bytes_inj]
|
||||
|
||||
@[simp] theorem data_singleton (c : Char) : (String.singleton c).data = [c] := by
|
||||
simp [singleton_eq]
|
||||
|
||||
@[simp]
|
||||
theorem length_singleton {c : Char} : (String.singleton c).length = 1 := by
|
||||
simp [← length_data]
|
||||
|
||||
theorem push_eq_append (c : Char) : String.push s c = s ++ singleton c := by
|
||||
simp [← bytes_inj]
|
||||
|
||||
@[simp] theorem data_push (c : Char) : (String.push s c).data = s.data ++ [c] := by
|
||||
simp [push_eq_append]
|
||||
|
||||
@[simp] theorem length_push (c : Char) : (String.push s c).length = s.length + 1 := by
|
||||
rw [push, length_mk, List.length_append, List.length_singleton, Nat.succ.injEq]
|
||||
rfl
|
||||
simp [← length_data]
|
||||
|
||||
@[simp] theorem length_pushn (c : Char) (n : Nat) : (pushn s c n).length = s.length + n := by
|
||||
unfold pushn; induction n <;> simp [Nat.repeat, Nat.add_assoc, *]
|
||||
|
||||
@[simp] theorem length_append (s t : String) : (s ++ t).length = s.length + t.length := by
|
||||
simp only [length, append, List.length_append]
|
||||
|
||||
@[simp] theorem data_push (s : String) (c : Char) : (s.push c).data = s.data ++ [c] := rfl
|
||||
|
||||
@[simp] theorem data_append (s t : String) : (s ++ t).data = s.data ++ t.data := rfl
|
||||
simp [← length_data]
|
||||
|
||||
attribute [simp] toList -- prefer `String.data` over `String.toList` in lemmas
|
||||
|
||||
@@ -2161,10 +2486,8 @@ end Pos
|
||||
theorem lt_next' (s : String) (p : Pos) : p < next s p := lt_next ..
|
||||
|
||||
@[simp] theorem prev_zero (s : String) : prev s 0 = 0 := by
|
||||
cases s with | mk cs
|
||||
cases cs
|
||||
next => rfl
|
||||
next => simp [prev, utf8PrevAux, Pos.le_iff]
|
||||
rw [prev]
|
||||
cases s.data <;> simp [utf8PrevAux, Pos.le_iff]
|
||||
|
||||
@[simp] theorem get'_eq (s : String) (p : Pos) (h) : get' s p h = get s p := rfl
|
||||
|
||||
@@ -2174,19 +2497,20 @@ theorem lt_next' (s : String) (p : Pos) : p < next s p := lt_next ..
|
||||
-- so for proving can be unfolded.
|
||||
attribute [simp] toSubstring'
|
||||
|
||||
theorem singleton_eq (c : Char) : singleton c = ⟨[c]⟩ := rfl
|
||||
|
||||
@[simp] theorem data_singleton (c : Char) : (singleton c).data = [c] := rfl
|
||||
|
||||
@[simp] theorem append_empty (s : String) : s ++ "" = s := ext (List.append_nil _)
|
||||
|
||||
@[simp] theorem empty_append (s : String) : "" ++ s = s := rfl
|
||||
|
||||
theorem append_assoc (s₁ s₂ s₃ : String) : (s₁ ++ s₂) ++ s₃ = s₁ ++ (s₂ ++ s₃) :=
|
||||
ext (List.append_assoc ..)
|
||||
ext (by simp [data_append])
|
||||
|
||||
end String
|
||||
|
||||
namespace Char
|
||||
|
||||
theorem toString_eq_singleton {c : Char} : c.toString = String.singleton c := rfl
|
||||
|
||||
@[simp] theorem length_toString (c : Char) : c.toString.length = 1 := by
|
||||
simp [toString_eq_singleton]
|
||||
|
||||
end Char
|
||||
|
||||
open String
|
||||
|
||||
namespace Substring
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
prelude
|
||||
public import Init.Data.List.Basic
|
||||
public import Init.Data.Char.Basic
|
||||
public import Init.Data.ByteArray.Bootstrap
|
||||
|
||||
public section
|
||||
|
||||
@@ -31,7 +32,11 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_string_push", expose]
|
||||
def push : String → Char → String
|
||||
| ⟨s⟩, c => ⟨s ++ [c]⟩
|
||||
| ⟨b, h⟩, c => ⟨b.append (List.utf8Encode [c]), ?pf⟩
|
||||
where finally
|
||||
have ⟨m, hm⟩ := h
|
||||
cases hm
|
||||
exact .intro (m ++ [c]) (by simp [List.utf8Encode, List.toByteArray_append'])
|
||||
|
||||
/--
|
||||
Returns a new string that contains only the character `c`.
|
||||
@@ -124,8 +129,21 @@ Examples:
|
||||
* `[].asString = ""`
|
||||
* `['a', 'a', 'a'].asString = "aaa"`
|
||||
-/
|
||||
@[extern "lean_string_mk", expose]
|
||||
def String.mk (data : List Char) : String :=
|
||||
⟨List.utf8Encode data,.intro data rfl⟩
|
||||
|
||||
/--
|
||||
Creates a string that contains the characters in a list, in order.
|
||||
|
||||
Examples:
|
||||
* `['L', '∃', '∀', 'N'].asString = "L∃∀N"`
|
||||
* `[].asString = ""`
|
||||
* `['a', 'a', 'a'].asString = "aaa"`
|
||||
-/
|
||||
@[expose]
|
||||
def List.asString (s : List Char) : String :=
|
||||
⟨s⟩
|
||||
String.mk s
|
||||
|
||||
namespace Substring.Internal
|
||||
|
||||
|
||||
1238
src/Init/Data/String/Decode.lean
Normal file
1238
src/Init/Data/String/Decode.lean
Normal file
File diff suppressed because it is too large
Load Diff
@@ -104,8 +104,7 @@ where
|
||||
|
||||
/--
|
||||
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
|
||||
the corresponding string. Invalid UTF-8 characters in the byte array result in `(default : Char)`,
|
||||
or `'A'`, in the string.
|
||||
the corresponding string.
|
||||
-/
|
||||
@[extern "lean_string_from_utf8_unchecked"]
|
||||
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
|
||||
@@ -133,92 +132,15 @@ the corresponding string, or panics if the array is not a valid UTF-8 encoding o
|
||||
@[inline] def fromUTF8! (a : ByteArray) : String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else panic! "invalid UTF-8 string"
|
||||
|
||||
/--
|
||||
Returns the sequence of bytes in a character's UTF-8 encoding.
|
||||
-/
|
||||
def utf8EncodeCharFast (c : Char) : List UInt8 :=
|
||||
let v := c.val
|
||||
if v ≤ 0x7f then
|
||||
[v.toUInt8]
|
||||
else if v ≤ 0x7ff then
|
||||
[(v >>> 6).toUInt8 &&& 0x1f ||| 0xc0,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else if v ≤ 0xffff then
|
||||
[(v >>> 12).toUInt8 &&& 0x0f ||| 0xe0,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else
|
||||
[(v >>> 18).toUInt8 &&& 0x07 ||| 0xf0,
|
||||
(v >>> 12).toUInt8 &&& 0x3f ||| 0x80,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
|
||||
private theorem Nat.add_two_pow_eq_or_of_lt {b : Nat} (i : Nat) (b_lt : b < 2 ^ i) (a : Nat) :
|
||||
b + 2 ^ i * a = b ||| 2 ^ i * a := by
|
||||
rw [Nat.add_comm, Nat.or_comm, Nat.two_pow_add_eq_or_of_lt b_lt]
|
||||
|
||||
@[csimp]
|
||||
theorem utf8EncodeChar_eq_utf8EncodeCharFast : @utf8EncodeChar = @utf8EncodeCharFast := by
|
||||
funext c
|
||||
simp only [utf8EncodeChar, utf8EncodeCharFast, UInt8.ofNat_uInt32ToNat, UInt8.ofNat_add,
|
||||
UInt8.reduceOfNat, UInt32.le_iff_toNat_le, UInt32.reduceToNat]
|
||||
split
|
||||
· rfl
|
||||
· split
|
||||
· simp only [List.cons.injEq, ← UInt8.toNat_inj, UInt8.toNat_add, UInt8.toNat_ofNat',
|
||||
Nat.reducePow, UInt8.reduceToNat, Nat.mod_add_mod, UInt8.toNat_or, UInt8.toNat_and,
|
||||
UInt32.toNat_toUInt8, UInt32.toNat_shiftRight, UInt32.reduceToNat, Nat.reduceMod, and_true]
|
||||
refine ⟨?_, ?_⟩
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 5 (by omega) 6,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 5, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_eq_of_lt (b := 256) (by omega)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.mod_mod_of_dvd _ (by decide)]
|
||||
· split
|
||||
· simp only [List.cons.injEq, ← UInt8.toNat_inj, UInt8.toNat_add, UInt8.toNat_ofNat',
|
||||
Nat.reducePow, UInt8.reduceToNat, Nat.mod_add_mod, UInt8.toNat_or, UInt8.toNat_and,
|
||||
UInt32.toNat_toUInt8, UInt32.toNat_shiftRight, UInt32.reduceToNat, Nat.reduceMod, and_true]
|
||||
refine ⟨?_, ?_, ?_⟩
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 4 (by omega) 14,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 4, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_eq_of_lt (b := 256) (by omega)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_mod_of_dvd (c.val.toNat / 2 ^ 6) (by decide)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.mod_mod_of_dvd c.val.toNat (by decide)]
|
||||
· simp only [List.cons.injEq, ← UInt8.toNat_inj, UInt8.toNat_add, UInt8.toNat_ofNat',
|
||||
Nat.reducePow, UInt8.reduceToNat, Nat.mod_add_mod, UInt8.toNat_or, UInt8.toNat_and,
|
||||
UInt32.toNat_toUInt8, UInt32.toNat_shiftRight, UInt32.reduceToNat, Nat.reduceMod, and_true]
|
||||
refine ⟨?_, ?_, ?_, ?_⟩
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 3 (by omega) 30,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 3, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_mod_of_dvd _ (by decide)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_mod_of_dvd (c.val.toNat / 2 ^ 12) (by decide)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.shiftRight_eq_div_pow,
|
||||
Nat.mod_mod_of_dvd (c.val.toNat / 2 ^ 6) (by decide)]
|
||||
· rw [Nat.mod_eq_of_lt (by omega), Nat.add_two_pow_eq_or_of_lt 6 (by omega) 2,
|
||||
Nat.and_two_pow_sub_one_eq_mod _ 6, Nat.mod_mod_of_dvd c.val.toNat (by decide)]
|
||||
|
||||
@[simp] theorem length_utf8EncodeChar (c : Char) : (utf8EncodeChar c).length = c.utf8Size := by
|
||||
simp [Char.utf8Size, utf8EncodeChar_eq_utf8EncodeCharFast, utf8EncodeCharFast]
|
||||
cases Decidable.em (c.val ≤ 0x7f) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0x7ff) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0xffff) <;> simp [*]
|
||||
|
||||
/--
|
||||
Encodes a string in UTF-8 as an array of bytes.
|
||||
-/
|
||||
@[extern "lean_string_to_utf8"]
|
||||
def toUTF8 (a : @& String) : ByteArray :=
|
||||
⟨⟨a.data.flatMap utf8EncodeChar⟩⟩
|
||||
a.bytes
|
||||
|
||||
@[simp] theorem size_toUTF8 (s : String) : s.toUTF8.size = s.utf8ByteSize := by
|
||||
simp [toUTF8, ByteArray.size, Array.size, utf8ByteSize, List.flatMap]
|
||||
induction s.data <;> simp [List.map, utf8ByteSize.go, Nat.add_comm, *]
|
||||
rfl
|
||||
|
||||
/--
|
||||
Accesses the indicated byte in the UTF-8 encoding of a string.
|
||||
|
||||
@@ -142,9 +142,9 @@ inductive LiftRel (r : α → γ → Prop) (s : β → δ → Prop) : α ⊕ β
|
||||
@[simp, grind =] theorem liftRel_inl_inl : LiftRel r s (inl a) (inl c) ↔ r a c :=
|
||||
⟨fun h => by cases h; assumption, LiftRel.inl⟩
|
||||
|
||||
@[simp, grind] theorem not_liftRel_inl_inr : ¬LiftRel r s (inl a) (inr d) := nofun
|
||||
@[simp, grind ←] theorem not_liftRel_inl_inr : ¬LiftRel r s (inl a) (inr d) := nofun
|
||||
|
||||
@[simp, grind] theorem not_liftRel_inr_inl : ¬LiftRel r s (inr b) (inl c) := nofun
|
||||
@[simp, grind ←] theorem not_liftRel_inr_inl : ¬LiftRel r s (inr b) (inl c) := nofun
|
||||
|
||||
@[simp, grind =] theorem liftRel_inr_inr : LiftRel r s (inr b) (inr d) ↔ s b d :=
|
||||
⟨fun h => by cases h; assumption, LiftRel.inr⟩
|
||||
@@ -179,7 +179,7 @@ attribute [simp] Lex.sep
|
||||
@[simp, grind =] theorem lex_inr_inr : Lex r s (inr b₁) (inr b₂) ↔ s b₁ b₂ :=
|
||||
⟨fun h => by cases h; assumption, Lex.inr⟩
|
||||
|
||||
@[simp, grind] theorem lex_inr_inl : ¬Lex r s (inr b) (inl a) := nofun
|
||||
@[simp, grind ←] theorem lex_inr_inl : ¬Lex r s (inr b) (inl a) := nofun
|
||||
|
||||
instance instDecidableRelSumLex [DecidableRel r] [DecidableRel s] : DecidableRel (Lex r s)
|
||||
| inl _, inl _ => decidable_of_iff' _ lex_inl_inl
|
||||
|
||||
@@ -7,7 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Meta
|
||||
import Init.Data.String.Extra
|
||||
public import Init.Data.String.Extra
|
||||
|
||||
/-!
|
||||
Here we give the. implementation of `Name.toString`. There is also a private implementation in
|
||||
|
||||
@@ -21,12 +21,7 @@ open Nat
|
||||
|
||||
/-- Converts a `Fin UInt8.size` into the corresponding `UInt8`. -/
|
||||
@[inline] def UInt8.ofFin (a : Fin UInt8.size) : UInt8 := ⟨⟨a⟩⟩
|
||||
@[deprecated UInt8.ofBitVec (since := "2025-02-12"), inherit_doc UInt8.ofBitVec]
|
||||
def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
|
||||
UInt8.ofBitVec bitVec
|
||||
@[inline, deprecated UInt8.ofNatLT (since := "2025-02-13"), inherit_doc UInt8.ofNatLT]
|
||||
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
|
||||
UInt8.ofNatLT n h
|
||||
|
||||
|
||||
/-- Converts an `Int` to a `UInt8` by taking the (non-negative remainder of the division by `2 ^ 8`. -/
|
||||
def UInt8.ofInt (x : Int) : UInt8 := ofNat (x % 2 ^ 8).toNat
|
||||
@@ -190,12 +185,6 @@ instance : Min UInt8 := minOfLe
|
||||
|
||||
/-- Converts a `Fin UInt16.size` into the corresponding `UInt16`. -/
|
||||
@[inline] def UInt16.ofFin (a : Fin UInt16.size) : UInt16 := ⟨⟨a⟩⟩
|
||||
@[deprecated UInt16.ofBitVec (since := "2025-02-12"), inherit_doc UInt16.ofBitVec]
|
||||
def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
|
||||
UInt16.ofBitVec bitVec
|
||||
@[inline, deprecated UInt16.ofNatLT (since := "2025-02-13"), inherit_doc UInt16.ofNatLT]
|
||||
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
|
||||
UInt16.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt16` by taking the (non-negative remainder of the division by `2 ^ 16`. -/
|
||||
def UInt16.ofInt (x : Int) : UInt16 := ofNat (x % 2 ^ 16).toNat
|
||||
@@ -406,12 +395,6 @@ instance : Min UInt16 := minOfLe
|
||||
|
||||
/-- Converts a `Fin UInt32.size` into the corresponding `UInt32`. -/
|
||||
@[inline] def UInt32.ofFin (a : Fin UInt32.size) : UInt32 := ⟨⟨a⟩⟩
|
||||
@[deprecated UInt32.ofBitVec (since := "2025-02-12"), inherit_doc UInt32.ofBitVec]
|
||||
def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
|
||||
UInt32.ofBitVec bitVec
|
||||
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
|
||||
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
|
||||
UInt32.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt32` by taking the (non-negative remainder of the division by `2 ^ 32`. -/
|
||||
def UInt32.ofInt (x : Int) : UInt32 := ofNat (x % 2 ^ 32).toNat
|
||||
@@ -585,12 +568,6 @@ def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
|
||||
|
||||
/-- Converts a `Fin UInt64.size` into the corresponding `UInt64`. -/
|
||||
@[inline] def UInt64.ofFin (a : Fin UInt64.size) : UInt64 := ⟨⟨a⟩⟩
|
||||
@[deprecated UInt64.ofBitVec (since := "2025-02-12"), inherit_doc UInt64.ofBitVec]
|
||||
def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
|
||||
UInt64.ofBitVec bitVec
|
||||
@[inline, deprecated UInt64.ofNatLT (since := "2025-02-13"), inherit_doc UInt64.ofNatLT]
|
||||
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
|
||||
UInt64.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `UInt64` by taking the (non-negative remainder of the division by `2 ^ 64`. -/
|
||||
def UInt64.ofInt (x : Int) : UInt64 := ofNat (x % 2 ^ 64).toNat
|
||||
@@ -799,12 +776,6 @@ instance : Min UInt64 := minOfLe
|
||||
|
||||
/-- Converts a `Fin USize.size` into the corresponding `USize`. -/
|
||||
@[inline] def USize.ofFin (a : Fin USize.size) : USize := ⟨⟨a⟩⟩
|
||||
@[deprecated USize.ofBitVec (since := "2025-02-12"), inherit_doc USize.ofBitVec]
|
||||
def USize.mk (bitVec : BitVec System.Platform.numBits) : USize :=
|
||||
USize.ofBitVec bitVec
|
||||
@[inline, deprecated USize.ofNatLT (since := "2025-02-13"), inherit_doc USize.ofNatLT]
|
||||
def USize.ofNatCore (n : Nat) (h : n < USize.size) : USize :=
|
||||
USize.ofNatLT n h
|
||||
|
||||
/-- Converts an `Int` to a `USize` by taking the (non-negative remainder of the division by `2 ^ numBits`. -/
|
||||
def USize.ofInt (x : Int) : USize := ofNat (x % 2 ^ System.Platform.numBits).toNat
|
||||
@@ -812,14 +783,6 @@ def USize.ofInt (x : Int) : USize := ofNat (x % 2 ^ System.Platform.numBits).toN
|
||||
@[simp] theorem USize.le_size : 2 ^ 32 ≤ USize.size := by cases USize.size_eq <;> simp_all
|
||||
@[simp] theorem USize.size_le : USize.size ≤ 2 ^ 64 := by cases USize.size_eq <;> simp_all
|
||||
|
||||
@[deprecated USize.size_le (since := "2025-02-24")]
|
||||
theorem usize_size_le : USize.size ≤ 18446744073709551616 :=
|
||||
USize.size_le
|
||||
|
||||
@[deprecated USize.le_size (since := "2025-02-24")]
|
||||
theorem le_usize_size : 4294967296 ≤ USize.size :=
|
||||
USize.le_size
|
||||
|
||||
/--
|
||||
Multiplies two word-sized unsigned integers, wrapping around on overflow. Usually accessed via the
|
||||
`*` operator.
|
||||
|
||||
@@ -26,8 +26,6 @@ open Nat
|
||||
|
||||
/-- Converts a `UInt8` into the corresponding `Fin UInt8.size`. -/
|
||||
def UInt8.toFin (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
|
||||
@[deprecated UInt8.toFin (since := "2025-02-12"), inherit_doc UInt8.toFin]
|
||||
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to an 8-bit unsigned integer, returning the largest representable value if
|
||||
@@ -67,8 +65,6 @@ instance UInt8.instOfNat : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
|
||||
/-- Converts a `UInt16` into the corresponding `Fin UInt16.size`. -/
|
||||
def UInt16.toFin (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
|
||||
@[deprecated UInt16.toFin (since := "2025-02-12"), inherit_doc UInt16.toFin]
|
||||
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to a 16-bit unsigned integer, wrapping on overflow.
|
||||
@@ -134,8 +130,6 @@ instance UInt16.instOfNat : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
|
||||
/-- Converts a `UInt32` into the corresponding `Fin UInt32.size`. -/
|
||||
def UInt32.toFin (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
|
||||
@[deprecated UInt32.toFin (since := "2025-02-12"), inherit_doc UInt32.toFin]
|
||||
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to a 32-bit unsigned integer, wrapping on overflow.
|
||||
@@ -149,8 +143,7 @@ Examples:
|
||||
-/
|
||||
@[extern "lean_uint32_of_nat"]
|
||||
def UInt32.ofNat (n : @& Nat) : UInt32 := ⟨BitVec.ofNat 32 n⟩
|
||||
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
|
||||
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := UInt32.ofNatLT n h
|
||||
|
||||
/--
|
||||
Converts a natural number to a 32-bit unsigned integer, returning the largest representable value if
|
||||
the number is too large.
|
||||
@@ -210,23 +203,14 @@ theorem UInt32.ofNatLT_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UIn
|
||||
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat,
|
||||
Fin.Internal.ofNat_eq_ofNat, Fin.ofNat, Nat.mod_eq_of_lt h2, imp_self]
|
||||
|
||||
@[deprecated UInt32.ofNatLT_lt_of_lt (since := "2025-02-13")]
|
||||
theorem UInt32.ofNat'_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
|
||||
n < m → UInt32.ofNatLT n h1 < UInt32.ofNat m := UInt32.ofNatLT_lt_of_lt h1 h2
|
||||
|
||||
theorem UInt32.lt_ofNatLT_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
|
||||
m < n → UInt32.ofNat m < UInt32.ofNatLT n h1 := by
|
||||
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat, Fin.Internal.ofNat_eq_ofNat,
|
||||
Fin.ofNat, Nat.mod_eq_of_lt h2, imp_self]
|
||||
|
||||
@[deprecated UInt32.lt_ofNatLT_of_lt (since := "2025-02-13")]
|
||||
theorem UInt32.lt_ofNat'_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
|
||||
m < n → UInt32.ofNat m < UInt32.ofNatLT n h1 := UInt32.lt_ofNatLT_of_lt h1 h2
|
||||
|
||||
/-- Converts a `UInt64` into the corresponding `Fin UInt64.size`. -/
|
||||
def UInt64.toFin (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
|
||||
@[deprecated UInt64.toFin (since := "2025-02-12"), inherit_doc UInt64.toFin]
|
||||
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts a natural number to a 64-bit unsigned integer, wrapping on overflow.
|
||||
|
||||
@@ -315,18 +299,9 @@ def UInt32.toUInt64 (a : UInt32) : UInt64 := ⟨⟨a.toNat, Nat.lt_trans a.toBit
|
||||
|
||||
instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
|
||||
@[deprecated USize.size_eq (since := "2025-02-24")]
|
||||
theorem usize_size_eq : USize.size = 4294967296 ∨ USize.size = 18446744073709551616 :=
|
||||
USize.size_eq
|
||||
|
||||
@[deprecated USize.size_pos (since := "2025-02-24")]
|
||||
theorem usize_size_pos : 0 < USize.size :=
|
||||
USize.size_pos
|
||||
|
||||
/-- Converts a `USize` into the corresponding `Fin USize.size`. -/
|
||||
def USize.toFin (x : USize) : Fin USize.size := x.toBitVec.toFin
|
||||
@[deprecated USize.toFin (since := "2025-02-12"), inherit_doc USize.toFin]
|
||||
def USize.val (x : USize) : Fin USize.size := x.toFin
|
||||
|
||||
/--
|
||||
Converts an arbitrary-precision natural number to an unsigned word-sized integer, wrapping around on
|
||||
overflow.
|
||||
|
||||
@@ -1327,3 +1327,14 @@ theorem UInt64.right_le_or {a b : UInt64} : b ≤ a ||| b := by
|
||||
simpa [UInt64.le_iff_toNat_le] using Nat.right_le_or
|
||||
theorem USize.right_le_or {a b : USize} : b ≤ a ||| b := by
|
||||
simpa [USize.le_iff_toNat_le] using Nat.right_le_or
|
||||
|
||||
theorem UInt8.and_lt_add_one {b c : UInt8} (h : c ≠ -1) : b &&& c < c + 1 :=
|
||||
UInt8.lt_of_le_of_lt UInt8.and_le_right (UInt8.lt_add_one h)
|
||||
theorem UInt16.and_lt_add_one {b c : UInt16} (h : c ≠ -1) : b &&& c < c + 1 :=
|
||||
UInt16.lt_of_le_of_lt UInt16.and_le_right (UInt16.lt_add_one h)
|
||||
theorem UInt32.and_lt_add_one {b c : UInt32} (h : c ≠ -1) : b &&& c < c + 1 :=
|
||||
UInt32.lt_of_le_of_lt UInt32.and_le_right (UInt32.lt_add_one h)
|
||||
theorem UInt64.and_lt_add_one {b c : UInt64} (h : c ≠ -1) : b &&& c < c + 1 :=
|
||||
UInt64.lt_of_le_of_lt UInt64.and_le_right (UInt64.lt_add_one h)
|
||||
theorem USize.and_lt_add_one {b c : USize} (h : c ≠ -1) : b &&& c < c + 1 :=
|
||||
USize.lt_of_le_of_lt USize.and_le_right (USize.lt_add_one h)
|
||||
|
||||
@@ -42,9 +42,6 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
|
||||
@[simp] theorem toNat_ofBitVec : (ofBitVec a).toNat = a.toNat := (rfl)
|
||||
|
||||
@[deprecated toNat_ofBitVec (since := "2025-02-12")]
|
||||
theorem toNat_mk : (ofBitVec a).toNat = a.toNat := (rfl)
|
||||
|
||||
@[simp] theorem toNat_ofNat' {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
|
||||
|
||||
-- Not `simp` because we have simprocs which will avoid the modulo.
|
||||
@@ -52,34 +49,14 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
|
||||
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
|
||||
|
||||
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
|
||||
theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
|
||||
|
||||
@[simp] theorem toFin_val (x : $typeName) : x.toFin.val = x.toNat := (rfl)
|
||||
@[deprecated toFin_val (since := "2025-02-12")]
|
||||
theorem val_val_eq_toNat (x : $typeName) : x.toFin.val = x.toNat := (rfl)
|
||||
|
||||
@[simp] theorem toNat_toBitVec (x : $typeName) : x.toBitVec.toNat = x.toNat := (rfl)
|
||||
@[simp] theorem toFin_toBitVec (x : $typeName) : x.toBitVec.toFin = x.toFin := (rfl)
|
||||
|
||||
@[deprecated toNat_toBitVec (since := "2025-02-21")]
|
||||
theorem toNat_toBitVec_eq_toNat (x : $typeName) : x.toBitVec.toNat = x.toNat := (rfl)
|
||||
|
||||
@[simp] theorem ofBitVec_toBitVec : ∀ (a : $typeName), ofBitVec a.toBitVec = a
|
||||
| ⟨_, _⟩ => rfl
|
||||
|
||||
@[deprecated ofBitVec_toBitVec (since := "2025-02-21")]
|
||||
theorem ofBitVec_toBitVec_eq : ∀ (a : $typeName), ofBitVec a.toBitVec = a :=
|
||||
ofBitVec_toBitVec
|
||||
|
||||
@[deprecated ofBitVec_toBitVec_eq (since := "2025-02-12")]
|
||||
theorem mk_toBitVec_eq : ∀ (a : $typeName), ofBitVec a.toBitVec = a
|
||||
| ⟨_, _⟩ => rfl
|
||||
|
||||
@[deprecated "Use `toNat_toBitVec` and `toNat_ofNat_of_lt`." (since := "2025-03-05")]
|
||||
theorem toBitVec_eq_of_lt {a : Nat} : a < size → (ofNat a).toBitVec.toNat = a :=
|
||||
Nat.mod_eq_of_lt
|
||||
|
||||
theorem toBitVec_ofNat' (n : Nat) : (ofNat n).toBitVec = BitVec.ofNat _ n := (rfl)
|
||||
|
||||
theorem toNat_ofNat_of_lt' {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
@@ -140,17 +117,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
protected theorem eq_of_toFin_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b := by
|
||||
rcases a with ⟨⟨_⟩⟩; rcases b with ⟨⟨_⟩⟩; simp_all [toFin]
|
||||
open $typeName (eq_of_toFin_eq) in
|
||||
@[deprecated eq_of_toFin_eq (since := "2025-02-12")]
|
||||
protected theorem eq_of_val_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b :=
|
||||
eq_of_toFin_eq h
|
||||
|
||||
open $typeName (eq_of_toFin_eq) in
|
||||
protected theorem toFin_inj {a b : $typeName} : a.toFin = b.toFin ↔ a = b :=
|
||||
Iff.intro eq_of_toFin_eq (congrArg toFin)
|
||||
open $typeName (toFin_inj) in
|
||||
@[deprecated toFin_inj (since := "2025-02-12")]
|
||||
protected theorem val_inj {a b : $typeName} : a.toFin = b.toFin ↔ a = b :=
|
||||
toFin_inj
|
||||
|
||||
open $typeName (eq_of_toBitVec_eq) in
|
||||
protected theorem toBitVec_ne_of_ne {a b : $typeName} (h : a ≠ b) : a.toBitVec ≠ b.toBitVec :=
|
||||
@@ -236,8 +206,6 @@ instance : LawfulOrderLT $typeName where
|
||||
|
||||
@[simp]
|
||||
theorem toFin_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := (rfl)
|
||||
@[deprecated toFin_ofNat (since := "2025-02-12")]
|
||||
theorem val_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := (rfl)
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := (rfl)
|
||||
@@ -245,9 +213,6 @@ instance : LawfulOrderLT $typeName where
|
||||
@[simp]
|
||||
theorem ofBitVec_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := (rfl)
|
||||
|
||||
@[deprecated ofBitVec_ofNat (since := "2025-02-12")]
|
||||
theorem mk_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := (rfl)
|
||||
|
||||
@[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)
|
||||
@@ -3163,3 +3128,15 @@ protected theorem USize.sub_lt {a b : USize} (hb : 0 < b) (hab : b ≤ a) : a -
|
||||
rw [lt_iff_toNat_lt, USize.toNat_sub_of_le _ _ hab]
|
||||
refine Nat.sub_lt ?_ (USize.lt_iff_toNat_lt.1 hb)
|
||||
exact USize.lt_iff_toNat_lt.1 (USize.lt_of_lt_of_le hb hab)
|
||||
|
||||
theorem UInt8.lt_add_one {c : UInt8} (h : c ≠ -1) : c < c + 1 :=
|
||||
UInt8.lt_iff_toBitVec_lt.2 (BitVec.lt_add_one (by simpa [← UInt8.toBitVec_inj] using h))
|
||||
theorem UInt16.lt_add_one {c : UInt16} (h : c ≠ -1) : c < c + 1 :=
|
||||
UInt16.lt_iff_toBitVec_lt.2 (BitVec.lt_add_one (by simpa [← UInt16.toBitVec_inj] using h))
|
||||
theorem UInt32.lt_add_one {c : UInt32} (h : c ≠ -1) : c < c + 1 :=
|
||||
UInt32.lt_iff_toBitVec_lt.2 (BitVec.lt_add_one (by simpa [← UInt32.toBitVec_inj] using h))
|
||||
theorem UInt64.lt_add_one {c : UInt64} (h : c ≠ -1) : c < c + 1 :=
|
||||
UInt64.lt_iff_toBitVec_lt.2 (BitVec.lt_add_one (by simpa [← UInt64.toBitVec_inj] using h))
|
||||
theorem USize.lt_add_one {c : USize} (h : c ≠ -1) : c < c + 1 :=
|
||||
USize.lt_iff_toBitVec_lt.2 (BitVec.lt_add_one
|
||||
(by simpa [← USize.toBitVec_inj, BitVec.neg_one_eq_allOnes] using h))
|
||||
|
||||
@@ -172,9 +172,6 @@ theorem attach_map_val (xs : Vector α n) (f : α → β) :
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
-- The argument `xs : Vector α n` is explicit to allow rewriting from right to left.
|
||||
theorem attach_map_subtype_val (xs : Vector α n) : xs.attach.map Subtype.val = xs := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
@@ -185,15 +182,12 @@ theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Vector α n}
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} {xs : Vector α n} (H : ∀ a ∈ xs, p a) :
|
||||
(xs.attachWith p H).map Subtype.val = xs := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind ←]
|
||||
theorem mem_attach (xs : Vector α n) : ∀ x, x ∈ xs.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
@@ -345,9 +339,6 @@ theorem map_attach_eq_pmap {xs : Vector α n} {f : { x // x ∈ xs } → β} :
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
@[grind =]
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f : ∀ b, q b → γ} {xs : Vector α n} (H₁ H₂) :
|
||||
pmap f (pmap g xs H₁) H₂ =
|
||||
|
||||
@@ -907,6 +907,8 @@ theorem getElem?_singleton {a : α} {i : Nat} : #v[a][i]? = if i = 0 then some a
|
||||
|
||||
grind_pattern getElem_mem => xs[i] ∈ xs
|
||||
|
||||
|
||||
@[grind ←]
|
||||
theorem not_mem_empty (a : α) : ¬ a ∈ #v[] := nofun
|
||||
|
||||
@[simp, grind =] theorem mem_push {xs : Vector α n} {x y : α} : x ∈ xs.push y ↔ x ∈ xs ∨ x = y := by
|
||||
|
||||
@@ -250,11 +250,6 @@ theorem getElem_of_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx
|
||||
(c[i]? = some c[i]) ↔ True := by
|
||||
simp [h]
|
||||
|
||||
@[deprecated getElem?_eq_none_iff (since := "2025-02-17")]
|
||||
abbrev getElem?_eq_none := @getElem?_eq_none_iff
|
||||
|
||||
|
||||
|
||||
@[simp, grind =] theorem isSome_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isSome = dom c i := by
|
||||
simp only [getElem?_def]
|
||||
|
||||
@@ -23,3 +23,4 @@ public import Init.Grind.ToIntLemmas
|
||||
public import Init.Grind.Attr
|
||||
public import Init.Data.Int.OfNat -- This may not have otherwise been imported, breaking `grind` proofs.
|
||||
public import Init.Grind.AC
|
||||
public import Init.Grind.Injective
|
||||
|
||||
@@ -10,6 +10,8 @@ public import Init.Core
|
||||
public import Init.Data.Nat.Lemmas
|
||||
public import Init.Data.RArray
|
||||
public import Init.Data.Bool
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
@[expose] public section
|
||||
|
||||
namespace Lean.Grind.AC
|
||||
@@ -38,7 +40,7 @@ attribute [local simp] Expr.denote_var Expr.denote_op
|
||||
inductive Seq where
|
||||
| var (x : Var)
|
||||
| cons (x : Var) (s : Seq)
|
||||
deriving Inhabited, Repr, BEq
|
||||
deriving Inhabited, Repr, BEq, ReflBEq, LawfulBEq
|
||||
|
||||
-- Kernel version for Seq.beq
|
||||
noncomputable def Seq.beq' (s₁ : Seq) : Seq → Bool :=
|
||||
@@ -55,12 +57,6 @@ theorem Seq.beq'_eq (s₁ s₂ : Seq) : s₁.beq' s₂ = (s₁ = s₂) := by
|
||||
|
||||
attribute [local simp] Seq.beq'_eq
|
||||
|
||||
instance : LawfulBEq Seq where
|
||||
eq_of_beq {a} := by
|
||||
induction a <;> intro b <;> cases b <;> simp! [BEq.beq]
|
||||
next x₁ s₁ ih x₂ s₂ => intro h₁ h₂; simp [h₁, ih h₂]
|
||||
rfl := by intro a; induction a <;> simp! [BEq.beq]; assumption
|
||||
|
||||
noncomputable def Seq.denote {α} (ctx : Context α) (s : Seq) : α :=
|
||||
Seq.rec (fun x => x.denote ctx) (fun x _ ih => ctx.op (x.denote ctx) ih) s
|
||||
|
||||
|
||||
@@ -98,31 +98,45 @@ syntax grindEqBwd := patternIgnore(atomic("←" "=") <|> atomic("<-" "="))
|
||||
The `←` modifier instructs `grind` to select a multi-pattern from the conclusion of theorem.
|
||||
In other words, `grind` will use the theorem for backwards reasoning.
|
||||
This may fail if not all of the arguments to the theorem appear in the conclusion.
|
||||
Each time it encounters a subexpression which covers an argument which was not
|
||||
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
|
||||
If `grind!` is used, then only minimal indexable subexpressions are considered.
|
||||
-/
|
||||
syntax grindBwd := patternIgnore("←" <|> "<-") (grindGen)?
|
||||
/--
|
||||
The `→` modifier instructs `grind` to select a multi-pattern from the hypotheses of the theorem.
|
||||
In other words, `grind` will use the theorem for forwards reasoning.
|
||||
To generate a pattern, it traverses the hypotheses of the theorem from left to right.
|
||||
Each time it encounters a minimal indexable subexpression which covers an argument which was not
|
||||
Each time it encounters a subexpression which covers an argument which was not
|
||||
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
|
||||
If `grind!` is used, then only minimal indexable subexpressions are considered.
|
||||
-/
|
||||
syntax grindFwd := patternIgnore("→" <|> "->")
|
||||
/--
|
||||
The `⇐` modifier instructs `grind` to select a multi-pattern by traversing the conclusion, and then
|
||||
all the hypotheses from right to left.
|
||||
Each time it encounters a minimal indexable subexpression which covers an argument which was not
|
||||
Each time it encounters a subexpression which covers an argument which was not
|
||||
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
|
||||
If `grind!` is used, then only minimal indexable subexpressions are considered.
|
||||
-/
|
||||
syntax grindRL := patternIgnore("⇐" <|> "<=")
|
||||
/--
|
||||
The `⇒` modifier instructs `grind` to select a multi-pattern by traversing all the hypotheses from
|
||||
left to right, followed by the conclusion.
|
||||
Each time it encounters a minimal indexable subexpression which covers an argument which was not
|
||||
Each time it encounters a subexpression which covers an argument which was not
|
||||
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
|
||||
If `grind!` is used, then only minimal indexable subexpressions are considered.
|
||||
-/
|
||||
syntax grindLR := patternIgnore("⇒" <|> "=>")
|
||||
/--
|
||||
The `.` modifier instructs `grind` to select a multi-pattern by traversing the conclusion of the
|
||||
theorem, and then the hypotheses from eft to right. We say this is the default modifier.
|
||||
Each time it encounters a subexpression which covers an argument which was not
|
||||
previously covered, it adds that subexpression as a pattern, until all arguments have been covered.
|
||||
If `grind!` is used, then only minimal indexable subexpressions are considered.
|
||||
-/
|
||||
syntax grindDef := patternIgnore("." <|> "·") (grindGen)?
|
||||
/--
|
||||
The `usr` modifier indicates that this theorem was applied using a
|
||||
**user-defined instantiation pattern**. Such patterns are declared with
|
||||
the `grind_pattern` command, which lets you specify how `grind` should
|
||||
@@ -168,6 +182,12 @@ available extensionality theorems whose matches the type of `a` and `b`.
|
||||
-/
|
||||
syntax grindExt := &"ext"
|
||||
/--
|
||||
The `inj` modifier marks injectivity theorems for use by `grind`.
|
||||
The conclusion of the theorem must be of the form `Function.Injective f`
|
||||
where the term `f` contains at least one constant symbol.
|
||||
-/
|
||||
syntax grindInj := &"inj"
|
||||
/--
|
||||
`symbol <prio>` sets the priority of a constant for `grind`’s pattern-selection
|
||||
procedure. `grind` prefers patterns that contain higher-priority symbols.
|
||||
Example:
|
||||
@@ -193,8 +213,49 @@ syntax grindSym := &"symbol" ppSpace prio
|
||||
syntax grindMod :=
|
||||
grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd
|
||||
<|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager
|
||||
<|> grindCases <|> grindIntro <|> grindExt <|> grindGen <|> grindSym
|
||||
<|> grindCases <|> grindIntro <|> grindExt <|> grindGen <|> grindSym <|> grindInj
|
||||
<|> grindDef
|
||||
|
||||
/--
|
||||
Marks a theorem or definition for use by the `grind` tactic.
|
||||
|
||||
An optional modifier (e.g. `=`, `→`, `←`, `cases`, `intro`, `ext`, `inj`, etc.)
|
||||
controls how `grind` uses the declaration:
|
||||
* whether it is applied forwards, backwards, or both,
|
||||
* whether equalities are used on the left, right, or both sides,
|
||||
* whether case-splits, constructors, extensionality, or injectivity are applied,
|
||||
* or whether custom instantiation patterns are used.
|
||||
|
||||
See the individual modifier docstrings for details.
|
||||
-/
|
||||
syntax (name := grind) "grind" (ppSpace grindMod)? : attr
|
||||
/--
|
||||
Like `@[grind]`, but enforces the **minimal indexable subexpression condition**:
|
||||
when several subterms cover the same free variables, `grind!` chooses the smallest one.
|
||||
|
||||
This influences E-matching pattern selection.
|
||||
|
||||
### Example
|
||||
```lean
|
||||
theorem fg_eq (h : x > 0) : f (g x) = x
|
||||
|
||||
@[grind <-] theorem fg_eq (h : x > 0) : f (g x) = x
|
||||
-- Pattern selected: `f (g x)`
|
||||
|
||||
-- With minimal subexpression:
|
||||
@[grind! <-] theorem fg_eq (h : x > 0) : f (g x) = x
|
||||
-- Pattern selected: `g x`
|
||||
-/
|
||||
syntax (name := grind!) "grind!" (ppSpace grindMod)? : attr
|
||||
/--
|
||||
Like `@[grind]`, but also prints the pattern(s) selected by `grind`
|
||||
as info messages. Useful for debugging annotations and modifiers.
|
||||
-/
|
||||
syntax (name := grind?) "grind?" (ppSpace grindMod)? : attr
|
||||
/--
|
||||
Like `@[grind!]`, but also prints the pattern(s) selected by `grind`
|
||||
as info messages. Combines minimal subexpression selection with debugging output.
|
||||
-/
|
||||
syntax (name := grind!?) "grind!?" (ppSpace grindMod)? : attr
|
||||
end Attr
|
||||
end Lean.Parser
|
||||
|
||||
41
src/Init/Grind/Injective.lean
Normal file
41
src/Init/Grind/Injective.lean
Normal file
@@ -0,0 +1,41 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
prelude
|
||||
public import Init.Data.Function
|
||||
public import Init.Classical
|
||||
public section
|
||||
namespace Lean.Grind
|
||||
open Function
|
||||
|
||||
theorem _root_.Function.Injective.leftInverse
|
||||
{α β} (f : α → β) (hf : Injective f) [hα : Nonempty α] :
|
||||
∃ g : β → α, LeftInverse g f := by
|
||||
classical
|
||||
cases hα; next a0 =>
|
||||
let g : β → α := fun b =>
|
||||
if h : ∃ a, f a = b then Classical.choose h else a0
|
||||
exists g
|
||||
intro a
|
||||
have h : ∃ a', f a' = f a := ⟨a, rfl⟩
|
||||
have hfa : f (Classical.choose h) = f a := Classical.choose_spec h
|
||||
have : Classical.choose h = a := hf hfa
|
||||
simp [g, h, this]
|
||||
|
||||
noncomputable def leftInv {α : Sort u} {β : Sort v} (f : α → β) (hf : Injective f) [Nonempty α] : β → α :=
|
||||
Classical.choose (hf.leftInverse f)
|
||||
|
||||
theorem leftInv_eq {α : Sort u} {β : Sort v} (f : α → β) (hf : Injective f) [Nonempty α] (a : α) : leftInv f hf (f a) = a :=
|
||||
Classical.choose_spec (hf.leftInverse f) a
|
||||
|
||||
@[app_unexpander leftInv]
|
||||
meta def leftInvUnexpander : PrettyPrinter.Unexpander := fun stx => do
|
||||
match stx with
|
||||
| `($_ $f:term $_) => `($f⁻¹)
|
||||
| `($_ $f:term $_ $a:term) => `($f⁻¹ $a)
|
||||
| _ => throw ()
|
||||
|
||||
end Lean.Grind
|
||||
@@ -182,7 +182,7 @@ init_grind_norm
|
||||
Nat.add_eq Nat.sub_eq Nat.mul_eq Nat.zero_eq Nat.le_eq
|
||||
Nat.div_zero Nat.mod_zero Nat.div_one Nat.mod_one
|
||||
Nat.sub_sub Nat.pow_zero Nat.pow_one Nat.sub_self
|
||||
Nat.one_pow Nat.zero_sub
|
||||
Nat.one_pow Nat.zero_sub Nat.sub_zero
|
||||
-- Int
|
||||
Int.lt_eq
|
||||
Int.emod_neg Int.ediv_neg
|
||||
|
||||
@@ -13,6 +13,7 @@ import all Init.Data.Ord.Basic
|
||||
public import Init.Data.AC
|
||||
import all Init.Data.AC
|
||||
public import Init.Data.RArray
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
@[expose] public section
|
||||
|
||||
@@ -55,7 +56,7 @@ def Expr.denote {α} [IntModule α] (ctx : Context α) : Expr → α
|
||||
inductive Poly where
|
||||
| nil
|
||||
| add (k : Int) (v : Var) (p : Poly)
|
||||
deriving BEq, Repr
|
||||
deriving BEq, ReflBEq, LawfulBEq, Repr
|
||||
|
||||
def Poly.denote {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
|
||||
match p with
|
||||
@@ -233,18 +234,6 @@ theorem Expr.denote_norm {α} [IntModule α] (ctx : Context α) (e : Expr) : e.n
|
||||
simp [norm, toPoly', Expr.denote_toPoly'_go, Poly.denote]
|
||||
|
||||
attribute [local simp] Expr.denote_norm
|
||||
|
||||
instance : LawfulBEq Poly where
|
||||
eq_of_beq {a} := by
|
||||
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
|
||||
next ih =>
|
||||
intro _ _ h
|
||||
exact ih h
|
||||
rfl := by
|
||||
intro a
|
||||
induction a <;> simp! [BEq.beq]
|
||||
assumption
|
||||
|
||||
attribute [local simp] Poly.denote'_eq_denote
|
||||
|
||||
def Poly.leadCoeff (p : Poly) : Int :=
|
||||
|
||||
@@ -7,7 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.NotationExtra
|
||||
meta import Init.Data.String.Basic
|
||||
public meta import Init.Data.String.Basic
|
||||
|
||||
public section
|
||||
|
||||
|
||||
@@ -10,70 +10,59 @@ public import Init.Data.Hashable
|
||||
public import Init.Data.RArray
|
||||
public import Init.Grind.Ring.CommSolver
|
||||
@[expose] public section
|
||||
namespace Lean.Grind.Ring.OfSemiring
|
||||
/-!
|
||||
Helper definitions and theorems for converting `Semiring` expressions into `Ring` ones.
|
||||
We use them to implement `grind`
|
||||
-/
|
||||
abbrev Var := Nat
|
||||
inductive Expr where
|
||||
| num (v : Nat)
|
||||
| var (i : Var)
|
||||
| add (a b : Expr)
|
||||
| mul (a b : Expr)
|
||||
| pow (a : Expr) (k : Nat)
|
||||
deriving Inhabited, BEq, Hashable
|
||||
namespace Lean.Grind
|
||||
namespace CommRing
|
||||
|
||||
abbrev Context (α : Type u) := RArray α
|
||||
def Expr.denoteS {α} [Semiring α] (ctx : Context α) : Expr → α
|
||||
| .num k => OfNat.ofNat (α := α) k.natAbs
|
||||
| .natCast k => OfNat.ofNat (α := α) k
|
||||
| .var v => v.denote ctx
|
||||
| .add a b => denoteS ctx a + denoteS ctx b
|
||||
| .mul a b => denoteS ctx a * denoteS ctx b
|
||||
| .pow a k => denoteS ctx a ^ k
|
||||
| .sub .. | .neg .. | .intCast .. => 0
|
||||
|
||||
def Var.denote {α} (ctx : Context α) (v : Var) : α :=
|
||||
ctx.get v
|
||||
def Expr.denoteSAsRing {α} [Semiring α] (ctx : Context α) : Expr → Ring.OfSemiring.Q α
|
||||
| .num k => OfNat.ofNat (α := Ring.OfSemiring.Q α) k.natAbs
|
||||
| .natCast k => OfNat.ofNat (α := Ring.OfSemiring.Q α) k
|
||||
| .var v => Ring.OfSemiring.toQ (v.denote ctx)
|
||||
| .add a b => denoteSAsRing ctx a + denoteSAsRing ctx b
|
||||
| .mul a b => denoteSAsRing ctx a * denoteSAsRing ctx b
|
||||
| .pow a k => denoteSAsRing ctx a ^ k
|
||||
| .sub .. | .neg .. | .intCast .. => 0
|
||||
|
||||
def Expr.denote {α} [Semiring α] (ctx : Context α) : Expr → α
|
||||
| .num k => OfNat.ofNat (α := α) k
|
||||
| .var v => v.denote ctx
|
||||
| .add a b => denote ctx a + denote ctx b
|
||||
| .mul a b => denote ctx a * denote ctx b
|
||||
| .pow a k => denote ctx a ^ k
|
||||
attribute [local simp] Ring.OfSemiring.toQ_add Ring.OfSemiring.toQ_mul Ring.OfSemiring.toQ_ofNat
|
||||
Ring.OfSemiring.toQ_pow Ring.OfSemiring.toQ_zero in
|
||||
theorem Expr.denoteAsRing_eq {α} [Semiring α] (ctx : Context α) (e : Expr) : e.denoteSAsRing ctx = Ring.OfSemiring.toQ (e.denoteS ctx) := by
|
||||
induction e <;> simp [denoteS, denoteSAsRing, *]
|
||||
|
||||
attribute [local instance] ofSemiring
|
||||
|
||||
def Expr.denoteAsRing {α} [Semiring α] (ctx : Context α) : Expr → Q α
|
||||
| .num k => OfNat.ofNat (α := Q α) k
|
||||
| .var v => toQ (v.denote ctx)
|
||||
| .add a b => denoteAsRing ctx a + denoteAsRing ctx b
|
||||
| .mul a b => denoteAsRing ctx a * denoteAsRing ctx b
|
||||
| .pow a k => denoteAsRing ctx a ^ k
|
||||
|
||||
attribute [local simp] toQ_add toQ_mul toQ_ofNat toQ_pow
|
||||
|
||||
theorem Expr.denoteAsRing_eq {α} [Semiring α] (ctx : Context α) (e : Expr) : e.denoteAsRing ctx = toQ (e.denote ctx) := by
|
||||
induction e <;> simp [denote, denoteAsRing, *]
|
||||
|
||||
theorem of_eq {α} [Semiring α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: lhs.denote ctx = rhs.denote ctx → lhs.denoteAsRing ctx = rhs.denoteAsRing ctx := by
|
||||
intro h; replace h := congrArg toQ h
|
||||
simpa [← Expr.denoteAsRing_eq] using h
|
||||
|
||||
theorem of_diseq {α} [Semiring α] [AddRightCancel α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: lhs.denote ctx ≠ rhs.denote ctx → lhs.denoteAsRing ctx ≠ rhs.denoteAsRing ctx := by
|
||||
intro h₁ h₂
|
||||
simp [Expr.denoteAsRing_eq] at h₂
|
||||
replace h₂ := toQ_inj h₂
|
||||
contradiction
|
||||
|
||||
def Expr.toPoly : Expr → CommRing.Poly
|
||||
| .num n => .num n
|
||||
def Expr.toPolyS : Expr → CommRing.Poly
|
||||
| .num n => .num n.natAbs
|
||||
| .var x => CommRing.Poly.ofVar x
|
||||
| .add a b => a.toPoly.combine b.toPoly
|
||||
| .mul a b => a.toPoly.mul b.toPoly
|
||||
| .add a b => a.toPolyS.combine b.toPolyS
|
||||
| .mul a b => a.toPolyS.mul b.toPolyS
|
||||
| .pow a k =>
|
||||
match a with
|
||||
| .num n => .num (n^k)
|
||||
| .num n => .num (n.natAbs ^ k)
|
||||
| .var x => CommRing.Poly.ofMon (.mult {x, k} .unit)
|
||||
| _ => a.toPoly.pow k
|
||||
| _ => a.toPolyS.pow k
|
||||
| .natCast n => .num n
|
||||
| .sub .. | .neg .. | .intCast .. => .num 0
|
||||
|
||||
end Ring.OfSemiring
|
||||
def Expr.toPolyS_nc : Expr → CommRing.Poly
|
||||
| .num n => .num n.natAbs
|
||||
| .var x => CommRing.Poly.ofVar x
|
||||
| .add a b => a.toPolyS_nc.combine b.toPolyS_nc
|
||||
| .mul a b => a.toPolyS_nc.mul_nc b.toPolyS_nc
|
||||
| .pow a k =>
|
||||
match a with
|
||||
| .num n => .num (n.natAbs ^ k)
|
||||
| .var x => CommRing.Poly.ofMon (.mult {x, k} .unit)
|
||||
| _ => a.toPolyS_nc.pow_nc k
|
||||
| .natCast n => .num n
|
||||
| .sub .. | .neg .. | .intCast .. => .num 0
|
||||
|
||||
end CommRing
|
||||
|
||||
namespace CommRing
|
||||
attribute [local instance] Semiring.natCast Ring.intCast
|
||||
@@ -106,15 +95,15 @@ def Poly.denoteS [Semiring α] (ctx : Context α) (p : Poly) : α :=
|
||||
|
||||
attribute [local simp] natCast_one natCast_zero zero_mul mul_zero one_mul mul_one add_zero zero_add denoteSInt_eq
|
||||
|
||||
theorem Poly.denoteS_ofMon {α} [CommSemiring α] (ctx : Context α) (m : Mon)
|
||||
theorem Poly.denoteS_ofMon {α} [Semiring α] (ctx : Context α) (m : Mon)
|
||||
: denoteS ctx (ofMon m) = m.denote ctx := by
|
||||
simp [ofMon, denoteS]
|
||||
|
||||
theorem Poly.denoteS_ofVar {α} [CommSemiring α] (ctx : Context α) (x : Var)
|
||||
theorem Poly.denoteS_ofVar {α} [Semiring α] (ctx : Context α) (x : Var)
|
||||
: denoteS ctx (ofVar x) = x.denote ctx := by
|
||||
simp [ofVar, denoteS_ofMon, Mon.denote_ofVar]
|
||||
|
||||
theorem Poly.denoteS_addConst {α} [CommSemiring α] (ctx : Context α) (p : Poly) (k : Int)
|
||||
theorem Poly.denoteS_addConst {α} [Semiring α] (ctx : Context α) (p : Poly) (k : Int)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (addConst p k).denoteS ctx = p.denoteS ctx + k.toNat := by
|
||||
simp [addConst, cond_eq_if]; split
|
||||
next => subst k; simp
|
||||
@@ -127,7 +116,7 @@ theorem Poly.denoteS_addConst {α} [CommSemiring α] (ctx : Context α) (p : Pol
|
||||
intro _ h; cases h
|
||||
next h₁ h₂ => simp [*, add_assoc]
|
||||
|
||||
theorem Poly.denoteS_insert {α} [CommSemiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
|
||||
theorem Poly.denoteS_insert {α} [Semiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (insert k m p).denoteS ctx = k.toNat * m.denote ctx + p.denoteS ctx := by
|
||||
simp [insert, cond_eq_if] <;> split
|
||||
next => simp [*]
|
||||
@@ -154,13 +143,13 @@ theorem Poly.denoteS_insert {α} [CommSemiring α] (ctx : Context α) (k : Int)
|
||||
intro hk hn; cases hn; rename_i hn₁ hn₂
|
||||
rw [ih hk hn₂, add_left_comm]
|
||||
|
||||
theorem Poly.denoteS_concat {α} [CommSemiring α] (ctx : Context α) (p₁ p₂ : Poly)
|
||||
theorem Poly.denoteS_concat {α} [Semiring α] (ctx : Context α) (p₁ p₂ : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → (concat p₁ p₂).denoteS ctx = p₁.denoteS ctx + p₂.denoteS ctx := by
|
||||
fun_induction concat <;> intro h₁ h₂; simp [*, denoteS]
|
||||
next => cases h₁; rw [add_comm, denoteS_addConst] <;> assumption
|
||||
next ih => cases h₁; next hn₁ hn₂ => rw [denoteS, denoteS, ih hn₂ h₂, add_assoc]
|
||||
|
||||
theorem Poly.denoteS_mulConst {α} [CommSemiring α] (ctx : Context α) (k : Int) (p : Poly)
|
||||
theorem Poly.denoteS_mulConst {α} [Semiring α] (ctx : Context α) (k : Int) (p : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (mulConst k p).denoteS ctx = k.toNat * p.denoteS ctx := by
|
||||
simp [mulConst, cond_eq_if] <;> split
|
||||
next => simp [denoteS, *, zero_mul]
|
||||
@@ -174,30 +163,7 @@ theorem Poly.denoteS_mulConst {α} [CommSemiring α] (ctx : Context α) (k : Int
|
||||
intro h₁ h₂; cases h₂; rename_i h₂ h₃
|
||||
rw [Int.toNat_mul, natCast_mul, left_distrib, mul_assoc, ih h₁ h₃] <;> assumption
|
||||
|
||||
theorem Poly.denoteS_mulMon {α} [CommSemiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (mulMon k m p).denoteS ctx = k.toNat * m.denote ctx * p.denoteS ctx := by
|
||||
simp [mulMon, cond_eq_if] <;> split
|
||||
next => simp [denoteS, *]
|
||||
next =>
|
||||
split
|
||||
next h =>
|
||||
intro h₁ h₂
|
||||
simp at h; simp [*, Mon.denote, denoteS_mulConst _ _ _ h₁ h₂]
|
||||
next =>
|
||||
fun_induction mulMon.go <;> simp [denoteS, *]
|
||||
next h => simp +zetaDelta at h; simp [*]
|
||||
next =>
|
||||
intro h₁ h₂; cases h₂
|
||||
rw [Int.toNat_mul]
|
||||
simp [natCast_mul, CommSemiring.mul_comm, CommSemiring.mul_left_comm, mul_assoc]
|
||||
assumption; assumption
|
||||
next ih =>
|
||||
intro h₁ h₂; cases h₂; rename_i h₂ h₃
|
||||
rw [Int.toNat_mul]
|
||||
simp [Mon.denote_mul, natCast_mul, left_distrib, CommSemiring.mul_left_comm, mul_assoc, ih h₁ h₃]
|
||||
assumption; assumption
|
||||
|
||||
theorem Poly.denoteS_combine {α} [CommSemiring α] (ctx : Context α) (p₁ p₂ : Poly)
|
||||
theorem Poly.denoteS_combine {α} [Semiring α] (ctx : Context α) (p₁ p₂ : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → (combine p₁ p₂).denoteS ctx = p₁.denoteS ctx + p₂.denoteS ctx := by
|
||||
unfold combine; generalize hugeFuel = fuel
|
||||
fun_induction combine.go
|
||||
@@ -230,6 +196,93 @@ theorem Poly.denoteS_combine {α} [CommSemiring α] (ctx : Context α) (p₁ p
|
||||
rename_i h₂
|
||||
simp [denoteS, ih h₁ h₂, add_left_comm, add_assoc]
|
||||
|
||||
theorem Poly.denoteS_mulMon {α} [CommSemiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (mulMon k m p).denoteS ctx = k.toNat * m.denote ctx * p.denoteS ctx := by
|
||||
simp [mulMon, cond_eq_if] <;> split
|
||||
next => simp [denoteS, *]
|
||||
next =>
|
||||
split
|
||||
next h =>
|
||||
intro h₁ h₂
|
||||
simp at h; simp [*, Mon.denote, denoteS_mulConst _ _ _ h₁ h₂]
|
||||
next =>
|
||||
fun_induction mulMon.go <;> simp [denoteS, *]
|
||||
next h => simp +zetaDelta at h; simp [*]
|
||||
next =>
|
||||
intro h₁ h₂; cases h₂
|
||||
rw [Int.toNat_mul]
|
||||
simp [natCast_mul, CommSemiring.mul_comm, CommSemiring.mul_left_comm, mul_assoc]
|
||||
assumption; assumption
|
||||
next ih =>
|
||||
intro h₁ h₂; cases h₂; rename_i h₂ h₃
|
||||
rw [Int.toNat_mul]
|
||||
simp [Mon.denote_mul, natCast_mul, left_distrib, CommSemiring.mul_left_comm, mul_assoc, ih h₁ h₃]
|
||||
assumption; assumption
|
||||
|
||||
theorem Poly.addConst_NonnegCoeffs {p : Poly} {k : Int} : k ≥ 0 → p.NonnegCoeffs → (p.addConst k).NonnegCoeffs := by
|
||||
simp [addConst, cond_eq_if]; split
|
||||
next => intros; assumption
|
||||
fun_induction addConst.go
|
||||
next h _ => intro _ h; cases h; constructor; apply Int.add_nonneg <;> assumption
|
||||
next ih => intro h₁ h₂; cases h₂; constructor; assumption; apply ih <;> assumption
|
||||
|
||||
theorem Poly.insert_Nonneg (k : Int) (m : Mon) (p : Poly) : k ≥ 0 → p.NonnegCoeffs → (p.insert k m).NonnegCoeffs := by
|
||||
intro h₁ h₂
|
||||
fun_cases Poly.insert
|
||||
next => assumption
|
||||
next => apply Poly.addConst_NonnegCoeffs <;> assumption
|
||||
next =>
|
||||
fun_induction Poly.insert.go
|
||||
next => constructor <;> assumption
|
||||
next => cases h₂; assumption
|
||||
next => simp +zetaDelta; cases h₂; constructor; omega; assumption
|
||||
next => constructor <;> assumption
|
||||
next ih =>
|
||||
cases h₂; constructor
|
||||
next => assumption
|
||||
next => apply ih; assumption
|
||||
|
||||
theorem Poly.denoteS_mulMon_nc_go {α} [Semiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly) (acc : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → acc.NonnegCoeffs
|
||||
→ (mulMon_nc.go k m p acc).denoteS ctx = k.toNat * m.denote ctx * p.denoteS ctx + acc.denoteS ctx := by
|
||||
fun_induction mulMon_nc.go with simp [*]
|
||||
| case1 acc k' =>
|
||||
intro h₁ h₂ h₃; cases h₂
|
||||
have : k * k' ≥ 0 := by apply Int.mul_nonneg <;> assumption
|
||||
simp [denoteS_insert, denoteS, Int.toNat_mul, Semiring.natCast_mul, Semiring.mul_assoc, *]
|
||||
rw [← Semiring.natCast_mul_comm]
|
||||
| case2 acc k' m' p ih =>
|
||||
intro h₁ h₂ h₃; rcases h₂
|
||||
next _ h₂ =>
|
||||
have : k * k' ≥ 0 := by apply Int.mul_nonneg <;> assumption
|
||||
have : (insert (k * k') (m.mul_nc m') acc).NonnegCoeffs := by apply Poly.insert_Nonneg <;> assumption
|
||||
rw [ih h₁ h₂ this]
|
||||
simp [denoteS_insert, Int.toNat_mul, Semiring.natCast_mul, denoteS, left_distrib, Mon.denote_mul_nc, *]
|
||||
simp only [← Semiring.add_assoc]
|
||||
congr 1
|
||||
rw [Semiring.add_comm]
|
||||
congr 1
|
||||
rw [Semiring.natCast_mul_left_comm]
|
||||
conv => enter [1, 1]; rw [Semiring.natCast_mul_comm]
|
||||
simp [Semiring.mul_assoc]
|
||||
|
||||
theorem Poly.num_zero_NonnegCoeffs : (num 0).NonnegCoeffs := by
|
||||
apply NonnegCoeffs.num; simp
|
||||
|
||||
theorem Poly.denoteS_mulMon_nc {α} [Semiring α] (ctx : Context α) (k : Int) (m : Mon) (p : Poly)
|
||||
: k ≥ 0 → p.NonnegCoeffs → (mulMon_nc k m p).denoteS ctx = k.toNat * m.denote ctx * p.denoteS ctx := by
|
||||
simp [mulMon_nc, cond_eq_if] <;> split
|
||||
next => simp [denoteS, *]
|
||||
next =>
|
||||
split
|
||||
next h =>
|
||||
intro h₁ h₂
|
||||
simp at h; simp [*, Mon.denote, denoteS_mulConst _ _ _ h₁ h₂]
|
||||
next =>
|
||||
intro h₁ h₂
|
||||
have := Poly.denoteS_mulMon_nc_go ctx k m p (.num 0) h₁ h₂ Poly.num_zero_NonnegCoeffs
|
||||
simp [this, denoteS]
|
||||
|
||||
theorem Poly.mulConst_NonnegCoeffs {p : Poly} {k : Int} : k ≥ 0 → p.NonnegCoeffs → (p.mulConst k).NonnegCoeffs := by
|
||||
simp [mulConst, cond_eq_if]; split
|
||||
next => intros; constructor; decide
|
||||
@@ -256,12 +309,29 @@ theorem Poly.mulMon_NonnegCoeffs {p : Poly} {k : Int} (m : Mon) : k ≥ 0 → p.
|
||||
apply Int.mul_nonneg <;> assumption
|
||||
apply ih <;> assumption
|
||||
|
||||
theorem Poly.addConst_NonnegCoeffs {p : Poly} {k : Int} : k ≥ 0 → p.NonnegCoeffs → (p.addConst k).NonnegCoeffs := by
|
||||
simp [addConst, cond_eq_if]; split
|
||||
next => intros; assumption
|
||||
fun_induction addConst.go
|
||||
next h _ => intro _ h; cases h; constructor; apply Int.add_nonneg <;> assumption
|
||||
next ih => intro h₁ h₂; cases h₂; constructor; assumption; apply ih <;> assumption
|
||||
theorem Poly.mulMon_nc_go_NonnegCoeffs {p : Poly} {k : Int} (m : Mon) {acc : Poly}
|
||||
: k ≥ 0 → p.NonnegCoeffs → acc.NonnegCoeffs → (Poly.mulMon_nc.go k m p acc).NonnegCoeffs := by
|
||||
intro h₁ h₂ h₃
|
||||
fun_induction Poly.mulMon_nc.go
|
||||
next k' =>
|
||||
cases h₂
|
||||
have : k*k' ≥ 0 := by apply Int.mul_nonneg <;> assumption
|
||||
apply Poly.insert_Nonneg <;> assumption
|
||||
next ih =>
|
||||
cases h₂; next h₂ =>
|
||||
apply ih; assumption
|
||||
apply insert_Nonneg
|
||||
next => apply Int.mul_nonneg <;> assumption
|
||||
next => assumption
|
||||
|
||||
theorem Poly.mulMon_nc_NonnegCoeffs {p : Poly} {k : Int} (m : Mon) : k ≥ 0 → p.NonnegCoeffs → (p.mulMon_nc k m).NonnegCoeffs := by
|
||||
simp [mulMon_nc, cond_eq_if]; split
|
||||
next => intros; constructor; decide
|
||||
split
|
||||
next => intros; apply mulConst_NonnegCoeffs <;> assumption
|
||||
intro h₁ h₂
|
||||
apply Poly.mulMon_nc_go_NonnegCoeffs; assumption; assumption
|
||||
exact Poly.num_zero_NonnegCoeffs
|
||||
|
||||
theorem Poly.concat_NonnegCoeffs {p₁ p₂ : Poly} : p₁.NonnegCoeffs → p₂.NonnegCoeffs → (p₁.concat p₂).NonnegCoeffs := by
|
||||
fun_induction Poly.concat
|
||||
@@ -309,14 +379,40 @@ theorem Poly.mul_NonnegCoeffs {p₁ p₂ : Poly} : p₁.NonnegCoeffs → p₂.No
|
||||
unfold mul; intros; apply mul_go_NonnegCoeffs
|
||||
assumption; assumption; constructor; decide
|
||||
|
||||
theorem Poly.mul_nc_go_NonnegCoeffs (p₁ p₂ acc : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → acc.NonnegCoeffs → (mul_nc.go p₂ p₁ acc).NonnegCoeffs := by
|
||||
fun_induction mul_nc.go
|
||||
next =>
|
||||
intro h₁ h₂ h₃
|
||||
cases h₁; rename_i h₁
|
||||
have := mulConst_NonnegCoeffs h₁ h₂
|
||||
apply combine_NonnegCoeffs <;> assumption
|
||||
next ih =>
|
||||
intro h₁ h₂ h₃
|
||||
cases h₁
|
||||
apply ih
|
||||
assumption; assumption
|
||||
apply Poly.combine_NonnegCoeffs; assumption
|
||||
apply Poly.mulMon_nc_NonnegCoeffs <;> assumption
|
||||
|
||||
theorem Poly.mul_nc_NonnegCoeffs {p₁ p₂ : Poly} : p₁.NonnegCoeffs → p₂.NonnegCoeffs → (p₁.mul_nc p₂).NonnegCoeffs := by
|
||||
unfold mul_nc; intros; apply mul_nc_go_NonnegCoeffs
|
||||
assumption; assumption; constructor; decide
|
||||
|
||||
theorem Poly.pow_NonnegCoeffs {p : Poly} (k : Nat) : p.NonnegCoeffs → (p.pow k).NonnegCoeffs := by
|
||||
fun_induction Poly.pow
|
||||
next => intros; constructor; decide
|
||||
next => intros; assumption
|
||||
next ih => intro h; apply mul_NonnegCoeffs; assumption; apply ih; assumption
|
||||
|
||||
theorem Poly.num_zero_NonnegCoeffs : (num 0).NonnegCoeffs := by
|
||||
apply NonnegCoeffs.num; simp
|
||||
theorem Poly.pow_nc_NonnegCoeffs {p : Poly} (k : Nat) : p.NonnegCoeffs → (p.pow_nc k).NonnegCoeffs := by
|
||||
fun_induction Poly.pow_nc
|
||||
next => intros; constructor; decide
|
||||
next => intros; assumption
|
||||
next ih =>
|
||||
intro h; apply mul_nc_NonnegCoeffs
|
||||
next => apply ih; assumption
|
||||
next => assumption
|
||||
|
||||
theorem Poly.denoteS_mul_go {α} [CommSemiring α] (ctx : Context α) (p₁ p₂ acc : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → acc.NonnegCoeffs → (mul.go p₂ p₁ acc).denoteS ctx = acc.denoteS ctx + p₁.denoteS ctx * p₂.denoteS ctx := by
|
||||
@@ -339,6 +435,27 @@ theorem Poly.denoteS_mul {α} [CommSemiring α] (ctx : Context α) (p₁ p₂ :
|
||||
intro h₁ h₂
|
||||
simp [mul, denoteS_mul_go, denoteS, Poly.num_zero_NonnegCoeffs, *]
|
||||
|
||||
theorem Poly.denoteS_mul_nc_go {α} [Semiring α] (ctx : Context α) (p₁ p₂ acc : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → acc.NonnegCoeffs → (mul_nc.go p₂ p₁ acc).denoteS ctx = acc.denoteS ctx + p₁.denoteS ctx * p₂.denoteS ctx := by
|
||||
fun_induction mul_nc.go <;> intro h₁ h₂ h₃
|
||||
next k =>
|
||||
cases h₁; rename_i h₁
|
||||
have := p₂.mulConst_NonnegCoeffs h₁ h₂
|
||||
simp [denoteS, denoteS_combine, denoteS_mulConst, *]
|
||||
next acc a m p ih =>
|
||||
cases h₁; rename_i h₁ h₁'
|
||||
have := p₂.mulMon_nc_NonnegCoeffs m h₁ h₂
|
||||
have := acc.combine_NonnegCoeffs h₃ this
|
||||
replace ih := ih h₁' h₂ this
|
||||
rw [ih, denoteS_combine, denoteS_mulMon_nc]
|
||||
simp [denoteS, add_assoc, right_distrib]
|
||||
all_goals assumption
|
||||
|
||||
theorem Poly.denoteS_mul_nc {α} [Semiring α] (ctx : Context α) (p₁ p₂ : Poly)
|
||||
: p₁.NonnegCoeffs → p₂.NonnegCoeffs → (mul_nc p₁ p₂).denoteS ctx = p₁.denoteS ctx * p₂.denoteS ctx := by
|
||||
intro h₁ h₂
|
||||
simp [mul_nc, denoteS_mul_nc_go, denoteS, Poly.num_zero_NonnegCoeffs, *]
|
||||
|
||||
theorem Poly.denoteS_pow {α} [CommSemiring α] (ctx : Context α) (p : Poly) (k : Nat)
|
||||
: p.NonnegCoeffs → (pow p k).denoteS ctx = p.denoteS ctx ^ k := by
|
||||
fun_induction pow <;> intro h₁
|
||||
@@ -350,13 +467,19 @@ theorem Poly.denoteS_pow {α} [CommSemiring α] (ctx : Context α) (p : Poly) (k
|
||||
assumption
|
||||
apply Poly.pow_NonnegCoeffs; assumption
|
||||
|
||||
end CommRing
|
||||
theorem Poly.denoteS_pow_nc {α} [Semiring α] (ctx : Context α) (p : Poly) (k : Nat)
|
||||
: p.NonnegCoeffs → (pow_nc p k).denoteS ctx = p.denoteS ctx ^ k := by
|
||||
fun_induction pow_nc <;> intro h₁
|
||||
next => simp [denoteS, pow_zero]
|
||||
next => simp [pow_succ, pow_zero]
|
||||
next ih =>
|
||||
replace ih := ih h₁
|
||||
rw [denoteS_mul_nc, ih, pow_succ]
|
||||
apply Poly.pow_nc_NonnegCoeffs; assumption
|
||||
assumption
|
||||
|
||||
namespace Ring.OfSemiring
|
||||
open CommRing
|
||||
|
||||
theorem Expr.toPoly_NonnegCoeffs {e : Expr} : e.toPoly.NonnegCoeffs := by
|
||||
fun_induction toPoly
|
||||
theorem Expr.toPolyS_NonnegCoeffs {e : Expr} : e.toPolyS.NonnegCoeffs := by
|
||||
fun_induction toPolyS
|
||||
next => constructor; apply Int.natCast_nonneg
|
||||
next => simp [Poly.ofVar, Poly.ofMon]; constructor; decide; constructor; decide
|
||||
next => apply Poly.combine_NonnegCoeffs <;> assumption
|
||||
@@ -364,29 +487,89 @@ theorem Expr.toPoly_NonnegCoeffs {e : Expr} : e.toPoly.NonnegCoeffs := by
|
||||
next => constructor; apply Int.pow_nonneg; apply Int.natCast_nonneg
|
||||
next => constructor; decide; constructor; decide
|
||||
next => apply Poly.pow_NonnegCoeffs; assumption
|
||||
next => constructor; apply Int.ofNat_zero_le
|
||||
all_goals exact Poly.num_zero_NonnegCoeffs
|
||||
|
||||
theorem Expr.denoteS_toPoly {α} [CommSemiring α] (ctx : Context α) (e : Expr)
|
||||
: e.toPoly.denoteS ctx = e.denote ctx := by
|
||||
fun_induction toPoly
|
||||
<;> simp [denote, Poly.denoteS, Poly.denoteS_ofVar, denoteSInt_eq, Semiring.ofNat_eq_natCast]
|
||||
next => simp [CommRing.Var.denote, Var.denote]
|
||||
next ih₁ ih₂ => rw [Poly.denoteS_combine, ih₁, ih₂] <;> apply toPoly_NonnegCoeffs
|
||||
next ih₁ ih₂ => rw [Poly.denoteS_mul, ih₁, ih₂] <;> apply toPoly_NonnegCoeffs
|
||||
next => rw [Int.toNat_pow_of_nonneg, Semiring.natCast_pow, Int.toNat_natCast]; apply Int.natCast_nonneg
|
||||
next =>
|
||||
simp [Poly.ofMon, Poly.denoteS, denoteSInt_eq, Power.denote_eq, Mon.denote,
|
||||
Semiring.natCast_zero, Semiring.natCast_one, Semiring.one_mul, Semiring.add_zero,
|
||||
CommRing.Var.denote, Var.denote, Semiring.mul_one]
|
||||
next ih => rw [Poly.denoteS_pow, ih]; apply toPoly_NonnegCoeffs
|
||||
attribute [local simp] Expr.toPolyS_NonnegCoeffs
|
||||
|
||||
theorem Expr.toPolyS_nc_NonnegCoeffs {e : Expr} : e.toPolyS_nc.NonnegCoeffs := by
|
||||
fun_induction toPolyS_nc
|
||||
next => constructor; apply Int.natCast_nonneg
|
||||
next => simp [Poly.ofVar, Poly.ofMon]; constructor; decide; constructor; decide
|
||||
next => apply Poly.combine_NonnegCoeffs <;> assumption
|
||||
next => apply Poly.mul_nc_NonnegCoeffs <;> assumption
|
||||
next => constructor; apply Int.pow_nonneg; apply Int.natCast_nonneg
|
||||
next => constructor; decide; constructor; decide
|
||||
next => apply Poly.pow_nc_NonnegCoeffs; assumption
|
||||
next => constructor; apply Int.ofNat_zero_le
|
||||
all_goals exact Poly.num_zero_NonnegCoeffs
|
||||
|
||||
attribute [local simp] Expr.toPolyS_nc_NonnegCoeffs
|
||||
|
||||
theorem Expr.denoteS_toPolyS {α} [CommSemiring α] (ctx : Context α) (e : Expr)
|
||||
: e.toPolyS.denoteS ctx = e.denoteS ctx := by
|
||||
fun_induction toPolyS <;> simp [denoteS, Poly.denoteS, Poly.denoteS_ofVar, denoteSInt_eq]
|
||||
next => simp [Semiring.ofNat_eq_natCast]
|
||||
next => simp [Poly.denoteS_combine] <;> simp [*]
|
||||
next => simp [Poly.denoteS_mul] <;> simp [*]
|
||||
next => rw [Int.toNat_pow_of_nonneg, Semiring.natCast_pow, Int.toNat_natCast, ← Semiring.ofNat_eq_natCast]
|
||||
apply Int.natCast_nonneg
|
||||
next => simp [Poly.ofMon, Poly.denoteS, denoteSInt_eq, Power.denote_eq, Mon.denote,
|
||||
Semiring.natCast_zero, Semiring.natCast_one, Semiring.one_mul,
|
||||
CommRing.Var.denote, Var.denote, Semiring.mul_one]
|
||||
next ih => rw [Poly.denoteS_pow, ih]; apply toPolyS_NonnegCoeffs
|
||||
next => simp [Semiring.natCast_eq_ofNat]
|
||||
|
||||
theorem Expr.denoteS_toPolyS_nc {α} [Semiring α] (ctx : Context α) (e : Expr)
|
||||
: e.toPolyS_nc.denoteS ctx = e.denoteS ctx := by
|
||||
fun_induction Expr.toPolyS_nc <;> simp [denoteS, Poly.denoteS, Poly.denoteS_ofVar, denoteSInt_eq]
|
||||
next => simp [Semiring.ofNat_eq_natCast]
|
||||
next => simp [Poly.denoteS_combine] <;> simp [*]
|
||||
next => simp [Poly.denoteS_mul_nc] <;> simp [*]
|
||||
next => rw [Int.toNat_pow_of_nonneg, Semiring.natCast_pow, Int.toNat_natCast, ← Semiring.ofNat_eq_natCast]
|
||||
apply Int.natCast_nonneg
|
||||
next => simp [Poly.ofMon, Poly.denoteS, denoteSInt_eq, Power.denote_eq, Mon.denote,
|
||||
Semiring.natCast_zero, Semiring.natCast_one, Semiring.one_mul,
|
||||
CommRing.Var.denote, Var.denote, Semiring.mul_one]
|
||||
next ih => rw [Poly.denoteS_pow_nc, ih]; apply toPolyS_nc_NonnegCoeffs
|
||||
next => simp [Semiring.natCast_eq_ofNat]
|
||||
|
||||
def eq_normS_cert (lhs rhs : Expr) : Bool :=
|
||||
lhs.toPoly == rhs.toPoly
|
||||
lhs.toPolyS == rhs.toPolyS
|
||||
|
||||
theorem eq_normS {α} [CommSemiring α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: eq_normS_cert lhs rhs → lhs.denote ctx = rhs.denote ctx := by
|
||||
: eq_normS_cert lhs rhs → lhs.denoteS ctx = rhs.denoteS ctx := by
|
||||
simp [eq_normS_cert]; intro h
|
||||
replace h := congrArg (Poly.denoteS ctx) h
|
||||
simp [Expr.denoteS_toPoly, *] at h
|
||||
simp [Expr.denoteS_toPolyS, *] at h
|
||||
assumption
|
||||
|
||||
end Lean.Grind.Ring.OfSemiring
|
||||
def eq_normS_nc_cert (lhs rhs : Expr) : Bool :=
|
||||
lhs.toPolyS_nc == rhs.toPolyS_nc
|
||||
|
||||
theorem eq_normS_nc {α} [Semiring α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: eq_normS_nc_cert lhs rhs → lhs.denoteS ctx = rhs.denoteS ctx := by
|
||||
simp [eq_normS_nc_cert]; intro h
|
||||
replace h := congrArg (Poly.denoteS ctx) h
|
||||
simp [Expr.denoteS_toPolyS_nc, *] at h
|
||||
assumption
|
||||
|
||||
end CommRing
|
||||
|
||||
namespace Ring.OfSemiring
|
||||
open CommRing
|
||||
|
||||
theorem of_eq {α} [Semiring α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: lhs.denoteS ctx = rhs.denoteS ctx → lhs.denoteSAsRing ctx = rhs.denoteSAsRing ctx := by
|
||||
intro h; replace h := congrArg toQ h
|
||||
simpa [← Expr.denoteAsRing_eq] using h
|
||||
|
||||
theorem of_diseq {α} [Semiring α] [AddRightCancel α] (ctx : Context α) (lhs rhs : Expr)
|
||||
: lhs.denoteS ctx ≠ rhs.denoteS ctx → lhs.denoteSAsRing ctx ≠ rhs.denoteSAsRing ctx := by
|
||||
intro h₁ h₂
|
||||
simp [Expr.denoteAsRing_eq] at h₂
|
||||
replace h₂ := toQ_inj h₂
|
||||
contradiction
|
||||
|
||||
end Ring.OfSemiring
|
||||
end Lean.Grind
|
||||
|
||||
@@ -15,7 +15,10 @@ public import Init.Grind.Ring.Field
|
||||
public import Init.Grind.Ordered.Ring
|
||||
public import Init.GrindInstances.Ring.Int
|
||||
import all Init.Data.Ord.Basic
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
@[expose] public section
|
||||
|
||||
namespace Lean.Grind.CommRing
|
||||
/-!
|
||||
Data-structures, definitions and theorems for implementing the
|
||||
@@ -68,11 +71,7 @@ noncomputable def Expr.denote {α} [Ring α] (ctx : Context α) (e : Expr) : α
|
||||
structure Power where
|
||||
x : Var
|
||||
k : Nat
|
||||
deriving BEq, Repr, Inhabited, Hashable
|
||||
|
||||
instance : LawfulBEq Power where
|
||||
eq_of_beq {a} := by cases a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
|
||||
rfl := by intro a; cases a <;> simp! [BEq.beq]
|
||||
deriving BEq, ReflBEq, LawfulBEq, Repr, Inhabited, Hashable
|
||||
|
||||
protected noncomputable def Power.beq' (pw₁ pw₂ : Power) : Bool :=
|
||||
Power.rec (fun x₁ k₁ => Power.rec (fun x₂ k₂ => Nat.beq x₁ x₂ && Nat.beq k₁ k₂) pw₂) pw₁
|
||||
@@ -93,18 +92,7 @@ def Power.denote {α} [Semiring α] (ctx : Context α) : Power → α
|
||||
inductive Mon where
|
||||
| unit
|
||||
| mult (p : Power) (m : Mon)
|
||||
deriving BEq, Repr, Inhabited, Hashable
|
||||
|
||||
instance : LawfulBEq Mon where
|
||||
eq_of_beq {a} := by
|
||||
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
|
||||
next p₁ m₁ p₂ m₂ ih =>
|
||||
cases p₁ <;> cases p₂ <;> simp <;> intros <;> simp [*]
|
||||
next h => exact ih h
|
||||
rfl := by
|
||||
intro a
|
||||
induction a <;> simp! [BEq.beq]
|
||||
assumption
|
||||
deriving BEq, ReflBEq, LawfulBEq, Repr, Inhabited, Hashable
|
||||
|
||||
protected noncomputable def Mon.beq' (m₁ : Mon) : Mon → Bool :=
|
||||
Mon.rec
|
||||
@@ -326,7 +314,7 @@ theorem Mon.grevlex_k_eq_grevlex (m₁ m₂ : Mon) : m₁.grevlex_k m₂ = m₁.
|
||||
inductive Poly where
|
||||
| num (k : Int)
|
||||
| add (k : Int) (v : Mon) (p : Poly)
|
||||
deriving BEq, Repr, Inhabited, Hashable
|
||||
deriving BEq, ReflBEq, LawfulBEq, Repr, Inhabited, Hashable
|
||||
|
||||
protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
|
||||
Poly.rec
|
||||
@@ -344,20 +332,6 @@ protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
|
||||
intro _ _; subst k₁ m₁
|
||||
simp [← ih p₂, ← Bool.and'_eq_and]; rfl
|
||||
|
||||
instance : LawfulBEq Poly where
|
||||
eq_of_beq {a} := by
|
||||
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
|
||||
intro h₁ h₂ h₃
|
||||
rename_i m₁ p₁ _ m₂ p₂ ih
|
||||
replace h₂ : m₁ == m₂ := h₂
|
||||
simp [ih h₃, eq_of_beq h₂]
|
||||
rfl := by
|
||||
intro a
|
||||
induction a <;> simp! [BEq.beq]
|
||||
rename_i k m p ih
|
||||
change m == m ∧ p == p
|
||||
simp [ih]
|
||||
|
||||
def Poly.denote [Ring α] (ctx : Context α) (p : Poly) : α :=
|
||||
match p with
|
||||
| .num k => Int.cast k
|
||||
|
||||
@@ -277,6 +277,9 @@ attribute [-simp] Q.mk
|
||||
|
||||
/-! Embedding theorems -/
|
||||
|
||||
theorem toQ_zero : toQ (0 : α) = (0 : Q α) := by
|
||||
simp; apply Quot.sound; simp
|
||||
|
||||
theorem toQ_add (a b : α) : toQ (a + b) = toQ a + toQ b := by
|
||||
simp
|
||||
|
||||
|
||||
@@ -4,11 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Grind.Attr
|
||||
public import Init.Core
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean.Grind
|
||||
@@ -98,8 +96,16 @@ structure Config where
|
||||
zeta := true
|
||||
/--
|
||||
When `true` (default: `true`), uses procedure for handling equalities over commutative rings.
|
||||
This solver also support commutative semirings, fields, and normalizer non-commutative rings and
|
||||
semirings.
|
||||
-/
|
||||
ring := true
|
||||
/--
|
||||
Maximum number of steps performed by the `ring` solver.
|
||||
A step is counted whenever one polynomial is used to simplify another.
|
||||
For example, given `x^2 + 1` and `x^2 * y^3 + x * y`, the first can be
|
||||
used to simplify the second to `-1 * y^3 + x * y`.
|
||||
-/
|
||||
ringSteps := 10000
|
||||
/--
|
||||
When `true` (default: `true`), uses procedure for handling linear arithmetic for `IntModule`, and
|
||||
@@ -114,6 +120,12 @@ structure Config where
|
||||
When `true` (default: `true`), uses procedure for handling associative (and commutative) operators.
|
||||
-/
|
||||
ac := true
|
||||
/--
|
||||
Maximum number of steps performed by the `ac` solver.
|
||||
A step is counted whenever one AC equation is used to simplify another.
|
||||
For example, given `ma x z = w` and `max x (max y z) = x`, the first can be
|
||||
used to simplify the second to `max w y = x`.
|
||||
-/
|
||||
acSteps := 1000
|
||||
/--
|
||||
Maximum exponent eagerly evaluated while computing bounds for `ToInt` and
|
||||
@@ -124,6 +136,14 @@ structure Config where
|
||||
When `true` (default: `true`), automatically creates an auxiliary theorem to store the proof.
|
||||
-/
|
||||
abstractProof := true
|
||||
/--
|
||||
When `true` (default: `true`), enables the procedure for handling injective functions.
|
||||
In this mode, `grind` takes into account theorems such as:
|
||||
```
|
||||
@[grind inj] theorem double_inj : Function.Injective double
|
||||
```
|
||||
-/
|
||||
inj := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
/--
|
||||
@@ -180,9 +200,14 @@ namespace Lean.Parser.Tactic
|
||||
`grind` tactic and related tactics.
|
||||
-/
|
||||
|
||||
syntax grindErase := "-" ident
|
||||
syntax grindLemma := ppGroup((Attr.grindMod ppSpace)? ident)
|
||||
syntax grindParam := grindErase <|> grindLemma
|
||||
syntax grindErase := "-" ident
|
||||
syntax grindLemma := ppGroup((Attr.grindMod ppSpace)? ident)
|
||||
/--
|
||||
The `!` modifier instructs `grind` to consider only minimal indexable subexpressions
|
||||
when selecting patterns.
|
||||
-/
|
||||
syntax grindLemmaMin := ppGroup("!" (Attr.grindMod ppSpace)? ident)
|
||||
syntax grindParam := grindErase <|> grindLemma <|> grindLemmaMin
|
||||
|
||||
/--
|
||||
`grind` is a tactic inspired by modern SMT solvers. **Picture a virtual whiteboard**:
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.Int.DivMod.Lemmas
|
||||
import Init.LawfulBEqTactics
|
||||
|
||||
public section
|
||||
|
||||
@@ -37,11 +38,7 @@ inductive IntInterval : Type where
|
||||
io (hi : Int)
|
||||
| /-- The infinite interval `(-∞, ∞)`. -/
|
||||
ii
|
||||
deriving BEq, DecidableEq, Inhabited
|
||||
|
||||
instance : LawfulBEq IntInterval where
|
||||
rfl := by intro a; cases a <;> simp_all! [BEq.beq]
|
||||
eq_of_beq := by intro a b; cases a <;> cases b <;> simp_all! [BEq.beq]
|
||||
deriving BEq, ReflBEq, LawfulBEq, DecidableEq, Inhabited
|
||||
|
||||
namespace IntInterval
|
||||
|
||||
|
||||
104
src/Init/LawfulBEqTactics.lean
Normal file
104
src/Init/LawfulBEqTactics.lean
Normal file
@@ -0,0 +1,104 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
module
|
||||
prelude
|
||||
public import Init.Prelude
|
||||
public import Init.Notation
|
||||
public import Init.Tactics
|
||||
public import Init.Core
|
||||
import Init.Data.Bool
|
||||
import Init.ByCases
|
||||
|
||||
public section
|
||||
|
||||
namespace DerivingHelpers
|
||||
|
||||
macro "deriving_ReflEq_tactic" : tactic => `(tactic|(
|
||||
intro x
|
||||
induction x
|
||||
all_goals
|
||||
simp only [ BEq.refl, ↓reduceDIte, Bool.and_true, *, reduceBEq ,reduceCtorIdx]
|
||||
))
|
||||
|
||||
theorem and_true_curry {a b : Bool} {P : Prop}
|
||||
(h : a → b → P) : (a && b) → P := by
|
||||
rw [Bool.and_eq_true_iff]
|
||||
intro h'
|
||||
apply h h'.1 h'.2
|
||||
|
||||
|
||||
theorem deriving_lawful_beq_helper_dep {x y : α} [BEq α] [ReflBEq α]
|
||||
{t : (x == y) = true → Bool} {P : Prop}
|
||||
(inst : (x == y) = true → x = y)
|
||||
(k : (h : x = y) → t (h ▸ ReflBEq.rfl) = true → P) :
|
||||
(if h : (x == y) then t h else false) = true → P := by
|
||||
intro h
|
||||
by_cases hxy : x = y
|
||||
· subst hxy
|
||||
apply k rfl
|
||||
rw [dif_pos (BEq.refl x)] at h
|
||||
exact h
|
||||
· by_cases hxy' : x == y
|
||||
· exact False.elim <| hxy (inst hxy')
|
||||
· rw [dif_neg hxy'] at h
|
||||
contradiction
|
||||
|
||||
theorem deriving_lawful_beq_helper_nd {x y : α} [BEq α] [ReflBEq α]
|
||||
{P : Prop}
|
||||
(inst : (x == y) = true → x = y)
|
||||
(k : x = y → P) :
|
||||
(x == y) = true → P := by
|
||||
intro h
|
||||
by_cases hxy : x = y
|
||||
· subst hxy
|
||||
apply k rfl
|
||||
· exact False.elim <| hxy (inst h)
|
||||
|
||||
end DerivingHelpers
|
||||
|
||||
syntax "deriving_LawfulEq_tactic_step" : tactic
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| fail "deriving_LawfulEq_tactic_step failed")
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| ( with_reducible change dite (_ == _) _ _ = true → _
|
||||
refine DerivingHelpers.deriving_lawful_beq_helper_dep ?_ ?_
|
||||
· solve | apply_assumption | simp | fail "could not discharge eq_of_beq assumption"
|
||||
intro h
|
||||
cases h
|
||||
dsimp only
|
||||
))
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| ( with_reducible change (_ == _) = true → _
|
||||
refine DerivingHelpers.deriving_lawful_beq_helper_nd ?_ ?_
|
||||
· solve | apply_assumption | simp | fail "could not discharge eq_of_beq assumption"
|
||||
intro h
|
||||
subst h
|
||||
))
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| ( with_reducible change (_ == _ && _) = true → _
|
||||
refine DerivingHelpers.and_true_curry ?_))
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| rfl)
|
||||
macro_rules
|
||||
| `(tactic| deriving_LawfulEq_tactic_step) =>
|
||||
`(tactic| intro _; trivial)
|
||||
|
||||
macro "deriving_LawfulEq_tactic" : tactic => `(tactic|(
|
||||
intro x
|
||||
induction x
|
||||
all_goals
|
||||
intro y
|
||||
cases y
|
||||
all_goals
|
||||
simp only [reduceBEq, reduceCtorIdx]
|
||||
repeat deriving_LawfulEq_tactic_step
|
||||
))
|
||||
70
src/Init/MethodSpecsSimp.lean
Normal file
70
src/Init/MethodSpecsSimp.lean
Normal file
@@ -0,0 +1,70 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
module
|
||||
prelude
|
||||
public import Init.Prelude
|
||||
|
||||
/-!
|
||||
This module contains theorems to be added to the `@[method_specs_simp]` simpset. When we use the
|
||||
idiom of a heterogeneous class with notation (e.g. `HAppend`) and a homogeneous class that the user
|
||||
typically instantiates, these rewrite the method specifications generated by `@[method_specs]` from
|
||||
the homogeneous form to the desired heterogeneous form.
|
||||
|
||||
Should modules within `Init` use `@[method_specs]` on such instances, they should import this file.
|
||||
-/
|
||||
|
||||
public section
|
||||
|
||||
@[method_specs_simp] theorem Add.add_eq_hAdd {α : Type u} [inst : Add α] :
|
||||
Eq (@Add.add α inst) (@HAdd.hAdd α α α (@instHAdd α inst)) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Sub.sub_eq_hSub [Sub α] :
|
||||
Eq (@Sub.sub α _) (@HSub.hSub α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Mul.mul_eq_hMul [Mul α] :
|
||||
Eq (@Mul.mul α _) (@HMul.hMul α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Div.div_eq_hDiv [Div α] :
|
||||
Eq (@Div.div α _) (@HDiv.hDiv α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Mod.mod_eq_hMod [Mod α] :
|
||||
Eq (@Mod.mod α _) (@HMod.hMod α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Pow.pow_eq_hPow {α β} [Pow α β] :
|
||||
Eq (@Pow.pow α β _) (@HPow.hPow α β α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem SMul.smul_eq_hSMul {α β} [SMul α β] :
|
||||
Eq (@SMul.smul α β _) (@HSMul.hSMul α β β _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Mul.mul_eq_smul {α} [Mul α] :
|
||||
Eq (@Mul.mul α _) (@SMul.smul α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem Append.append_eq_hAppend [Append α] :
|
||||
Eq (@Append.append α _) (@HAppend.hAppend α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem OrElse.orElse_eq_hOrElse [OrElse α] :
|
||||
Eq (@OrElse.orElse α _) (@HOrElse.hOrElse α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem AndThen.andThen_eq_hAndThen [AndThen α] :
|
||||
Eq (@AndThen.andThen α _) (@HAndThen.hAndThen α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem AndOp.andOp_hAnd [AndOp α] :
|
||||
Eq (@AndOp.and α _) (@HAnd.hAnd α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem XorOp.xor_hXor [XorOp α] :
|
||||
Eq (@XorOp.xor α _) (@HXor.hXor α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem OrOp.or_hOr [OrOp α] :
|
||||
Eq (@OrOp.or α _) (@HOr.hOr α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem ShiftLeft.shiftLeft_hShiftLeft [ShiftLeft α] :
|
||||
Eq (@ShiftLeft.shiftLeft α _) (@HShiftLeft.hShiftLeft α α α _) := rfl
|
||||
|
||||
@[method_specs_simp] theorem ShiftRight.shiftRight_hShiftRight [ShiftRight α] :
|
||||
Eq (@ShiftRight.shiftRight α _) (@HShiftRight.hShiftRight α α α _) := rfl
|
||||
|
||||
end
|
||||
@@ -19,7 +19,7 @@ namespace Lean
|
||||
Auxiliary type used to represent syntax categories. We mainly use auxiliary
|
||||
definitions with this type to attach doc strings to syntax categories.
|
||||
-/
|
||||
structure Parser.Category
|
||||
meta structure Parser.Category
|
||||
|
||||
namespace Parser.Category
|
||||
|
||||
@@ -855,7 +855,7 @@ which would include `#guard_msgs` itself, and would cause duplicate and/or uncap
|
||||
The top-level command elaborator only runs the linters if `#guard_msgs` is not present.
|
||||
-/
|
||||
syntax (name := guardMsgsCmd)
|
||||
(docComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
|
||||
(plainDocComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
|
||||
|
||||
/--
|
||||
Format and print the info trees for a given command.
|
||||
|
||||
@@ -25,7 +25,7 @@ syntax bracketedExplicitBinders := "(" withoutPosition((binderIdent ppSpace)+
|
||||
syntax explicitBinders := (ppSpace bracketedExplicitBinders)+ <|> unbracketedExplicitBinders
|
||||
|
||||
open TSyntax.Compat in
|
||||
def expandExplicitBindersAux (combinator : Syntax) (idents : Array Syntax) (type? : Option Syntax) (body : Syntax) : MacroM Syntax :=
|
||||
meta def expandExplicitBindersAux (combinator : Syntax) (idents : Array Syntax) (type? : Option Syntax) (body : Syntax) : MacroM Syntax :=
|
||||
let rec loop (i : Nat) (h : i ≤ idents.size) (acc : Syntax) := do
|
||||
match i with
|
||||
| 0 => pure acc
|
||||
@@ -39,7 +39,7 @@ def expandExplicitBindersAux (combinator : Syntax) (idents : Array Syntax) (type
|
||||
loop i (Nat.le_of_succ_le h) acc
|
||||
loop idents.size (by simp) body
|
||||
|
||||
def expandBracketedBindersAux (combinator : Syntax) (binders : Array Syntax) (body : Syntax) : MacroM Syntax :=
|
||||
meta def expandBracketedBindersAux (combinator : Syntax) (binders : Array Syntax) (body : Syntax) : MacroM Syntax :=
|
||||
let rec loop (i : Nat) (h : i ≤ binders.size) (acc : Syntax) := do
|
||||
match i with
|
||||
| 0 => pure acc
|
||||
@@ -49,7 +49,7 @@ def expandBracketedBindersAux (combinator : Syntax) (binders : Array Syntax) (bo
|
||||
loop i (Nat.le_of_succ_le h) (← expandExplicitBindersAux combinator idents (some type) acc)
|
||||
loop binders.size (by simp) body
|
||||
|
||||
def expandExplicitBinders (combinatorDeclName : Name) (explicitBinders : Syntax) (body : Syntax) : MacroM Syntax := do
|
||||
meta def expandExplicitBinders (combinatorDeclName : Name) (explicitBinders : Syntax) (body : Syntax) : MacroM Syntax := do
|
||||
let combinator := mkCIdentFrom (← getRef) combinatorDeclName
|
||||
let explicitBinders := explicitBinders[0]
|
||||
if explicitBinders.getKind == ``Lean.unbracketedExplicitBinders then
|
||||
@@ -61,7 +61,7 @@ def expandExplicitBinders (combinatorDeclName : Name) (explicitBinders : Syntax)
|
||||
else
|
||||
Macro.throwError "unexpected explicit binder"
|
||||
|
||||
def expandBracketedBinders (combinatorDeclName : Name) (bracketedExplicitBinders : Syntax) (body : Syntax) : MacroM Syntax := do
|
||||
meta def expandBracketedBinders (combinatorDeclName : Name) (bracketedExplicitBinders : Syntax) (body : Syntax) : MacroM Syntax := do
|
||||
let combinator := mkCIdentFrom (← getRef) combinatorDeclName
|
||||
expandBracketedBindersAux combinator #[bracketedExplicitBinders] body
|
||||
|
||||
|
||||
@@ -3010,218 +3010,56 @@ def List.concat {α : Type u} : List α → α → List α
|
||||
| cons a as, b => cons a (concat as b)
|
||||
|
||||
/--
|
||||
Returns the sequence of bytes in a character's UTF-8 encoding.
|
||||
Appends two lists. Normally used via the `++` operator.
|
||||
|
||||
Appending lists takes time proportional to the length of the first list: `O(|xs|)`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3] ++ [4, 5] = [1, 2, 3, 4, 5]`.
|
||||
* `[] ++ [4, 5] = [4, 5]`.
|
||||
* `[1, 2, 3] ++ [] = [1, 2, 3]`.
|
||||
-/
|
||||
def String.utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
let v := c.val.toNat
|
||||
ite (LE.le v 0x7f)
|
||||
(List.cons (UInt8.ofNat v) List.nil)
|
||||
(ite (LE.le v 0x7ff)
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x20) 0xc0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil))
|
||||
(ite (LE.le v 0xffff)
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 4096) 0x10) 0xe0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil)))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 262144) 0x08) 0xf0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 4096) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil))))))
|
||||
protected def List.append : (xs ys : List α) → List α
|
||||
| nil, bs => bs
|
||||
| cons a as, bs => cons a (List.append as bs)
|
||||
|
||||
/--
|
||||
A string is a sequence of Unicode code points.
|
||||
Concatenates a list of lists into a single list, preserving the order of the elements.
|
||||
|
||||
At runtime, strings are represented by [dynamic arrays](https://en.wikipedia.org/wiki/Dynamic_array)
|
||||
of bytes using the UTF-8 encoding. Both the size in bytes (`String.utf8ByteSize`) and in characters
|
||||
(`String.length`) are cached and take constant time. Many operations on strings perform in-place
|
||||
modifications when the reference to the string is unique.
|
||||
`O(|flatten L|)`.
|
||||
|
||||
Examples:
|
||||
* `[["a"], ["b", "c"]].flatten = ["a", "b", "c"]`
|
||||
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flatten = ["a", "b", "c", "d", "e", "f"]`
|
||||
-/
|
||||
structure String where
|
||||
/-- Pack a `List Char` into a `String`. This function is overridden by the
|
||||
compiler and is O(n) in the length of the list. -/
|
||||
mk ::
|
||||
/-- Unpack `String` into a `List Char`. This function is overridden by the
|
||||
compiler and is O(n) in the length of the list. -/
|
||||
data : List Char
|
||||
|
||||
attribute [extern "lean_string_mk"] String.mk
|
||||
attribute [extern "lean_string_data"] String.data
|
||||
def List.flatten : List (List α) → List α
|
||||
| nil => nil
|
||||
| cons l L => List.append l (flatten L)
|
||||
|
||||
/--
|
||||
Decides whether two strings are equal. Normally used via the `DecidableEq String` instance and the
|
||||
`=` operator.
|
||||
Applies a function to each element of the list, returning the resulting list of values.
|
||||
|
||||
At runtime, this function is overridden with an efficient native implementation.
|
||||
`O(|l|)`.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].map f = [f a, f b, f c]`
|
||||
* `[].map Nat.succ = []`
|
||||
* `["one", "two", "three"].map (·.length) = [3, 3, 5]`
|
||||
* `["one", "two", "three"].map (·.reverse) = ["eno", "owt", "eerht"]`
|
||||
-/
|
||||
@[extern "lean_string_dec_eq"]
|
||||
def String.decEq (s₁ s₂ : @& String) : Decidable (Eq s₁ s₂) :=
|
||||
match s₁, s₂ with
|
||||
| ⟨s₁⟩, ⟨s₂⟩ =>
|
||||
dite (Eq s₁ s₂) (fun h => isTrue (congrArg _ h)) (fun h => isFalse (fun h' => String.noConfusion h' (fun h' => absurd h' h)))
|
||||
|
||||
instance : DecidableEq String := String.decEq
|
||||
@[specialize] def List.map (f : α → β) : (l : List α) → List β
|
||||
| nil => nil
|
||||
| cons a as => cons (f a) (map f as)
|
||||
|
||||
/--
|
||||
A byte position in a `String`, according to its UTF-8 encoding.
|
||||
Applies a function that returns a list to each element of a list, and concatenates the resulting
|
||||
lists.
|
||||
|
||||
Character positions (counting the Unicode code points rather than bytes) are represented by plain
|
||||
`Nat`s. Indexing a `String` by a `String.Pos` takes constant time, while character positions need to
|
||||
be translated internally to byte positions, which takes linear time.
|
||||
|
||||
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.endPos` and `p` lies on a UTF-8
|
||||
character boundary.
|
||||
Examples:
|
||||
* `[2, 3, 2].flatMap List.range = [0, 1, 0, 1, 2, 0, 1]`
|
||||
* `["red", "blue"].flatMap String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
|
||||
-/
|
||||
structure String.Pos where
|
||||
/-- Get the underlying byte index of a `String.Pos` -/
|
||||
byteIdx : Nat := 0
|
||||
|
||||
instance : Inhabited String.Pos where
|
||||
default := {}
|
||||
|
||||
instance : DecidableEq String.Pos :=
|
||||
fun ⟨a⟩ ⟨b⟩ => match decEq a b with
|
||||
| isTrue h => isTrue (h ▸ rfl)
|
||||
| isFalse h => isFalse (fun he => String.Pos.noConfusion he fun he => absurd he h)
|
||||
|
||||
/--
|
||||
A region or slice of some underlying string.
|
||||
|
||||
A substring contains an string together with the start and end byte positions of a region of
|
||||
interest. Actually extracting a substring requires copying and memory allocation, while many
|
||||
substrings of the same underlying string may exist with very little overhead, and they are more
|
||||
convenient than tracking the bounds by hand.
|
||||
|
||||
Using its constructor explicitly, it is possible to construct a `Substring` in which one or both of
|
||||
the positions is invalid for the string. Many operations will return unexpected or confusing results
|
||||
if the start and stop positions are not valid. Instead, it's better to use API functions that ensure
|
||||
the validity of the positions in a substring to create and manipulate them.
|
||||
-/
|
||||
structure Substring where
|
||||
/-- The underlying string. -/
|
||||
str : String
|
||||
/-- The byte position of the start of the string slice. -/
|
||||
startPos : String.Pos
|
||||
/-- The byte position of the end of the string slice. -/
|
||||
stopPos : String.Pos
|
||||
|
||||
instance : Inhabited Substring where
|
||||
default := ⟨"", {}, {}⟩
|
||||
|
||||
/--
|
||||
The number of bytes used by the string's UTF-8 encoding.
|
||||
-/
|
||||
@[inline, expose] def Substring.bsize : Substring → Nat
|
||||
| ⟨_, b, e⟩ => e.byteIdx.sub b.byteIdx
|
||||
|
||||
/--
|
||||
The number of bytes used by the string's UTF-8 encoding.
|
||||
|
||||
At runtime, this function takes constant time because the byte length of strings is cached.
|
||||
-/
|
||||
@[extern "lean_string_utf8_byte_size"]
|
||||
def String.utf8ByteSize : (@& String) → Nat
|
||||
| ⟨s⟩ => go s
|
||||
where
|
||||
go : List Char → Nat
|
||||
| .nil => 0
|
||||
| .cons c cs => hAdd (go cs) c.utf8Size
|
||||
|
||||
/--
|
||||
A UTF-8 byte position that points at the end of a string, just after the last character.
|
||||
|
||||
* `"abc".endPos = ⟨3⟩`
|
||||
* `"L∃∀N".endPos = ⟨8⟩`
|
||||
-/
|
||||
@[inline] def String.endPos (s : String) : String.Pos where
|
||||
byteIdx := utf8ByteSize s
|
||||
|
||||
/--
|
||||
Converts a `String` into a `Substring` that denotes the entire string.
|
||||
-/
|
||||
@[inline] def String.toSubstring (s : String) : Substring where
|
||||
str := s
|
||||
startPos := {}
|
||||
stopPos := s.endPos
|
||||
|
||||
/--
|
||||
Converts a `String` into a `Substring` that denotes the entire string.
|
||||
|
||||
This is a version of `String.toSubstring` that doesn't have an `@[inline]` annotation.
|
||||
-/
|
||||
def String.toSubstring' (s : String) : Substring :=
|
||||
s.toSubstring
|
||||
|
||||
/--
|
||||
This function will cast a value of type `α` to type `β`, and is a no-op in the
|
||||
compiler. This function is **extremely dangerous** because there is no guarantee
|
||||
that types `α` and `β` have the same data representation, and this can lead to
|
||||
memory unsafety. It is also logically unsound, since you could just cast
|
||||
`True` to `False`. For all those reasons this function is marked as `unsafe`.
|
||||
|
||||
It is implemented by lifting both `α` and `β` into a common universe, and then
|
||||
using `cast (lcProof : ULift (PLift α) = ULift (PLift β))` to actually perform
|
||||
the cast. All these operations are no-ops in the compiler.
|
||||
|
||||
Using this function correctly requires some knowledge of the data representation
|
||||
of the source and target types. Some general classes of casts which are safe in
|
||||
the current runtime:
|
||||
|
||||
* `Array α` to `Array β` where `α` and `β` have compatible representations,
|
||||
or more generally for other inductive types.
|
||||
* `Quot α r` and `α`.
|
||||
* `@Subtype α p` and `α`, or generally any structure containing only one
|
||||
non-`Prop` field of type `α`.
|
||||
* Casting `α` to/from `NonScalar` when `α` is a boxed generic type
|
||||
(i.e. a function that accepts an arbitrary type `α` and is not specialized to
|
||||
a scalar type like `UInt8`).
|
||||
-/
|
||||
unsafe def unsafeCast {α : Sort u} {β : Sort v} (a : α) : β :=
|
||||
PLift.down (ULift.down.{max u v} (cast lcProof (ULift.up.{max u v} (PLift.up a))))
|
||||
|
||||
|
||||
/-- Auxiliary definition for `panic`. -/
|
||||
/-
|
||||
This is a workaround for `panic` occurring in monadic code. See issue #695.
|
||||
The `panicCore` definition cannot be specialized since it is an extern.
|
||||
When `panic` occurs in monadic code, the `Inhabited α` parameter depends on a
|
||||
`[inst : Monad m]` instance. The `inst` parameter will not be eliminated during
|
||||
specialization if it occurs inside of a binder (to avoid work duplication), and
|
||||
will prevent the actual monad from being "copied" to the code being specialized.
|
||||
When we reimplement the specializer, we may consider copying `inst` if it also
|
||||
occurs outside binders or if it is an instance.
|
||||
-/
|
||||
@[never_extract, extern "lean_panic_fn"]
|
||||
def panicCore {α : Sort u} [Inhabited α] (msg : String) : α := default
|
||||
|
||||
/--
|
||||
`(panic "msg" : α)` has a built-in implementation which prints `msg` to
|
||||
the error buffer. It *does not* terminate execution, and because it is a safe
|
||||
function, it still has to return an element of `α`, so it takes `[Inhabited α]`
|
||||
and returns `default`. It is primarily intended for debugging in pure contexts,
|
||||
and assertion failures.
|
||||
|
||||
Because this is a pure function with side effects, it is marked as
|
||||
`@[never_extract]` so that the compiler will not perform common sub-expression
|
||||
elimination and other optimizations that assume that the expression is pure.
|
||||
-/
|
||||
@[noinline, never_extract]
|
||||
def panic {α : Sort u} [Inhabited α] (msg : String) : α :=
|
||||
panicCore msg
|
||||
|
||||
-- TODO: this be applied directly to `Inhabited`'s definition when we remove the above workaround
|
||||
attribute [nospecialize] Inhabited
|
||||
@[inline] def List.flatMap {α : Type u} {β : Type v} (b : α → List β) (as : List α) : List β := flatten (map b as)
|
||||
|
||||
/--
|
||||
`Array α` is the type of [dynamic arrays](https://en.wikipedia.org/wiki/Dynamic_array) with elements
|
||||
@@ -3452,6 +3290,276 @@ def Array.extract (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : A
|
||||
let sz' := Nat.sub (min stop as.size) start
|
||||
loop sz' start (emptyWithCapacity sz')
|
||||
|
||||
/-- `ByteArray` is like `Array UInt8`, but with an efficient run-time representation as a packed
|
||||
byte buffer. -/
|
||||
structure ByteArray where
|
||||
/-- The data contained in the byte array. -/
|
||||
data : Array UInt8
|
||||
|
||||
attribute [extern "lean_byte_array_mk"] ByteArray.mk
|
||||
attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
/--
|
||||
Constructs a new empty byte array with initial capacity `c`.
|
||||
-/
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def ByteArray.emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := Array.empty }
|
||||
|
||||
/--
|
||||
Constructs a new empty byte array with initial capacity `0`.
|
||||
|
||||
Use `ByteArray.emptyWithCapacity` to create an array with a greater initial capacity.
|
||||
-/
|
||||
def ByteArray.empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
/--
|
||||
Adds an element to the end of an array. The resulting array's size is one greater than the input
|
||||
array. If there are no other references to the array, then it is modified in-place.
|
||||
|
||||
This takes amortized `O(1)` time because `Array α` is represented by a dynamic array.
|
||||
-/
|
||||
@[extern "lean_byte_array_push"]
|
||||
def ByteArray.push : ByteArray → UInt8 → ByteArray
|
||||
| ⟨bs⟩, b => ⟨bs.push b⟩
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| nil, r => r
|
||||
| cons b bs, r => loop bs (r.push b)
|
||||
loop bs ByteArray.empty
|
||||
|
||||
/-- Returns the size of the byte array. -/
|
||||
@[extern "lean_byte_array_size"]
|
||||
def ByteArray.size : (@& ByteArray) → Nat
|
||||
| ⟨bs⟩ => bs.size
|
||||
|
||||
/--
|
||||
Returns the sequence of bytes in a character's UTF-8 encoding.
|
||||
-/
|
||||
def String.utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
let v := c.val.toNat
|
||||
ite (LE.le v 0x7f)
|
||||
(List.cons (UInt8.ofNat v) List.nil)
|
||||
(ite (LE.le v 0x7ff)
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x20) 0xc0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil))
|
||||
(ite (LE.le v 0xffff)
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 4096) 0x10) 0xe0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil)))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 262144) 0x08) 0xf0))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 4096) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod (HDiv.hDiv v 64) 0x40) 0x80))
|
||||
(List.cons
|
||||
(UInt8.ofNat (HAdd.hAdd (HMod.hMod v 0x40) 0x80))
|
||||
List.nil))))))
|
||||
|
||||
/-- Encode a list of characters (Unicode scalar value) in UTF-8. This is an inefficient model
|
||||
implementation. Use `List.asString` instead. -/
|
||||
def List.utf8Encode (l : List Char) : ByteArray :=
|
||||
l.flatMap String.utf8EncodeChar |>.toByteArray
|
||||
|
||||
/-- A byte array is valid UTF-8 if it is of the form `List.Internal.utf8Encode m` for some `m`.
|
||||
|
||||
Note that in order for this definition to be well-behaved it is necessary to know that this `m`
|
||||
is unique. To show this, one defines UTF-8 decoding and shows that encoding and decoding are
|
||||
mutually inverse. -/
|
||||
inductive ByteArray.IsValidUtf8 (b : ByteArray) : Prop
|
||||
/-- Show that a byte -/
|
||||
| intro (m : List Char) (hm : Eq b (List.utf8Encode m))
|
||||
|
||||
/--
|
||||
A string is a sequence of Unicode scalar values.
|
||||
|
||||
At runtime, strings are represented by [dynamic arrays](https://en.wikipedia.org/wiki/Dynamic_array)
|
||||
of bytes using the UTF-8 encoding. Both the size in bytes (`String.utf8ByteSize`) and in characters
|
||||
(`String.length`) are cached and take constant time. Many operations on strings perform in-place
|
||||
modifications when the reference to the string is unique.
|
||||
-/
|
||||
structure String where ofByteArray ::
|
||||
/-- The bytes of the UTF-8 encoding of the string. -/
|
||||
bytes : ByteArray
|
||||
/-- The bytes of the string form valid UTF-8. -/
|
||||
isValidUtf8 : ByteArray.IsValidUtf8 bytes
|
||||
|
||||
attribute [extern "lean_string_to_utf8"] String.bytes
|
||||
|
||||
/--
|
||||
Decides whether two strings are equal. Normally used via the `DecidableEq String` instance and the
|
||||
`=` operator.
|
||||
|
||||
At runtime, this function is overridden with an efficient native implementation.
|
||||
-/
|
||||
@[extern "lean_string_dec_eq"]
|
||||
def String.decEq (s₁ s₂ : @& String) : Decidable (Eq s₁ s₂) :=
|
||||
match s₁, s₂ with
|
||||
| ⟨⟨⟨s₁⟩⟩, _⟩, ⟨⟨⟨s₂⟩⟩, _⟩ =>
|
||||
dite (Eq s₁ s₂) (fun h => match s₁, s₂, h with | _, _, Eq.refl _ => isTrue rfl)
|
||||
(fun h => isFalse
|
||||
(fun h' => h (congrArg (fun s => Array.toList (ByteArray.data (String.bytes s))) h')))
|
||||
|
||||
instance : DecidableEq String := String.decEq
|
||||
|
||||
/--
|
||||
A byte position in a `String`, according to its UTF-8 encoding.
|
||||
|
||||
Character positions (counting the Unicode code points rather than bytes) are represented by plain
|
||||
`Nat`s. Indexing a `String` by a `String.Pos` takes constant time, while character positions need to
|
||||
be translated internally to byte positions, which takes linear time.
|
||||
|
||||
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.endPos` and `p` lies on a UTF-8
|
||||
character boundary.
|
||||
-/
|
||||
structure String.Pos where
|
||||
/-- Get the underlying byte index of a `String.Pos` -/
|
||||
byteIdx : Nat := 0
|
||||
|
||||
instance : Inhabited String.Pos where
|
||||
default := {}
|
||||
|
||||
instance : DecidableEq String.Pos :=
|
||||
fun ⟨a⟩ ⟨b⟩ => match decEq a b with
|
||||
| isTrue h => isTrue (h ▸ rfl)
|
||||
| isFalse h => isFalse (fun he => String.Pos.noConfusion he fun he => absurd he h)
|
||||
|
||||
/--
|
||||
A region or slice of some underlying string.
|
||||
|
||||
A substring contains an string together with the start and end byte positions of a region of
|
||||
interest. Actually extracting a substring requires copying and memory allocation, while many
|
||||
substrings of the same underlying string may exist with very little overhead, and they are more
|
||||
convenient than tracking the bounds by hand.
|
||||
|
||||
Using its constructor explicitly, it is possible to construct a `Substring` in which one or both of
|
||||
the positions is invalid for the string. Many operations will return unexpected or confusing results
|
||||
if the start and stop positions are not valid. Instead, it's better to use API functions that ensure
|
||||
the validity of the positions in a substring to create and manipulate them.
|
||||
-/
|
||||
structure Substring where
|
||||
/-- The underlying string. -/
|
||||
str : String
|
||||
/-- The byte position of the start of the string slice. -/
|
||||
startPos : String.Pos
|
||||
/-- The byte position of the end of the string slice. -/
|
||||
stopPos : String.Pos
|
||||
|
||||
instance : Inhabited Substring where
|
||||
default := ⟨"", {}, {}⟩
|
||||
|
||||
/--
|
||||
The number of bytes used by the string's UTF-8 encoding.
|
||||
-/
|
||||
@[inline, expose] def Substring.bsize : Substring → Nat
|
||||
| ⟨_, b, e⟩ => e.byteIdx.sub b.byteIdx
|
||||
|
||||
/--
|
||||
The number of bytes used by the string's UTF-8 encoding.
|
||||
|
||||
At runtime, this function takes constant time because the byte length of strings is cached.
|
||||
-/
|
||||
@[extern "lean_string_utf8_byte_size"]
|
||||
def String.utf8ByteSize (s : @& String) : Nat :=
|
||||
s.bytes.size
|
||||
|
||||
/--
|
||||
A UTF-8 byte position that points at the end of a string, just after the last character.
|
||||
|
||||
* `"abc".endPos = ⟨3⟩`
|
||||
* `"L∃∀N".endPos = ⟨8⟩`
|
||||
-/
|
||||
@[inline] def String.endPos (s : String) : String.Pos where
|
||||
byteIdx := utf8ByteSize s
|
||||
|
||||
/--
|
||||
Converts a `String` into a `Substring` that denotes the entire string.
|
||||
-/
|
||||
@[inline] def String.toSubstring (s : String) : Substring where
|
||||
str := s
|
||||
startPos := {}
|
||||
stopPos := s.endPos
|
||||
|
||||
/--
|
||||
Converts a `String` into a `Substring` that denotes the entire string.
|
||||
|
||||
This is a version of `String.toSubstring` that doesn't have an `@[inline]` annotation.
|
||||
-/
|
||||
def String.toSubstring' (s : String) : Substring :=
|
||||
s.toSubstring
|
||||
|
||||
/--
|
||||
This function will cast a value of type `α` to type `β`, and is a no-op in the
|
||||
compiler. This function is **extremely dangerous** because there is no guarantee
|
||||
that types `α` and `β` have the same data representation, and this can lead to
|
||||
memory unsafety. It is also logically unsound, since you could just cast
|
||||
`True` to `False`. For all those reasons this function is marked as `unsafe`.
|
||||
|
||||
It is implemented by lifting both `α` and `β` into a common universe, and then
|
||||
using `cast (lcProof : ULift (PLift α) = ULift (PLift β))` to actually perform
|
||||
the cast. All these operations are no-ops in the compiler.
|
||||
|
||||
Using this function correctly requires some knowledge of the data representation
|
||||
of the source and target types. Some general classes of casts which are safe in
|
||||
the current runtime:
|
||||
|
||||
* `Array α` to `Array β` where `α` and `β` have compatible representations,
|
||||
or more generally for other inductive types.
|
||||
* `Quot α r` and `α`.
|
||||
* `@Subtype α p` and `α`, or generally any structure containing only one
|
||||
non-`Prop` field of type `α`.
|
||||
* Casting `α` to/from `NonScalar` when `α` is a boxed generic type
|
||||
(i.e. a function that accepts an arbitrary type `α` and is not specialized to
|
||||
a scalar type like `UInt8`).
|
||||
-/
|
||||
unsafe def unsafeCast {α : Sort u} {β : Sort v} (a : α) : β :=
|
||||
PLift.down (ULift.down.{max u v} (cast lcProof (ULift.up.{max u v} (PLift.up a))))
|
||||
|
||||
|
||||
/-- Auxiliary definition for `panic`. -/
|
||||
/-
|
||||
This is a workaround for `panic` occurring in monadic code. See issue #695.
|
||||
The `panicCore` definition cannot be specialized since it is an extern.
|
||||
When `panic` occurs in monadic code, the `Inhabited α` parameter depends on a
|
||||
`[inst : Monad m]` instance. The `inst` parameter will not be eliminated during
|
||||
specialization if it occurs inside of a binder (to avoid work duplication), and
|
||||
will prevent the actual monad from being "copied" to the code being specialized.
|
||||
When we reimplement the specializer, we may consider copying `inst` if it also
|
||||
occurs outside binders or if it is an instance.
|
||||
-/
|
||||
@[never_extract, extern "lean_panic_fn"]
|
||||
def panicCore {α : Sort u} [Inhabited α] (msg : String) : α := default
|
||||
|
||||
/--
|
||||
`(panic "msg" : α)` has a built-in implementation which prints `msg` to
|
||||
the error buffer. It *does not* terminate execution, and because it is a safe
|
||||
function, it still has to return an element of `α`, so it takes `[Inhabited α]`
|
||||
and returns `default`. It is primarily intended for debugging in pure contexts,
|
||||
and assertion failures.
|
||||
|
||||
Because this is a pure function with side effects, it is marked as
|
||||
`@[never_extract]` so that the compiler will not perform common sub-expression
|
||||
elimination and other optimizations that assume that the expression is pure.
|
||||
-/
|
||||
@[noinline, never_extract]
|
||||
def panic {α : Sort u} [Inhabited α] (msg : String) : α :=
|
||||
panicCore msg
|
||||
|
||||
-- TODO: this be applied directly to `Inhabited`'s definition when we remove the above workaround
|
||||
attribute [nospecialize] Inhabited
|
||||
|
||||
/--
|
||||
The `>>=` operator is overloaded via instances of `bind`.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.NotationExtra
|
||||
import Init.Data.ToString.Name
|
||||
public meta import Init.Data.ToString.Name
|
||||
|
||||
public section
|
||||
|
||||
@@ -131,7 +131,7 @@ macro_rules
|
||||
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
|
||||
builtin_simproc_pattern% $pattern => $n)
|
||||
|
||||
private def mkAttributeCmds
|
||||
private meta def mkAttributeCmds
|
||||
(kind : TSyntax `Lean.Parser.Term.attrKind)
|
||||
(pre? : Option (TSyntax [`Lean.Parser.Tactic.simpPre, `Lean.Parser.Tactic.simpPost]))
|
||||
(ids? : Option (Syntax.TSepArray `ident ","))
|
||||
|
||||
@@ -84,10 +84,11 @@ deriving instance SizeOf for USize
|
||||
deriving instance SizeOf for Char
|
||||
deriving instance SizeOf for Option
|
||||
deriving instance SizeOf for List
|
||||
deriving instance SizeOf for Array
|
||||
deriving instance SizeOf for ByteArray
|
||||
deriving instance SizeOf for String
|
||||
deriving instance SizeOf for String.Pos
|
||||
deriving instance SizeOf for Substring
|
||||
deriving instance SizeOf for Array
|
||||
deriving instance SizeOf for Except
|
||||
deriving instance SizeOf for EStateM.Result
|
||||
|
||||
|
||||
@@ -16,10 +16,6 @@ namespace IO
|
||||
|
||||
private opaque PromisePointed : NonemptyType.{0}
|
||||
|
||||
private structure PromiseImpl (α : Type) : Type where
|
||||
prom : PromisePointed.type
|
||||
h : Nonempty α
|
||||
|
||||
/--
|
||||
`Promise α` allows you to create a `Task α` whose value is provided later by calling `resolve`.
|
||||
|
||||
@@ -33,7 +29,9 @@ Typical usage is as follows:
|
||||
If the promise is dropped without ever being resolved, `promise.result?.get` will return `none`.
|
||||
See `Promise.result!/resultD` for other ways to handle this case.
|
||||
-/
|
||||
def Promise (α : Type) : Type := PromiseImpl α
|
||||
structure Promise (α : Type) : Type where
|
||||
private prom : PromisePointed.type
|
||||
private h : Nonempty α
|
||||
|
||||
instance [s : Nonempty α] : Nonempty (Promise α) :=
|
||||
by exact Nonempty.intro { prom := Classical.choice PromisePointed.property, h := s }
|
||||
@@ -73,9 +71,6 @@ def Promise.result! (promise : @& Promise α) : Task α :=
|
||||
let _ : Nonempty α := promise.h
|
||||
promise.result?.map (sync := true) Option.getOrBlock!
|
||||
|
||||
@[inherit_doc Promise.result!, deprecated Promise.result! (since := "2025-02-05")]
|
||||
def Promise.result := @Promise.result!
|
||||
|
||||
/--
|
||||
Like `Promise.result`, but resolves to `dflt` if the promise is dropped without ever being resolved.
|
||||
-/
|
||||
|
||||
@@ -436,8 +436,8 @@ macro "rfl'" : tactic => `(tactic| set_option smartUnfolding false in with_unfol
|
||||
/--
|
||||
`ac_rfl` proves equalities up to application of an associative and commutative operator.
|
||||
```
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
instance : Std.Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Std.Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by ac_rfl
|
||||
```
|
||||
@@ -1025,7 +1025,7 @@ The form
|
||||
```
|
||||
fun_induction f
|
||||
```
|
||||
(with no arguments to `f`) searches the goal for an unique eligible application of `f`, and uses
|
||||
(with no arguments to `f`) searches the goal for a unique eligible application of `f`, and uses
|
||||
these arguments. An application of `f` is eligible if it is saturated and the arguments that will
|
||||
become targets are free variables.
|
||||
|
||||
@@ -1058,7 +1058,7 @@ The form
|
||||
```
|
||||
fun_cases f
|
||||
```
|
||||
(with no arguments to `f`) searches the goal for an unique eligible application of `f`, and uses
|
||||
(with no arguments to `f`) searches the goal for a unique eligible application of `f`, and uses
|
||||
these arguments. An application of `f` is eligible if it is saturated and the arguments that will
|
||||
become targets are free variables.
|
||||
|
||||
@@ -1563,8 +1563,8 @@ syntax (name := normCastAddElim) "norm_cast_add_elim" ident : command
|
||||
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
|
||||
can also be used, to signify the target of the goal.
|
||||
```
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
instance : Std.Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Std.Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
|
||||
ac_nf
|
||||
|
||||
@@ -16,7 +16,7 @@ Extra tactics and implementation for some tactics defined at `Init/Tactic.lean`
|
||||
-/
|
||||
namespace Lean.Parser.Tactic
|
||||
|
||||
private def expandIfThenElse
|
||||
private meta def expandIfThenElse
|
||||
(ifTk thenTk elseTk pos neg : Syntax)
|
||||
(mkIf : Term → Term → MacroM Term) : MacroM (TSyntax `tactic) := do
|
||||
let mkCase tk holeOrTacticSeq mkName : MacroM (Term × Array (TSyntax `tactic)) := do
|
||||
@@ -24,14 +24,17 @@ private def expandIfThenElse
|
||||
pure (⟨holeOrTacticSeq⟩, #[])
|
||||
else if holeOrTacticSeq.isOfKind `Lean.Parser.Term.hole then
|
||||
pure (← mkName, #[])
|
||||
else if tk.isMissing then
|
||||
pure (← `(sorry), #[])
|
||||
else
|
||||
let hole ← withFreshMacroScope mkName
|
||||
let holeId := hole.raw[1]
|
||||
let case ← (open TSyntax.Compat in `(tactic|
|
||||
case $holeId:ident =>%$tk
|
||||
-- annotate `then/else` with state after `case`
|
||||
with_annotate_state $tk skip
|
||||
$holeOrTacticSeq))
|
||||
let holeId : Ident := ⟨hole.raw[1]⟩
|
||||
let tacticSeq : TSyntax `Lean.Parser.Tactic.tacticSeq := ⟨holeOrTacticSeq⟩
|
||||
-- Use `missing` for ref to ensure that the source range is the same as `holeOrTacticSeq`'s.
|
||||
let tacticSeq : TSyntax `Lean.Parser.Tactic.tacticSeq ← MonadRef.withRef .missing `(tacticSeq|
|
||||
with_annotate_state $tk skip
|
||||
($tacticSeq))
|
||||
let case ← withRef tk <| `(tactic| case $holeId:ident =>%$tk $tacticSeq:tacticSeq)
|
||||
pure (hole, #[case])
|
||||
let (posHole, posCase) ← mkCase thenTk pos `(?pos)
|
||||
let (negHole, negCase) ← mkCase elseTk neg `(?neg)
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
prelude
|
||||
public import Lean.CoreM
|
||||
public import Lean.MonadEnv
|
||||
public import Lean.Compiler.MetaAttr
|
||||
|
||||
public section
|
||||
|
||||
@@ -141,6 +142,15 @@ def throwAttrNotInAsyncCtx (attrName declName : Name) (asyncPrefix? : Option Nam
|
||||
def throwAttrDeclNotOfExpectedType (attrName declName : Name) (givenType expectedType : Expr) : m α :=
|
||||
throwError m!"Cannot add attribute `[{attrName}]`: Declaration `{.ofConstName declName}` has type{indentExpr givenType}\n\
|
||||
but `[{attrName}]` can only be added to declarations of type{indentExpr expectedType}"
|
||||
|
||||
def ensureAttrDeclIsMeta [MonadEnv m] (attrName declName : Name) (attrKind : AttributeKind) : m Unit := do
|
||||
if (← getEnv).header.isModule && !isMeta (← getEnv) declName then
|
||||
throwError m!"Cannot add attribute `[{attrName}]`: Declaration `{.ofConstName declName}` must be marked as `meta`"
|
||||
-- Make sure attributed decls can't refer to private meta imports, which is already checked for
|
||||
-- public decls.
|
||||
if (← getEnv).header.isModule && attrKind == .global && !((← getEnv).setExporting true).contains declName then
|
||||
throwError m!"Cannot add attribute `[{attrName}]`: Declaration `{.ofConstName declName}` must be public"
|
||||
|
||||
end
|
||||
|
||||
/--
|
||||
|
||||
@@ -214,9 +214,9 @@ def getSorryDep (env : Environment) (declName : Name) : Option Name :=
|
||||
|
||||
/-- Returns additional names that compiler env exts may want to call `getModuleIdxFor?` on. -/
|
||||
@[export lean_get_ir_extra_const_names]
|
||||
private def getIRExtraConstNames (env : Environment) (level : OLeanLevel) : Array Name :=
|
||||
private def getIRExtraConstNames (env : Environment) (level : OLeanLevel) (includeDecls := false) : Array Name :=
|
||||
declMapExt.getEntries env |>.toArray.map (·.name)
|
||||
|>.filter fun n => !env.contains n &&
|
||||
|>.filter fun n => (includeDecls || !env.contains n) &&
|
||||
(level == .private || Compiler.LCNF.isDeclPublic env n || isDeclMeta env n)
|
||||
|
||||
end IR
|
||||
|
||||
@@ -44,7 +44,7 @@ partial def inferMeta (decls : Array Decl) : CompilerM Unit := do
|
||||
if !(← getEnv).header.isModule then
|
||||
return
|
||||
for decl in decls do
|
||||
if metaExt.isTagged (← getEnv) decl.name then
|
||||
if isMeta (← getEnv) decl.name then
|
||||
trace[compiler.ir.inferMeta] m!"Marking {decl.name} as meta because it is tagged with `meta`"
|
||||
modifyEnv (setDeclMeta · decl.name)
|
||||
setClosureMeta decl
|
||||
|
||||
@@ -311,21 +311,25 @@ private def isPersistent : Expr → Bool
|
||||
| .fap _ xs => xs.isEmpty -- all global constants are persistent objects
|
||||
| _ => false
|
||||
|
||||
/-- Return true iff `v` at runtime is a scalar value stored in a tagged pointer.
|
||||
We do not need RC operations for this kind of value. -/
|
||||
private def typeForScalarBoxedInTaggedPtr? (v : Expr) : Option IRType :=
|
||||
match v with
|
||||
| .ctor c _ =>
|
||||
some c.type
|
||||
| .lit (.num n) =>
|
||||
if n ≤ maxSmallNat then
|
||||
some .tagged
|
||||
else
|
||||
some .tobject
|
||||
| _ => none
|
||||
/--
|
||||
If `v` is a value that does not need ref counting return `.tagged` so it is never ref counted,
|
||||
otherwise `origt` unmodified.
|
||||
-/
|
||||
private def refineTypeForExpr (v : Expr) (origt : IRType) : IRType :=
|
||||
if origt.isScalar then
|
||||
origt
|
||||
else
|
||||
match v with
|
||||
| .ctor c _ => c.type
|
||||
| .lit (.num n) =>
|
||||
if n ≤ maxSmallNat then
|
||||
.tagged
|
||||
else
|
||||
origt
|
||||
| _ => origt
|
||||
|
||||
private def updateVarInfo (ctx : Context) (x : VarId) (t : IRType) (v : Expr) : Context :=
|
||||
let type := typeForScalarBoxedInTaggedPtr? v |>.getD t
|
||||
let type := refineTypeForExpr v t
|
||||
let isPossibleRef := type.isPossibleRef
|
||||
let isDefiniteRef := type.isDefiniteRef
|
||||
{ ctx with
|
||||
|
||||
@@ -85,6 +85,8 @@ unsafe def registerInitAttrUnsafe (attrName : Name) (runAfterImport : Bool) (ref
|
||||
continue
|
||||
interpretedModInits.modify (·.insert mod)
|
||||
for (decl, initDecl) in modEntries do
|
||||
if getIRPhases ctx.env decl == .runtime then
|
||||
continue
|
||||
if initDecl.isAnonymous then
|
||||
let initFn ← IO.ofExcept <| ctx.env.evalConst (IO Unit) ctx.opts decl
|
||||
initFn
|
||||
|
||||
@@ -109,6 +109,10 @@ def run (declNames : Array Name) : CompilerM (Array IR.Decl) := withAtLeastMaxRe
|
||||
if !(isValidMainType info.type) then
|
||||
throwError "`main` function must have type `(List String →)? IO (UInt32 | Unit | PUnit)`"
|
||||
let decls ← declNames.mapM toDecl
|
||||
-- Check meta accesses now before optimizations may obscure references. This check should stay in
|
||||
-- `lean` if some compilation is moved out.
|
||||
for decl in decls do
|
||||
checkMeta decl
|
||||
let decls := markRecDecls decls
|
||||
let manager ← getPassManager
|
||||
let isCheckEnabled := compiler.check.get (← getOptions)
|
||||
|
||||
@@ -85,6 +85,9 @@ def builtinPassManager : PassManager := {
|
||||
-/
|
||||
simp { etaPoly := true, inlinePartial := true, implementedBy := true } (occurrence := 1),
|
||||
eagerLambdaLifting,
|
||||
-- Should be as early as possible but after `eagerLambdaLifting` to make sure instances are
|
||||
-- checked without nested functions whose bodies specialization does not require access to.
|
||||
checkTemplateVisibility,
|
||||
specialize,
|
||||
simp (occurrence := 2),
|
||||
cse (shouldElimFunDecls := false) (occurrence := 1),
|
||||
@@ -149,6 +152,7 @@ builtin_initialize
|
||||
add := fun declName stx kind => do
|
||||
Attribute.Builtin.ensureNoArgs stx
|
||||
unless kind == AttributeKind.global do throwAttrMustBeGlobal `cpass kind
|
||||
ensureAttrDeclIsMeta `cpass declName kind
|
||||
discard <| addPass declName
|
||||
applicationTime := .afterCompilation
|
||||
}
|
||||
|
||||
@@ -101,24 +101,32 @@ instance : Literal Bool where
|
||||
getLit := getBoolLit
|
||||
mkLit := mkBoolLit
|
||||
|
||||
private partial def getLitAux [Inhabited α] (fvarId : FVarId) (ofNat : Nat → α) (ofNatName : Name) : CompilerM (Option α) := do
|
||||
private def getLitAux (fvarId : FVarId) (ofNat : Nat → α) (ofNatName : Name) : CompilerM (Option α) := do
|
||||
let some (.const declName _ #[.fvar fvarId]) ← findLetValue? fvarId | return none
|
||||
unless declName == ofNatName do return none
|
||||
let some natLit ← getLit fvarId | return none
|
||||
return ofNat natLit
|
||||
|
||||
def mkNatWrapperInstance [Inhabited α] (ofNat : Nat → α) (ofNatName : Name) (toNat : α → Nat) : Literal α where
|
||||
def mkNatWrapperInstance (ofNat : Nat → α) (ofNatName : Name) (toNat : α → Nat) : Literal α where
|
||||
getLit := (getLitAux · ofNat ofNatName)
|
||||
mkLit x := do
|
||||
let helperId ← mkAuxLit <| toNat x
|
||||
return .const ofNatName [] #[.fvar helperId]
|
||||
|
||||
instance : Literal UInt8 := mkNatWrapperInstance UInt8.ofNat ``UInt8.ofNat UInt8.toNat
|
||||
instance : Literal UInt16 := mkNatWrapperInstance UInt16.ofNat ``UInt16.ofNat UInt16.toNat
|
||||
instance : Literal UInt32 := mkNatWrapperInstance UInt32.ofNat ``UInt32.ofNat UInt32.toNat
|
||||
instance : Literal UInt64 := mkNatWrapperInstance UInt64.ofNat ``UInt64.ofNat UInt64.toNat
|
||||
instance : Literal Char := mkNatWrapperInstance Char.ofNat ``Char.ofNat Char.toNat
|
||||
|
||||
def mkUIntInstance (matchLit : LitValue → Option α) (litValueCtor : α → LitValue) : Literal α where
|
||||
getLit fvarId := do
|
||||
let some (.lit litVal) ← findLetValue? fvarId | return none
|
||||
return matchLit litVal
|
||||
mkLit x :=
|
||||
return .lit <| litValueCtor x
|
||||
|
||||
instance : Literal UInt8 := mkUIntInstance (fun | .uint8 x => some x | _ => none) .uint8
|
||||
instance : Literal UInt16 := mkUIntInstance (fun | .uint16 x => some x | _ => none) .uint16
|
||||
instance : Literal UInt32 := mkUIntInstance (fun | .uint32 x => some x | _ => none) .uint32
|
||||
instance : Literal UInt64 := mkUIntInstance (fun | .uint64 x => some x | _ => none) .uint64
|
||||
|
||||
end Literals
|
||||
|
||||
/--
|
||||
@@ -386,7 +394,7 @@ def relationFolders : List (Name × Folder) := [
|
||||
(``UInt64.decLt, Folder.mkBinaryDecisionProcedure UInt64.decLt),
|
||||
(``UInt64.decLe, Folder.mkBinaryDecisionProcedure UInt64.decLe),
|
||||
(``Bool.decEq, Folder.mkBinaryDecisionProcedure Bool.decEq),
|
||||
(``Bool.decEq, Folder.mkBinaryDecisionProcedure String.decEq)
|
||||
(``String.decEq, Folder.mkBinaryDecisionProcedure String.decEq)
|
||||
]
|
||||
|
||||
def conversionFolders : List (Name × Folder) := [
|
||||
|
||||
@@ -103,7 +103,9 @@ partial def Code.toExprM (code : Code) : ToExprM Expr := do
|
||||
| .unreach type => return mkApp (mkConst ``lcUnreachable) (← abstractM type)
|
||||
| .cases c =>
|
||||
let alts ← c.alts.mapM fun
|
||||
| .alt _ params k => withParams params do mkLambdaM params (← k.toExprM)
|
||||
| .alt ctorName params k => do
|
||||
let body ← withParams params do mkLambdaM params (← k.toExprM)
|
||||
return mkApp (mkConst ctorName) body
|
||||
| .default k => k.toExprM
|
||||
return mkAppN (mkConst `cases) (#[← c.discr.toExprM] ++ alts)
|
||||
end
|
||||
|
||||
@@ -669,8 +669,8 @@ where
|
||||
let args := e.getAppArgs
|
||||
let lhs ← liftMetaM do Meta.whnf args[inductVal.numParams + inductVal.numIndices + 1]!
|
||||
let rhs ← liftMetaM do Meta.whnf args[inductVal.numParams + inductVal.numIndices + 2]!
|
||||
let lhs := lhs.toCtorIfLit
|
||||
let rhs := rhs.toCtorIfLit
|
||||
let lhs ← liftMetaM lhs.toCtorIfLit
|
||||
let rhs ← liftMetaM rhs.toCtorIfLit
|
||||
match (← liftMetaM <| Meta.isConstructorApp? lhs), (← liftMetaM <| Meta.isConstructorApp? rhs) with
|
||||
| some lhsCtorVal, some rhsCtorVal =>
|
||||
if lhsCtorVal.name == rhsCtorVal.name then
|
||||
|
||||
@@ -123,27 +123,49 @@ open Meta in
|
||||
Convert a Lean type into a LCNF type used by the code generator.
|
||||
-/
|
||||
partial def toLCNFType (type : Expr) : MetaM Expr := do
|
||||
if ← isProp type then
|
||||
return erasedExpr
|
||||
let type ← whnfEta type
|
||||
match type with
|
||||
| .sort u => return .sort u
|
||||
| .const .. => visitApp type #[]
|
||||
| .lam n d b bi =>
|
||||
withLocalDecl n bi d fun x => do
|
||||
let d ← toLCNFType d
|
||||
let b ← toLCNFType (b.instantiate1 x)
|
||||
if b.isErased then
|
||||
return b
|
||||
else
|
||||
return Expr.lam n d (b.abstract #[x]) bi
|
||||
| .forallE .. => visitForall type #[]
|
||||
| .app .. => type.withApp visitApp
|
||||
| .fvar .. => visitApp type #[]
|
||||
| .proj ``Subtype 0 (.const ``IO.RealWorld.nonemptyType []) =>
|
||||
return mkConst ``lcRealWorld
|
||||
| _ => return mkConst ``lcAny
|
||||
let res ← go type
|
||||
if (← getEnv).header.isModule then
|
||||
-- Under the module system, `type` may reduce differently in different modules, leading to
|
||||
-- IR-level mismatches and thus undefined behavior. We want to make this part independent of the
|
||||
-- current module and its view of the environment but until then, we force the user to make
|
||||
-- involved type definitions exposed by checking whether we would infer a different type in the
|
||||
-- public scope. We ignore failed inference in the public scope because it can easily fail when
|
||||
-- compiling private declarations using private types, and even if that private code should
|
||||
-- escape into different modules, it can only generate a static error there, not a
|
||||
-- miscompilation.
|
||||
-- Note that always using `withExporting` would not always be correct either because it is
|
||||
-- ignored in non-`module`s and thus mismatches with upstream `module`s may again occur.
|
||||
let res'? ← observing <| withExporting <| go type
|
||||
if let .ok res' := res'? then
|
||||
if res != res' then
|
||||
throwError "Compilation failed, locally inferred compilation type{indentD res}\n\
|
||||
differs from type{indentD res'}\n\
|
||||
that would be inferred in other modules. This usually means that a type `def` involved \
|
||||
with the mentioned declarations needs to be `@[expose]`d. This is a current compiler \
|
||||
limitation for `module`s that may be lifted in the future."
|
||||
return res
|
||||
where
|
||||
go type := do
|
||||
if ← isProp type then
|
||||
return erasedExpr
|
||||
let type ← whnfEta type
|
||||
match type with
|
||||
| .sort u => return .sort u
|
||||
| .const .. => visitApp type #[]
|
||||
| .lam n d b bi =>
|
||||
withLocalDecl n bi d fun x => do
|
||||
let d ← go d
|
||||
let b ← go (b.instantiate1 x)
|
||||
if b.isErased then
|
||||
return b
|
||||
else
|
||||
return Expr.lam n d (b.abstract #[x]) bi
|
||||
| .forallE .. => visitForall type #[]
|
||||
| .app .. => type.withApp visitApp
|
||||
| .fvar .. => visitApp type #[]
|
||||
| .proj ``Subtype 0 (.const ``IO.RealWorld.nonemptyType []) =>
|
||||
return mkConst ``lcRealWorld
|
||||
| _ => return mkConst ``lcAny
|
||||
whnfEta (type : Expr) : MetaM Expr := do
|
||||
let type ← whnf type
|
||||
let type' := type.eta
|
||||
@@ -158,12 +180,12 @@ where
|
||||
let d := d.instantiateRev xs
|
||||
withLocalDecl n bi d fun x => do
|
||||
let isBorrowed := isMarkedBorrowed d
|
||||
let mut d := (← toLCNFType d).abstract xs
|
||||
let mut d := (← go d).abstract xs
|
||||
if isBorrowed then
|
||||
d := markBorrowed d
|
||||
return .forallE n d (← visitForall b (xs.push x)) bi
|
||||
| _ =>
|
||||
let e ← toLCNFType (e.instantiateRev xs)
|
||||
let e ← go (e.instantiateRev xs)
|
||||
return e.abstract xs
|
||||
|
||||
visitApp (f : Expr) (args : Array Expr) := do
|
||||
@@ -178,7 +200,7 @@ where
|
||||
if ← isProp arg <||> isPropFormer arg then
|
||||
result := mkApp result erasedExpr
|
||||
else if ← isTypeFormer arg then
|
||||
result := mkApp result (← toLCNFType arg)
|
||||
result := mkApp result (← go arg)
|
||||
else
|
||||
result := mkApp result (mkConst ``lcAny)
|
||||
return result
|
||||
|
||||
@@ -56,6 +56,97 @@ partial def markDeclPublicRec (phase : Phase) (decl : Decl) : CompilerM Unit :=
|
||||
trace[Compiler.inferVisibility] m!"Marking {ref} as opaque because it is used by transparent {decl.name}"
|
||||
markDeclPublicRec phase refDecl
|
||||
|
||||
/-- Checks whether references in the given declaration adhere to phase distinction. -/
|
||||
partial def checkMeta (origDecl : Decl) : CompilerM Unit := do
|
||||
if !(← getEnv).header.isModule then
|
||||
return
|
||||
let isMeta := isMeta (← getEnv) origDecl.name
|
||||
-- If the meta decl is public, we want to ensure it can only refer to public meta imports so that
|
||||
-- references to private imports cannot escape the current module. In particular, we check that
|
||||
-- decls with relevant global attrs are public (`Lean.ensureAttrDeclIsMeta`).
|
||||
let isPublic := !isPrivateName origDecl.name
|
||||
go isMeta isPublic origDecl |>.run' {}
|
||||
where go (isMeta isPublic : Bool) (decl : Decl) : StateT NameSet CompilerM Unit := do
|
||||
decl.value.forCodeM fun code =>
|
||||
for ref in collectUsedDecls code do
|
||||
if (← get).contains ref then
|
||||
continue
|
||||
modify (·.insert ref)
|
||||
if isMeta && isPublic then
|
||||
if let some modIdx := (← getEnv).getModuleIdxFor? ref then
|
||||
if (← getEnv).header.modules[modIdx]?.any (!·.isExported) then
|
||||
throwError "Invalid `meta` definition `{.ofConstName origDecl.name}`, `{.ofConstName ref}` not publicly marked or imported as `meta`"
|
||||
match getIRPhases (← getEnv) ref, isMeta with
|
||||
| .runtime, true =>
|
||||
throwError "Invalid `meta` definition `{.ofConstName origDecl.name}`, may not access declaration `{.ofConstName ref}` not marked or imported as `meta`"
|
||||
| .comptime, false =>
|
||||
throwError "Invalid definition `{.ofConstName origDecl.name}`, may not access declaration `{.ofConstName ref}` marked or imported as `meta`"
|
||||
| _, _ =>
|
||||
-- We allow auxiliary defs to be used in either phase but we need to recursively check
|
||||
-- *their* references in this case. We also need to do this for non-auxiliary defs in case a
|
||||
-- public meta def tries to use a private meta import via a local private meta def :/ .
|
||||
if let some refDecl ← getLocalDecl? ref then
|
||||
go isMeta isPublic refDecl
|
||||
|
||||
/--
|
||||
Checks meta availability just before `evalConst`. This is a "last line of defense" as accesses
|
||||
should have been checked at declaration time in case of attributes. We do not solely want to rely on
|
||||
errors from the interpreter itself as those depend on whether we are running in the server.
|
||||
-/
|
||||
@[export lean_eval_check_meta]
|
||||
private partial def evalCheckMeta (env : Environment) (declName : Name) : Except String Unit := do
|
||||
if !env.header.isModule then
|
||||
return
|
||||
let some decl := getDeclCore? env baseExt declName
|
||||
| return -- We might not have the LCNF available, in which case there's nothing we can do
|
||||
go decl |>.run' {}
|
||||
where go (decl : Decl) : StateT NameSet (Except String) Unit :=
|
||||
decl.value.forCodeM fun code =>
|
||||
for ref in collectUsedDecls code do
|
||||
if (← get).contains ref then
|
||||
continue
|
||||
modify (·.insert ref)
|
||||
if let some localDecl := baseExt.getState env |>.find? ref then
|
||||
go localDecl
|
||||
else
|
||||
if getIRPhases env ref == .runtime then
|
||||
throw s!"Cannot evaluate constant `{declName}` as it uses `{ref}` which is neither marked nor imported as `meta`"
|
||||
|
||||
/--
|
||||
Checks that imports necessary for inlining/specialization are public as otherwise we may run into
|
||||
unknown declarations at the point of inlining/specializing. This is a limitation that we want to
|
||||
lift in the future by moving main compilation into a different process that can use a different
|
||||
import/incremental system.
|
||||
-/
|
||||
partial def checkTemplateVisibility : Pass where
|
||||
occurrence := 0
|
||||
phase := .base
|
||||
name := `checkTemplateVisibility
|
||||
run decls := do
|
||||
if (← getEnv).header.isModule then
|
||||
for decl in decls do
|
||||
-- A private template-like decl cannot directly be used by a different module. If it could be used
|
||||
-- indirectly via a public template-like, we do a recursive check when checking the latter.
|
||||
if !isPrivateName decl.name && (← decl.isTemplateLike) then
|
||||
let isMeta := isMeta (← getEnv) decl.name
|
||||
go isMeta decl decl |>.run' {}
|
||||
return decls
|
||||
where go (isMeta : Bool) (origDecl decl : Decl) : StateT NameSet CompilerM Unit := do
|
||||
decl.value.forCodeM fun code =>
|
||||
for ref in collectUsedDecls code do
|
||||
if (← get).contains ref then
|
||||
continue
|
||||
modify (·.insert decl.name)
|
||||
if let some localDecl := baseExt.getState (← getEnv) |>.find? ref then
|
||||
-- check transitively through local decls
|
||||
if isPrivateName localDecl.name && (← localDecl.isTemplateLike) then
|
||||
go isMeta origDecl localDecl
|
||||
else if let some modIdx := (← getEnv).getModuleIdxFor? ref then
|
||||
if (← getEnv).header.modules[modIdx]?.any (!·.isExported) then
|
||||
throwError "Cannot compile inline/specializing declaration `{.ofConstName origDecl.name}` as \
|
||||
it uses `{.ofConstName ref}` of module `{(← getEnv).header.moduleNames[modIdx]!}` \
|
||||
which must be imported publicly. This limitation may be lifted in the future."
|
||||
|
||||
def inferVisibility (phase : Phase) : Pass where
|
||||
occurrence := 0
|
||||
phase
|
||||
|
||||
@@ -12,15 +12,36 @@ public section
|
||||
|
||||
namespace Lean
|
||||
|
||||
builtin_initialize metaExt : TagDeclarationExtension ←
|
||||
/-- Environment extension collecting `meta` annotations. -/
|
||||
private builtin_initialize metaExt : TagDeclarationExtension ←
|
||||
-- set by `addPreDefinitions`; if we ever make `def` elaboration async, it should be moved to
|
||||
-- remain on the main environment branch
|
||||
mkTagDeclarationExtension (asyncMode := .async .mainEnv)
|
||||
/--
|
||||
Environment extension collecting declarations *could* have been marked as `meta` by the user but
|
||||
were not, so should not allow access to `meta` declarations to surface phase distinction errors as
|
||||
soon as possible.
|
||||
-/
|
||||
private builtin_initialize notMetaExt : EnvExtension NameSet ←
|
||||
registerEnvExtension
|
||||
(mkInitial := pure {})
|
||||
(asyncMode := .async .mainEnv)
|
||||
(replay? := some fun _ _ newEntries s => newEntries.foldl (·.insert) s)
|
||||
|
||||
/-- Marks in the environment extension that the given declaration has been declared by the user as `meta`. -/
|
||||
def addMeta (env : Environment) (declName : Name) : Environment :=
|
||||
metaExt.tag env declName
|
||||
|
||||
/--
|
||||
Marks the given declaration as not being annotated with `meta` even if it could have been by the
|
||||
user.
|
||||
-/
|
||||
def addNotMeta (env : Environment) (declName : Name) : Environment :=
|
||||
if declName.isAnonymous then -- avoid panic from `modifyState` on partial input
|
||||
env
|
||||
else
|
||||
notMetaExt.modifyState (asyncDecl := declName) env (·.insert declName)
|
||||
|
||||
/-- Returns true iff the user has declared the given declaration as `meta`. -/
|
||||
def isMeta (env : Environment) (declName : Name) : Bool :=
|
||||
metaExt.isTagged env declName
|
||||
@@ -29,7 +50,6 @@ def isMeta (env : Environment) (declName : Name) : Bool :=
|
||||
Returns the IR phases of the given declaration that should be considered accessible. Does not take
|
||||
additional IR loaded for language server purposes into account.
|
||||
-/
|
||||
@[export lean_get_ir_phases]
|
||||
def getIRPhases (env : Environment) (declName : Name) : IRPhases := Id.run do
|
||||
if !env.header.isModule then
|
||||
return .all
|
||||
@@ -38,8 +58,15 @@ def getIRPhases (env : Environment) (declName : Name) : IRPhases := Id.run do
|
||||
if isMeta env declName then
|
||||
.comptime
|
||||
else
|
||||
env.header.modules[idx.toNat]?.map (·.irPhases) |>.get!
|
||||
-- Allow `meta`->non-`meta` accesses in the same module
|
||||
| none => if isMeta env declName then .comptime else .all
|
||||
env.header.modules[idx]?.map (·.irPhases) |>.get!
|
||||
| none =>
|
||||
if isMeta env declName then
|
||||
.comptime
|
||||
else if notMetaExt.getState env |>.contains declName then
|
||||
.runtime
|
||||
else
|
||||
-- Allow `meta`->non-`meta` references in the same module for auxiliary declarations the user
|
||||
-- could not have marked as `meta` themselves.
|
||||
.all
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -408,7 +408,9 @@ itself after calling `act` as well as by reuse-handling code such as the one sup
|
||||
|
||||
/-- Restore backtrackable parts of the state. -/
|
||||
def SavedState.restore (b : SavedState) : CoreM Unit :=
|
||||
modify fun s => { s with env := b.env, messages := b.messages, infoState := b.infoState }
|
||||
modify fun s => { s with
|
||||
env := b.env, messages := b.messages, infoState := b.infoState
|
||||
snapshotTasks := b.snapshotTasks }
|
||||
|
||||
private def mkFreshNameImp (n : Name) : CoreM Name := do
|
||||
withFreshMacroScope do
|
||||
|
||||
@@ -8,8 +8,8 @@ module
|
||||
prelude
|
||||
public import Init.Prelude
|
||||
import Init.Data.Stream
|
||||
import Init.Data.Range.Polymorphic.Nat
|
||||
import Init.Data.Range.Polymorphic.Iterators
|
||||
public import Init.Data.Range.Polymorphic.Nat
|
||||
public import Init.Data.Range.Polymorphic.Iterators
|
||||
|
||||
namespace Array
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ def ClientCapabilities.silentDiagnosticSupport (c : ClientCapabilities) : Bool :
|
||||
|
||||
structure LeanServerCapabilities where
|
||||
moduleHierarchyProvider? : Option ModuleHierarchyOptions
|
||||
rpcProvider? : Option RpcOptions
|
||||
deriving FromJson, ToJson
|
||||
|
||||
-- TODO largely unimplemented
|
||||
|
||||
@@ -117,7 +117,7 @@ structure DiagnosticRelatedInformation where
|
||||
/-- Represents a diagnostic, such as a compiler error or warning. Diagnostic objects are only valid in the scope of a resource.
|
||||
|
||||
LSP accepts a `Diagnostic := DiagnosticWith String`.
|
||||
The infoview also accepts `InteractiveDiagnostic := DiagnosticWith (TaggedText MsgEmbed)`.
|
||||
The infoview also accepts `InteractiveDiagnostic := DiagnosticWith InteractiveMessage`.
|
||||
|
||||
[reference](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic) -/
|
||||
structure DiagnosticWith (α : Type) where
|
||||
|
||||
@@ -128,6 +128,13 @@ structure PlainTermGoal where
|
||||
structure ModuleHierarchyOptions where
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure HighlightMatchesOptions where
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure RpcOptions where
|
||||
highlightMatchesProvider? : Option HighlightMatchesOptions := none
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure LeanModule where
|
||||
name : String
|
||||
uri : DocumentUri
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user