mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
Compare commits
63 Commits
array_repl
...
release-st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fec62fc83 | ||
|
|
e18afa8ed1 | ||
|
|
6cf3402f1c | ||
|
|
e3c6909ad5 | ||
|
|
255810db64 | ||
|
|
f094652481 | ||
|
|
3eb07cac44 | ||
|
|
58034bf237 | ||
|
|
7ba7ea4e16 | ||
|
|
4877e84031 | ||
|
|
9c47f395c8 | ||
|
|
3f98b4835c | ||
|
|
a86145b6bb | ||
|
|
c4d3a74f32 | ||
|
|
c74865fbe2 | ||
|
|
93a908469c | ||
|
|
903fe29863 | ||
|
|
84da113355 | ||
|
|
75df4c0b52 | ||
|
|
ad5a746cdd | ||
|
|
2bd3ce5463 | ||
|
|
2b752ec245 | ||
|
|
909ee719aa | ||
|
|
7dd5e957da | ||
|
|
d67e0eea47 | ||
|
|
10bfeba2d9 | ||
|
|
4285f8ba05 | ||
|
|
d8be3ef7a8 | ||
|
|
c924768879 | ||
|
|
c1e76e8976 | ||
|
|
60a9f8e492 | ||
|
|
604133d189 | ||
|
|
d3781bb787 | ||
|
|
87e8da5230 | ||
|
|
727c696d9f | ||
|
|
cf2b7f4c1b | ||
|
|
cd4383b6f3 | ||
|
|
0d9859370a | ||
|
|
c292ae2e0e | ||
|
|
3113847806 | ||
|
|
d275455674 | ||
|
|
a4d10742d3 | ||
|
|
777fba495a | ||
|
|
2e66341f69 | ||
|
|
2e44585ce9 | ||
|
|
e2f0e14b04 | ||
|
|
e801dc96ca | ||
|
|
56a3ac1814 | ||
|
|
6c62f720c8 | ||
|
|
a57efd0a88 | ||
|
|
7e2d6e2254 | ||
|
|
4603e1a6ad | ||
|
|
550d2918b8 | ||
|
|
eb5ad2c03a | ||
|
|
769fe4ebf6 | ||
|
|
8130fdc474 | ||
|
|
41bba59868 | ||
|
|
115f06c32a | ||
|
|
1e1e17cb35 | ||
|
|
831e8d768b | ||
|
|
b4b878b2d0 | ||
|
|
2377f35426 | ||
|
|
c7f706baeb |
2
.github/workflows/pr-release.yml
vendored
2
.github/workflows/pr-release.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v8 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
|
||||
@@ -41,6 +41,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- In order to have the access rights to push to these repositories and merge PRs,
|
||||
you will need to be a member of the `lean-release-managers` team at both `leanprover-community` and `leanprover`.
|
||||
Contact Kim Morrison (@kim-em) to arrange access.
|
||||
- There is an experimental script that will guide you through the steps for each of the repositories below.
|
||||
The script should be invoked as
|
||||
`script/release_steps.py vx.y.x <repo>` where `<repo>` is a case-insensitive substring of the repo name.
|
||||
For example: `script/release_steps.py v4.6.0 batt` will guide you through the steps for the Batteries repository.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
|
||||
- The usual branch name would be `bump_to_v4.6.0`.
|
||||
|
||||
@@ -25,7 +25,10 @@ cp llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
|
||||
echo '
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
|
||||
#define SEM_FAILCRITICALERRORS 0x0001
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);' > stage1/include/clang/windows.h
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
|
||||
#define CP_UTF8 65001
|
||||
__declspec(dllimport) __stdcall int SetConsoleOutputCP(unsigned int wCodePageID);' > stage1/include/clang/windows.h
|
||||
# COFF dependencies
|
||||
cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
|
||||
# runtime
|
||||
|
||||
@@ -65,20 +65,21 @@ def format_markdown_description(pr_number, description):
|
||||
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
|
||||
return f"{link} {description}"
|
||||
|
||||
def commit_types():
|
||||
# see doc/dev/commit_convention.md
|
||||
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
|
||||
|
||||
def count_commit_types(commits):
|
||||
counts = {
|
||||
'total': len(commits),
|
||||
'feat': 0,
|
||||
'fix': 0,
|
||||
'refactor': 0,
|
||||
'doc': 0,
|
||||
'chore': 0
|
||||
}
|
||||
for commit_type in commit_types():
|
||||
counts[commit_type] = 0
|
||||
|
||||
for _, first_line, _ in commits:
|
||||
for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
|
||||
if first_line.startswith(commit_type):
|
||||
counts[commit_type.rstrip(':')] += 1
|
||||
for commit_type in commit_types():
|
||||
if first_line.startswith(f'{commit_type}:'):
|
||||
counts[commit_type] += 1
|
||||
break
|
||||
|
||||
return counts
|
||||
@@ -158,8 +159,9 @@ def main():
|
||||
counts = count_commit_types(commits)
|
||||
print(f"For this release, {counts['total']} changes landed. "
|
||||
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements "
|
||||
f"and {counts['chore']} chores.\n")
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
|
||||
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
|
||||
f"and {counts['style'] + counts['chore']} other changes.\n")
|
||||
|
||||
section_order = sort_sections_order()
|
||||
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
|
||||
|
||||
140
script/release_steps.py
Executable file
140
script/release_steps.py
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Generate release steps script for Lean4 repositories.
|
||||
|
||||
This script helps automate the release process for Lean4 and its dependent repositories
|
||||
by generating step-by-step instructions for updating toolchains, creating tags,
|
||||
and managing branches.
|
||||
|
||||
Usage:
|
||||
python3 release_steps.py <version> <repo>
|
||||
|
||||
Arguments:
|
||||
version: The version to set in the lean-toolchain file (e.g., v4.6.0)
|
||||
repo: A substring of the repository name as specified in release_repos.yml
|
||||
|
||||
Example:
|
||||
python3 release_steps.py v4.6.0 mathlib
|
||||
python3 release_steps.py v4.6.0 batt
|
||||
|
||||
The script reads repository configurations from release_repos.yml in the same directory.
|
||||
Each repository may have specific requirements for:
|
||||
- Branch management
|
||||
- Toolchain updates
|
||||
- Dependency updates
|
||||
- Tagging conventions
|
||||
- Stable branch handling
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import yaml
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
def load_repos_config(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
return yaml.safe_load(f)["repositories"]
|
||||
|
||||
def find_repo(repo_substring, config):
|
||||
pattern = re.compile(re.escape(repo_substring), re.IGNORECASE)
|
||||
matching_repos = [r for r in config if pattern.search(r["name"])]
|
||||
if not matching_repos:
|
||||
print(f"Error: No repository matching '{repo_substring}' found in configuration.")
|
||||
sys.exit(1)
|
||||
if len(matching_repos) > 1:
|
||||
print(f"Error: Multiple repositories matching '{repo_substring}' found in configuration: {', '.join(r['name'] for r in matching_repos)}")
|
||||
sys.exit(1)
|
||||
return matching_repos[0]
|
||||
|
||||
def generate_script(repo, version, config):
|
||||
repo_config = find_repo(repo, config)
|
||||
repo_name = repo_config['name']
|
||||
default_branch = repo_config.get("branch", "main")
|
||||
dependencies = repo_config.get("dependencies", [])
|
||||
requires_tagging = repo_config.get("toolchain-tag", True)
|
||||
has_stable_branch = repo_config.get("stable-branch", True)
|
||||
|
||||
script_lines = [
|
||||
f"cd {repo_name}",
|
||||
"git fetch",
|
||||
f"git checkout {default_branch}",
|
||||
f"git checkout -b bump_to_{version}",
|
||||
f"echo leanprover/lean4:{version} > lean-toolchain",
|
||||
]
|
||||
|
||||
# Special cases for specific repositories
|
||||
if repo_name == "REPL":
|
||||
script_lines.extend([
|
||||
"cd test/Mathlib",
|
||||
f"echo leanprover/lean4:{version} > lean-toolchain",
|
||||
'echo "Please update the dependencies in lakefile.{lean,toml}"',
|
||||
"lake update",
|
||||
"cd ../.."
|
||||
])
|
||||
elif dependencies:
|
||||
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
|
||||
|
||||
script_lines.append("lake update")
|
||||
script_lines.append("")
|
||||
|
||||
if not re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
|
||||
script_lines.extend([
|
||||
"echo 'This repo has nightly-testing infrastructure'",
|
||||
f"git merge bump/{version}",
|
||||
"echo 'Please resolve any conflicts.'",
|
||||
""
|
||||
])
|
||||
|
||||
script_lines.extend([
|
||||
f'git commit -am "chore: bump toolchain to {version}"',
|
||||
"gh pr create",
|
||||
"echo 'Please review the PR and merge it.'",
|
||||
""
|
||||
])
|
||||
|
||||
# Special cases for specific repositories
|
||||
if repo_name == "ProofWidgets4":
|
||||
script_lines.append(f"echo 'Note: Follow the version convention of the repository for tagging.'")
|
||||
elif requires_tagging:
|
||||
script_lines.append(f"git tag -a {version} -m 'Release {version}'")
|
||||
script_lines.append("git push origin --tags")
|
||||
|
||||
if has_stable_branch:
|
||||
script_lines.extend([
|
||||
"git checkout stable",
|
||||
f"git merge {version}",
|
||||
"git push origin stable"
|
||||
])
|
||||
|
||||
return "\n".join(script_lines)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate release steps script for Lean4 repositories.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s v4.6.0 mathlib Generate steps for updating Mathlib to v4.6.0
|
||||
%(prog)s v4.6.0 batt Generate steps for updating Batteries to v4.6.0
|
||||
|
||||
The script will generate shell commands to:
|
||||
1. Update the lean-toolchain file
|
||||
2. Create appropriate branches and commits
|
||||
3. Create pull requests
|
||||
4. Create version tags
|
||||
5. Update stable branches where applicable"""
|
||||
)
|
||||
parser.add_argument("version", help="The version to set in the lean-toolchain file (e.g., v4.6.0)")
|
||||
parser.add_argument("repo", help="A substring of the repository name as specified in release_repos.yml")
|
||||
args = parser.parse_args()
|
||||
|
||||
config_path = os.path.join(os.path.dirname(__file__), "release_repos.yml")
|
||||
config = load_repos_config(config_path)
|
||||
|
||||
script = generate_script(args.repo, args.version, config)
|
||||
print(script)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -555,6 +555,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Array.map_push]
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Array { x // p x }} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem size_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.unattach.size = xs.size := by
|
||||
unfold unattach
|
||||
@@ -676,6 +680,20 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [List.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.all f 0 stop = xs.unattach.all g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.any f 0 stop = xs.unattach.any g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {xs : Array { x // p x }}
|
||||
|
||||
@@ -144,6 +144,8 @@ end List
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem size_eq_length_toList (xs : Array α) : xs.size = xs.toList.length := rfl
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
|
||||
|
||||
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
|
||||
@@ -1090,6 +1092,11 @@ def split (as : Array α) (p : α → Bool) : Array α × Array α :=
|
||||
as.foldl (init := (#[], #[])) fun (as, bs) a =>
|
||||
if p a then (as.push a, bs) else (as, bs.push a)
|
||||
|
||||
def replace [BEq α] (xs : Array α) (a b : α) : Array α :=
|
||||
match xs.finIdxOf? a with
|
||||
| none => xs
|
||||
| some i => xs.set i b
|
||||
|
||||
/-! ### Lexicographic ordering -/
|
||||
|
||||
instance instLT [LT α] : LT (Array α) := ⟨fun as bs => as.toList < bs.toList⟩
|
||||
|
||||
@@ -23,6 +23,18 @@ section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem _root_.List.countP_toArray (l : List α) : countP p l.toArray = l.countP p := by
|
||||
simp [countP]
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons hd tl ih =>
|
||||
simp only [List.foldr_cons, ih, List.countP_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem countP_toList (xs : Array α) : xs.toList.countP p = countP p xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos (xs) (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
|
||||
@@ -150,6 +162,13 @@ section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem _root_.List.count_toArray (l : List α) (a : α) : count a l.toArray = l.count a := by
|
||||
simp [count, List.count_eq_countP]
|
||||
|
||||
@[simp] theorem count_toList (xs : Array α) (a : α) : xs.toList.count a = xs.count a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
|
||||
|
||||
theorem count_push (a b : α) (xs : Array α) :
|
||||
|
||||
@@ -282,6 +282,10 @@ end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
|
||||
simp [eraseIdxIfInBounds, h]
|
||||
|
||||
theorem eraseIdx_eq_take_drop_succ (xs : Array α) (i : Nat) (h) : xs.eraseIdx i = xs.take i ++ xs.drop (i + 1) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.size_toArray] at h
|
||||
|
||||
@@ -299,24 +299,6 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.find?_eq_some_iff_getElem]
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := rfl
|
||||
|
||||
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
|
||||
theorem findFinIdx?_congr {p : α → Bool} {xs ys : Array α} (w : xs = ys) :
|
||||
findFinIdx? p xs = (findFinIdx? p ys).map (fun i => i.cast (by simp [w])) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
cases xs
|
||||
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
|
||||
rw [findFinIdx?_congr List.unattach_toArray]
|
||||
simp [Function.comp_def]
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
|
||||
@@ -542,6 +524,47 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := rfl
|
||||
|
||||
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
|
||||
theorem findFinIdx?_congr {p : α → Bool} {xs ys : Array α} (w : xs = ys) :
|
||||
findFinIdx? p xs = (findFinIdx? p ys).map (fun i => i.cast (by simp [w])) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α → Bool} :
|
||||
xs.findFinIdx? p =
|
||||
(xs.findIdx? p).pmap
|
||||
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact ⟨i, m.choose⟩)
|
||||
(fun i h => h) := by
|
||||
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
|
||||
|
||||
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
|
||||
xs.findFinIdx? p = none ↔ ∀ x, x ∈ xs → ¬ p x := by
|
||||
simp [findFinIdx?_eq_pmap_findIdx?]
|
||||
|
||||
@[simp]
|
||||
theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.size} :
|
||||
xs.findFinIdx? p = some i ↔
|
||||
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
|
||||
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
|
||||
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
|
||||
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
|
||||
· rintro ⟨h, w⟩
|
||||
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
cases xs
|
||||
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
|
||||
rw [findFinIdx?_congr List.unattach_toArray]
|
||||
simp [Function.comp_def]
|
||||
|
||||
/-! ### idxOf
|
||||
|
||||
The verification API for `idxOf` is still incomplete.
|
||||
@@ -579,10 +602,26 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.idxOf?_eq_none_iff]
|
||||
|
||||
/-! ### finIdxOf? -/
|
||||
/-! ### finIdxOf?
|
||||
|
||||
The verification API for `finIdxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := rfl
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.finIdxOf? a = none ↔ a ∉ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.finIdxOf?_eq_none_iff]
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Fin xs.size} :
|
||||
xs.finIdxOf? a = some i ↔ xs[i] = a ∧ ∀ j (_ : j < i), ¬xs[j] = a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.finIdxOf?_eq_some_iff]
|
||||
|
||||
end Array
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
|
||||
@@ -23,6 +23,9 @@ open Nat
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
@[simp] theorem mapM_id {xs : Array α} {f : α → Id β} : xs.mapM f = xs.map f := by
|
||||
induction xs; simp_all
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {xs ys : Array α} :
|
||||
(xs ++ ys).mapM f = (return (← xs.mapM f) ++ (← ys.mapM f)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
|
||||
@@ -16,6 +16,25 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem ofFn_zero (f : Fin 0 → α) : ofFn f = #[] := rfl
|
||||
|
||||
theorem ofFn_succ (f : Fin (n+1) → α) :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split <;> rename_i h₃
|
||||
· rfl
|
||||
· congr
|
||||
simp at h₁ h₂
|
||||
omega
|
||||
|
||||
@[simp] theorem _rooy_.List.toArray_ofFn (f : Fin n → α) : (List.ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem toList_ofFn (f : Fin n → α) : (Array.ofFn f).toList = List.ofFn f := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
|
||||
rw [← Array.toList_inj]
|
||||
|
||||
@@ -13,6 +13,7 @@ import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Nat.Mod
|
||||
import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.Pow
|
||||
|
||||
set_option linter.missingDocs true
|
||||
@@ -569,6 +570,11 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||||
have p : 0 ≤ i % (2^n : Nat) := by omega
|
||||
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
|
||||
|
||||
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
|
||||
(h : -2 ^ (w - 1) ≤ n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
|
||||
have hw : w = (w - 1) + 1 := by omega
|
||||
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
|
||||
|
||||
@[simp] theorem ofInt_natCast (w n : Nat) :
|
||||
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
|
||||
|
||||
@@ -2693,6 +2699,9 @@ theorem toInt_neg {x : BitVec w} :
|
||||
rw [← BitVec.zero_sub, toInt_sub]
|
||||
simp [BitVec.toInt_ofNat]
|
||||
|
||||
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
|
||||
eq_of_toInt_eq (by simp [toInt_neg])
|
||||
|
||||
@[simp] theorem toFin_neg (x : BitVec n) :
|
||||
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
|
||||
rfl
|
||||
@@ -4109,9 +4118,7 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
|
||||
|
||||
theorem msb_eq_toInt {x : BitVec w}:
|
||||
x.msb = decide (x.toInt < 0) := by
|
||||
by_cases h : x.msb <;>
|
||||
· simp [h, toInt_eq_msb_cond]
|
||||
omega
|
||||
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
|
||||
|
||||
theorem msb_eq_toNat {x : BitVec w}:
|
||||
x.msb = decide (x.toNat ≥ 2 ^ (w - 1)) := by
|
||||
|
||||
@@ -45,6 +45,7 @@ theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
|
||||
theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩ :=
|
||||
⟨fun h i hi => h ⟨i, hi⟩, fun h ⟨i, hi⟩ => h i hi⟩
|
||||
|
||||
/-- Restatement of `Fin.mk.injEq` as an `iff`. -/
|
||||
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := Fin.ext_iff
|
||||
|
||||
@@ -55,6 +56,14 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@[simp] theorem mk_eq_zero {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
(⟨a, ha⟩ : Fin n) = 0 ↔ a = 0 :=
|
||||
mk.inj_iff
|
||||
|
||||
@[simp] theorem zero_eq_mk {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
0 = (⟨a, ha⟩ : Fin n) ↔ a = 0 := by
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
|
||||
(Fin.ofNat' n a).val = a % n := rfl
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@ open Nat
|
||||
This file defines the `Int` type as well as
|
||||
|
||||
* coercions, conversions, and compatibility with numeric literals,
|
||||
* basic arithmetic operations add/sub/mul/div/mod/pow,
|
||||
* basic arithmetic operations add/sub/mul/pow,
|
||||
* a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
|
||||
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
|
||||
* decidability of equality, relations and `NonNeg`.
|
||||
|
||||
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
|
||||
-/
|
||||
|
||||
/--
|
||||
|
||||
@@ -227,33 +227,4 @@ theorem cooper_resolution_dvd_right
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_le_of_neg_le lower
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_mul _ _ ▸ dvd
|
||||
|
||||
/--
|
||||
Left Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_left
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < a ∧ b * k + b * p ≤ a * q ∧ a ∣ k + p) := by
|
||||
have h := cooper_resolution_dvd_left
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt a_pos), Int.one_dvd, and_true,
|
||||
and_self] at h
|
||||
exact h
|
||||
|
||||
/--
|
||||
Right Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_right
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < b ∧ a * k + b * p ≤ a * q ∧ b ∣ k - q) := by
|
||||
have h := cooper_resolution_dvd_right
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
have : ∀ k : Int, (b ∣ -k + q) ↔ (b ∣ k - q) := by
|
||||
intro k
|
||||
rw [← Int.dvd_neg, Int.neg_add, Int.neg_neg, Int.sub_eq_add_neg]
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.one_dvd, and_true,
|
||||
and_self, ← Int.neg_eq_neg_one_mul, this] at h
|
||||
exact h
|
||||
end Int
|
||||
|
||||
@@ -21,25 +21,25 @@ and satisfy `x / 0 = 0` and `x % 0 = x`.
|
||||
In early versions of Lean, the typeclasses provided by `/` and `%`
|
||||
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
|
||||
|
||||
However we decided it was better to use `ediv` and `emod`,
|
||||
However we decided it was better to use `ediv` and `emod` for the default typeclass instances,
|
||||
as they are consistent with the conventions used in SMTLib, and Mathlib,
|
||||
and often mathematical reasoning is easier with these conventions.
|
||||
|
||||
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
|
||||
|
||||
In September 2024, we decided to do this rename (with deprecations in place),
|
||||
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
|
||||
ever need to use these functions and their associated lemmas.
|
||||
|
||||
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
|
||||
In December 2024, we removed `div` and `mod`, but have not yet renamed `ediv` and `emod`.
|
||||
-/
|
||||
|
||||
/-! ### E-rounding division
|
||||
This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
|
||||
This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of `Int.div` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
|
||||
Integer division. This version of integer division uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
This is the function powering the `/` notation on integers.
|
||||
@@ -71,7 +71,7 @@ def ediv : (@& Int) → (@& Int) → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the E-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
@@ -229,7 +229,7 @@ def fdiv : Int → Int → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ m / succ n)
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the F-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
|
||||
@@ -268,11 +268,14 @@ Balanced mod (and balanced div) are a division and modulus pair such
|
||||
that `b * (Int.bdiv a b) + Int.bmod a b = a` and
|
||||
`-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
|
||||
|
||||
This is used in Omega as well as signed bitvectors.
|
||||
Note that unlike `emod`, `fmod`, and `tmod`,
|
||||
`bmod` takes a natural number as the second argument, rather than an integer.
|
||||
|
||||
This function is used in `omega` as well as signed bitvectors.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced modulus. This version of Integer modulus uses the
|
||||
Balanced modulus. This version of integer modulus uses the
|
||||
balanced rounding convention, which guarantees that
|
||||
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
|
||||
@@ -18,7 +18,7 @@ open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
|
||||
-- /-! ### dvd -/
|
||||
/-! ### dvd -/
|
||||
|
||||
protected theorem dvd_def (a b : Int) : (a ∣ b) = Exists (fun c => b = a * c) := rfl
|
||||
|
||||
@@ -67,7 +67,7 @@ protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natAbs := by
|
||||
rw [← natAbs_dvd_natAbs, natAbs_ofNat]
|
||||
|
||||
/-! ### *div zero -/
|
||||
/-! ### ediv zero -/
|
||||
|
||||
@[simp] theorem zero_ediv : ∀ b : Int, 0 / b = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
@@ -77,7 +77,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
/-! ### mod zero -/
|
||||
/-! ### emod zero -/
|
||||
|
||||
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl
|
||||
|
||||
@@ -89,7 +89,6 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
@@ -106,12 +105,17 @@ where
|
||||
← Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
|
||||
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
|
||||
|
||||
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
|
||||
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
|
||||
rw [Int.mul_comm]; exact emod_add_ediv ..
|
||||
|
||||
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
|
||||
rw [Int.add_comm]; exact emod_add_ediv ..
|
||||
|
||||
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
|
||||
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
rw [Int.mul_comm]; exact ediv_add_emod ..
|
||||
|
||||
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
|
||||
rw [← Int.add_sub_cancel (a % b), emod_add_ediv]
|
||||
|
||||
@@ -170,7 +174,7 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c +
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
| Int.ofNat (b+1), _ =>
|
||||
@@ -178,6 +182,9 @@ theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0
|
||||
norm_cast
|
||||
simp
|
||||
|
||||
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
|
||||
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
theorem emod_nonneg : ∀ (a : Int) {b : Int}, b ≠ 0 → 0 ≤ a % b
|
||||
|
||||
@@ -94,6 +94,14 @@ theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
|
||||
protected theorem mul_dvd_mul_iff_left {a b c : Int} (h : a ≠ 0) : (a * b) ∣ (a * c) ↔ b ∣ c :=
|
||||
⟨by rintro ⟨d, h'⟩; exact ⟨d, by rw [Int.mul_assoc] at h'; exact (mul_eq_mul_left_iff h).mp h'⟩,
|
||||
by rintro ⟨d, rfl⟩; exact ⟨d, by simp [Int.mul_assoc]⟩⟩
|
||||
|
||||
protected theorem mul_dvd_mul_iff_right {a b c : Int} (h : a ≠ 0) : (b * a) ∣ (c * a) ↔ b ∣ c := by
|
||||
rw [Int.mul_comm b a, Int.mul_comm c a]
|
||||
exact Int.mul_dvd_mul_iff_left h
|
||||
|
||||
/-! ### *div zero -/
|
||||
|
||||
@[simp] protected theorem zero_tdiv : ∀ b : Int, tdiv 0 b = 0
|
||||
@@ -234,6 +242,13 @@ theorem tdiv_eq_fdiv {a b : Int} :
|
||||
rw [fdiv_eq_tdiv]
|
||||
omega
|
||||
|
||||
|
||||
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.tdiv b = a / b := by
|
||||
simp [tdiv_eq_ediv, h]
|
||||
|
||||
theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.fdiv b = a / b := by
|
||||
simp [fdiv_eq_ediv, h]
|
||||
|
||||
/-! ### mod zero -/
|
||||
|
||||
@[simp] theorem zero_tmod (b : Int) : tmod 0 b = 0 := by cases b <;> simp [tmod]
|
||||
@@ -251,9 +266,6 @@ theorem tdiv_eq_fdiv {a b : Int} :
|
||||
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
rw [Int.mul_comm]; exact ediv_add_emod ..
|
||||
|
||||
theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
|
||||
| ofNat m, -[n+1] => by
|
||||
@@ -274,9 +286,11 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
|
||||
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
|
||||
rw [Int.add_comm]; apply tmod_add_tdiv ..
|
||||
|
||||
/-- Variant of `tmod_add_tdiv` with the multiplication written the other way around. -/
|
||||
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
|
||||
rw [Int.mul_comm]; apply tmod_add_tdiv
|
||||
|
||||
/-- Variant of `tdiv_add_tmod` with the multiplication written the other way around. -/
|
||||
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
|
||||
rw [Int.mul_comm]; apply tdiv_add_tmod
|
||||
|
||||
@@ -300,9 +314,17 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
|
||||
show -(↑(succ m % succ n) : Int) + -↑(succ n * (succ m / succ n)) = -↑(succ m)
|
||||
rw [← Int.neg_add]; exact congrArg (-ofNat ·) <| Nat.mod_add_div ..
|
||||
|
||||
/-- Variant of `fmod_add_fdiv` with the multiplication written the other way around. -/
|
||||
theorem fmod_add_fdiv' (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
|
||||
rw [Int.mul_comm]; exact fmod_add_fdiv ..
|
||||
|
||||
theorem fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
|
||||
rw [Int.add_comm]; exact fmod_add_fdiv ..
|
||||
|
||||
/-- Variant of `fdiv_add_fmod` with the multiplication written the other way around. -/
|
||||
theorem fdiv_add_fmod' (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
|
||||
rw [Int.mul_comm]; exact fdiv_add_fmod ..
|
||||
|
||||
theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
|
||||
rw [← Int.add_sub_cancel (a.fmod b), fmod_add_fdiv]
|
||||
|
||||
@@ -396,6 +418,11 @@ theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
|
||||
rw [Int.div_def, ediv]
|
||||
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
|
||||
|
||||
theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b := by
|
||||
rw [Int.div_def]
|
||||
match a, b, ha, hb with
|
||||
| .negSucc a, .negSucc b, _, _ => apply ofNat_succ_pos
|
||||
|
||||
theorem ediv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a / b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. ▸ Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
@@ -446,6 +473,10 @@ protected theorem ediv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a / b = c :=
|
||||
Int.ediv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a * b = c) : a = c / b :=
|
||||
(Int.ediv_eq_of_eq_mul_left H1 H2.symm).symm
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
theorem mod_def' (m n : Int) : m % n = emod m n := rfl
|
||||
@@ -715,16 +746,100 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
|
||||
|
||||
/-! ### tdiv -/
|
||||
|
||||
@[simp] protected theorem tdiv_one : ∀ a : Int, a.tdiv 1 = a
|
||||
| (n:Nat) => congrArg ofNat (Nat.div_one _)
|
||||
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_neg : ∀ a b : Int, a.tdiv (-b) = -(a.tdiv b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
|
||||
/-!
|
||||
We don't give `tdiv` versions of
|
||||
* `add_mul_ediv_right : c ≠ 0 → (a + b * c) / c = a / c + b`
|
||||
* `add_mul_ediv_left : b ≠ 0 → (a + b * c) / b = a / b + c`
|
||||
* `add_ediv_of_dvd_right : c ∣ b → (a + b) / c = a / c + b / c`
|
||||
* `add_ediv_of_dvd_left : c ∣ a → (a + b) / c = a / c + b / c`
|
||||
because they all involve awkward off-by-one corrections.
|
||||
-/
|
||||
|
||||
@[simp] theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).tdiv b = a := by
|
||||
rw [tdiv_eq_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ H]
|
||||
|
||||
@[simp] theorem mul_tdiv_cancel_left (b : Int) (H : a ≠ 0) : (a * b).tdiv a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_tdiv_cancel _ H
|
||||
|
||||
-- There's no good analogues of `ediv_nonneg_iff_of_pos`, `ediv_neg'`, or `negSucc_ediv`
|
||||
-- for `tdiv`.
|
||||
|
||||
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.tdiv b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0) : 0 ≤ a.tdiv b := by
|
||||
rw [tdiv_eq_ediv]
|
||||
split <;> rename_i h
|
||||
· simpa using ediv_nonneg_of_nonpos_of_nonpos Ha Hb
|
||||
· simp at h
|
||||
by_cases h' : b = 0
|
||||
· subst h'
|
||||
simp
|
||||
· replace h' : b < 0 := by omega
|
||||
rw [sign_eq_neg_one_of_neg h']
|
||||
have : 0 < a / b := by
|
||||
by_cases h'' : a = 0
|
||||
· subst h''
|
||||
simp at h
|
||||
· replace h'' : a < 0 := by omega
|
||||
exact ediv_pos_of_neg_of_neg h'' h'
|
||||
omega
|
||||
|
||||
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.tdiv b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. ▸ Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.tdiv b = 0 :=
|
||||
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
|
||||
|
||||
@[simp] theorem mul_tdiv_mul_of_pos {a : Int}
|
||||
(b c : Int) (H : 0 < a) : (a * b).tdiv (a * c) = b.tdiv c := by
|
||||
rw [tdiv_eq_ediv, mul_ediv_mul_of_pos _ _ H, tdiv_eq_ediv]
|
||||
simp only [sign_mul]
|
||||
by_cases h : 0 ≤ b
|
||||
· rw [if_pos, if_pos (.inl h)]
|
||||
left
|
||||
exact Int.mul_nonneg (Int.le_of_lt H) h
|
||||
· have H' : a ≠ 0 := by omega
|
||||
simp only [Int.mul_dvd_mul_iff_left H']
|
||||
by_cases h' : c ∣ b
|
||||
· simp [h']
|
||||
· rw [if_neg, if_neg]
|
||||
· simp [sign_eq_one_of_pos H]
|
||||
· simp [h']; omega
|
||||
· simp_all only [Int.not_le, ne_eq, or_false]
|
||||
exact Int.mul_neg_of_pos_of_neg H h
|
||||
|
||||
@[simp] theorem mul_tdiv_mul_of_pos_left
|
||||
(a : Int) {b : Int} (c : Int) (H : 0 < b) : (a * b).tdiv (c * b) = a.tdiv c := by
|
||||
rw [Int.mul_comm, Int.mul_comm c, mul_tdiv_mul_of_pos _ _ H]
|
||||
|
||||
@[simp] protected theorem tdiv_one : ∀ a : Int, a.tdiv 1 = a
|
||||
| (n:Nat) => congrArg ofNat (Nat.div_one _)
|
||||
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.tdiv a :=
|
||||
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.tdiv b = c :=
|
||||
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a * b = c) : a = c.tdiv b :=
|
||||
(Int.tdiv_eq_of_eq_mul_left H1 H2.symm).symm
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem neg_tdiv : ∀ a b : Int, (-a).tdiv b = -(a.tdiv b)
|
||||
| 0, n => by simp [Int.neg_zero]
|
||||
@@ -734,33 +849,6 @@ unseal Nat.div in
|
||||
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
|
||||
|
||||
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a.tdiv b :=
|
||||
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_zero_le _
|
||||
|
||||
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a.tdiv b ≤ 0 :=
|
||||
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. ▸ Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
|
||||
|
||||
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.tdiv b = 0 :=
|
||||
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b).tdiv b = a :=
|
||||
have : ∀ {a b : Nat}, (b : Int) ≠ 0 → (tdiv (a * b) b : Int) = a := fun H => by
|
||||
rw [← ofNat_mul, ← ofNat_tdiv,
|
||||
Nat.mul_div_cancel _ <| Nat.pos_of_ne_zero <| Int.ofNat_ne_zero.1 H]
|
||||
match a, b, a.eq_nat_or_neg, b.eq_nat_or_neg with
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inl rfl⟩ => this H
|
||||
| _, _, ⟨a, .inl rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.mul_neg, Int.neg_tdiv, Int.tdiv_neg, Int.neg_neg,
|
||||
this (Int.neg_ne_zero.1 H)]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inl rfl⟩ => by rw [Int.neg_mul, Int.neg_tdiv, this H]
|
||||
| _, _, ⟨a, .inr rfl⟩, ⟨b, .inr rfl⟩ => by
|
||||
rw [Int.neg_mul_neg, Int.tdiv_neg, this (Int.neg_ne_zero.1 H)]
|
||||
|
||||
@[simp] protected theorem mul_tdiv_cancel_left (b : Int) (H : a ≠ 0) : (a * b).tdiv a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_tdiv_cancel _ H
|
||||
|
||||
@[simp] protected theorem tdiv_self {a : Int} (H : a ≠ 0) : a.tdiv a = 1 := by
|
||||
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
@@ -796,14 +884,7 @@ theorem tdiv_dvd_tdiv : ∀ {a b c : Int}, a ∣ b → b ∣ c → b.tdiv a ∣
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inl rfl⟩ => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
|
||||
| _, _, ⟨_, .inr rfl⟩, ⟨_, .inr rfl⟩ => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
|
||||
|
||||
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
|
||||
(H1 : a ≠ 0) (H2 : a * b = c) : b = c.tdiv a :=
|
||||
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
|
||||
|
||||
/-! ### (t-)mod -/
|
||||
/-! ### tmod -/
|
||||
|
||||
theorem ofNat_tmod (m n : Nat) : (↑(m % n) : Int) = tmod m n := rfl
|
||||
|
||||
@@ -878,9 +959,6 @@ protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
|
||||
(H1 : b ∣ a) (H2 : a.tdiv b = c) : a = c * b := by
|
||||
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
|
||||
|
||||
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
|
||||
(H1 : b ≠ 0) (H2 : a = c * b) : a.tdiv b = c :=
|
||||
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
|
||||
|
||||
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d ∣ n) (H : n.tdiv d = 0) : n = 0 := by
|
||||
rw [← Int.mul_tdiv_cancel' h, H, Int.mul_zero]
|
||||
@@ -968,19 +1046,6 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
|
||||
@[simp] theorem fmod_self {a : Int} : a.fmod a = 0 := by
|
||||
have := mul_fmod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
/-! ### Theorems crossing div/mod versions -/
|
||||
|
||||
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b ∣ a) : a.tdiv b = a / b := by
|
||||
by_cases b0 : b = 0
|
||||
· simp [b0]
|
||||
· rw [Int.tdiv_eq_iff_eq_mul_left b0 h, ← Int.ediv_eq_iff_eq_mul_left b0 h]
|
||||
|
||||
theorem fdiv_eq_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → a.fdiv b = a / b
|
||||
| _, b, ⟨c, rfl⟩ => by
|
||||
by_cases bz : b = 0
|
||||
· simp [bz]
|
||||
· rw [mul_fdiv_cancel_left _ bz, mul_ediv_cancel_left _ bz]
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -46,4 +46,23 @@ theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
@[simp] theorem toNat_eq_zero : ∀ {n : Int}, n.toNat = 0 ↔ n ≤ 0 := by omega
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
obtain ⟨a, rfl⟩ := h
|
||||
rw [natAbs_mul] at h₁
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
have := le_bmod (x := n) (m := m) (by omega)
|
||||
have := bmod_lt (x := n) (m := m) (by omega)
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
end Int
|
||||
|
||||
@@ -9,6 +9,7 @@ import Init.Data.Prod
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.DivMod.Bootstrap
|
||||
import Init.Data.Int.Cooper
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.RArray
|
||||
import Init.Data.AC
|
||||
@@ -531,8 +532,9 @@ def Poly.isValidLe (p : Poly) : Bool :=
|
||||
| .num k => k ≤ 0
|
||||
| _ => false
|
||||
|
||||
attribute [-simp] Int.not_le in
|
||||
theorem le_eq_false (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isUnsatLe → (lhs.denote ctx ≤ rhs.denote ctx) = False := by
|
||||
simp [Poly.isUnsatLe] <;> split <;> simp
|
||||
simp only [Poly.isUnsatLe] <;> split <;> simp
|
||||
next p k h =>
|
||||
intro h'
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
@@ -820,7 +822,7 @@ def le_neg_cert (p₁ p₂ : Poly) : Bool :=
|
||||
theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ → ¬ p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 := by
|
||||
simp [le_neg_cert]
|
||||
intro; subst p₂; simp; intro h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg <| Int.lt_of_not_ge h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg h
|
||||
simp at h
|
||||
exact h
|
||||
|
||||
@@ -846,9 +848,6 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
|
||||
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe → p.denote' ctx ≤ 0 → False := by
|
||||
simp [Poly.isUnsatLe]; split <;> simp
|
||||
intro h₁ h₂
|
||||
have := Int.lt_of_le_of_lt h₂ h₁
|
||||
simp at this
|
||||
|
||||
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 → p₂.denote' ctx = 0 := by
|
||||
simp at h
|
||||
@@ -1006,6 +1005,474 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [h, ←Int.sub_eq_add_neg, Int.sub_self]
|
||||
|
||||
def Poly.isUnsatDiseq (p : Poly) : Bool :=
|
||||
match p with
|
||||
| .num 0 => true
|
||||
| _ => false
|
||||
|
||||
theorem diseq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx ≠ 0 → p₂.denote' ctx ≠ 0 := by
|
||||
simp at h
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
simp at h
|
||||
simp [*]
|
||||
|
||||
theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p' k → p.denote' ctx ≠ 0 → p'.denote' ctx ≠ 0 := by
|
||||
simp [eq_coeff_cert]
|
||||
intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) → p.denote' ctx ≠ 0 → p'.denote' ctx ≠ 0 := by
|
||||
simp; intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq → p.denote' ctx ≠ 0 → False := by
|
||||
simp [Poly.isUnsatDiseq] <;> split <;> simp
|
||||
|
||||
def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
let a := p₁.coeff x
|
||||
let b := p₂.coeff x
|
||||
a != 0 && p₃ == (p₁.mul b |>.combine (p₂.mul (-a)))
|
||||
|
||||
theorem eq_diseq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: diseq_eq_subst_cert x p₁ p₂ p₃ → p₁.denote' ctx = 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≠ 0 := by
|
||||
simp [diseq_eq_subst_cert]
|
||||
intros _ _; subst p₃
|
||||
intro h₁ h₂
|
||||
simp [*]
|
||||
|
||||
theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: eq_of_core_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ p₂.denote' ctx → p₃.denote' ctx ≠ 0 := by
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [← Int.sub_eq_zero] at h
|
||||
rw [←Int.sub_eq_add_neg]; assumption
|
||||
|
||||
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
|
||||
p₂ == p₁.mul (-1)
|
||||
|
||||
theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
|
||||
: eq_of_le_ge_cert p₁ p₂ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₁.denote' ctx = 0 := by
|
||||
simp [eq_of_le_ge_cert]
|
||||
intro; subst p₂; simp
|
||||
intro h₁ h₂
|
||||
replace h₂ := Int.neg_le_of_neg_le h₂; simp at h₂
|
||||
simp [Int.eq_iff_le_and_ge, *]
|
||||
|
||||
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
|
||||
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
|
||||
p₃ == p₁.addConst 1
|
||||
|
||||
theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: le_of_le_diseq_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp [le_of_le_diseq_cert]
|
||||
have (a : Int) : a ≤ 0 → ¬ a = 0 → 1 + a ≤ 0 := by
|
||||
intro h₁ h₂; cases (Int.lt_or_gt_of_ne h₂)
|
||||
next => apply Int.le_of_lt_add_one; rw [Int.add_comm, Int.add_lt_add_iff_right]; assumption
|
||||
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
|
||||
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
|
||||
|
||||
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
|
||||
p₂ == p₁.addConst 1 &&
|
||||
p₃ == (p₁.mul (-1)).addConst 1
|
||||
|
||||
theorem diseq_split (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → p₂.denote' ctx ≤ 0 ∨ p₃.denote' ctx ≤ 0 := by
|
||||
simp [diseq_split_cert]
|
||||
intro _ _; subst p₂ p₃; simp
|
||||
generalize p₁.denote ctx = p
|
||||
intro h; cases Int.lt_or_gt_of_ne h
|
||||
next h => have := Int.add_one_le_of_lt h; rw [Int.add_comm]; simp [*]
|
||||
next h => have := Int.add_one_le_of_lt (Int.neg_lt_neg h); simp at this; simp [*]
|
||||
|
||||
theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → ¬p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
intro h₁ h₂ h₃
|
||||
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
|
||||
|
||||
def OrOver (n : Nat) (p : Nat → Prop) : Prop :=
|
||||
match n with
|
||||
| 0 => False
|
||||
| n+1 => p n ∨ OrOver n p
|
||||
|
||||
theorem orOver_unsat {p} : ¬ OrOver 0 p := by simp [OrOver]
|
||||
|
||||
theorem orOver_resolve {n p} : OrOver (n+1) p → ¬ p n → OrOver n p := by
|
||||
intro h₁ h₂
|
||||
rw [OrOver] at h₁
|
||||
cases h₁
|
||||
· contradiction
|
||||
· assumption
|
||||
|
||||
private theorem orOver_of_p {i n p} (h₁ : i < n) (h₂ : p i) : OrOver n p := by
|
||||
induction n
|
||||
next => simp at h₁
|
||||
next n ih =>
|
||||
simp [OrOver]
|
||||
cases Nat.eq_or_lt_of_le <| Nat.le_of_lt_add_one h₁
|
||||
next h => subst i; exact Or.inl h₂
|
||||
next h => exact Or.inr (ih h)
|
||||
|
||||
private theorem orOver_of_exists {n p} : (∃ k, k < n ∧ p k) → OrOver n p := by
|
||||
intro ⟨k, h₁, h₂⟩
|
||||
apply orOver_of_p h₁ h₂
|
||||
|
||||
private theorem ofNat_toNat {a : Int} : a ≥ 0 → Int.ofNat a.toNat = a := by cases a <;> simp
|
||||
private theorem cast_toNat {a : Int} : a ≥ 0 → a.toNat = a := by cases a <;> simp
|
||||
private theorem ofNat_lt {a : Int} {n : Nat} : a ≥ 0 → a < Int.ofNat n → a.toNat < n := by cases a <;> simp
|
||||
@[local simp] private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
|
||||
@[local simp] private theorem gcd_neg_left (a b : Int) : Int.gcd (-a) b = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_neg_right (a b : Int) : Int.gcd a (-b) = Int.gcd a b := by simp [Int.gcd]
|
||||
@[local simp] private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
|
||||
@[local simp] private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
|
||||
|
||||
private theorem cooper_dvd_left_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm a (a * d / Int.gcd (a * d) c)) fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k ∧
|
||||
a * d ∣ c * p + (-a) * s + c * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_left a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
rw [Int.neg_mul] at h₂
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
rw [Int.neg_ediv_of_dvd Int.gcd_dvd_left] at h₂
|
||||
simp only [lcm_neg_right] at h₂
|
||||
have : c * k + c * p + -(a * s) = c * p + -(a * s) + c * k := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
rw [← ofNat_toNat h₁] at h₃ h₄ h₅
|
||||
rw [Int.add_comm] at h₄
|
||||
have := ofNat_lt h₁ h₂
|
||||
apply orOver_of_exists
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : b * Int.ofNat k.toNat + b * p + -(a * q) = b * p + -(a * q) + b * Int.ofNat k.toNat := by ac_rfl
|
||||
rw [this] at h₃
|
||||
exists k.toNat
|
||||
|
||||
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
|
||||
|
||||
def Poly.tail (p : Poly) : Poly :=
|
||||
match p with
|
||||
| .add _ _ p => p
|
||||
| _ => p
|
||||
|
||||
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
∧ a*d ∣ (p₂.addConst (c*k)).denote' ctx
|
||||
|
||||
private theorem denote'_mul_combine_mul_addConst_eq (ctx : Context) (p q : Poly) (a b c : Int)
|
||||
: ((p.mul b |>.combine (q.mul a)).addConst c).denote' ctx = b*p.denote ctx + a*q.denote ctx + c := by
|
||||
simp
|
||||
|
||||
private theorem denote'_addConst_eq (ctx : Context) (p : Poly) (a : Int)
|
||||
: (p.addConst a).denote' ctx = p.denote ctx + a := by
|
||||
simp
|
||||
|
||||
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_left_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_left_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd1_cert p₁ p' a k → a ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let p := p₁.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
d' == a*d && p' == p₂.addConst (c*k)
|
||||
|
||||
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' → d' ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_left_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver a.natAbs fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true] at h
|
||||
assumption
|
||||
|
||||
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
|
||||
n == a.natAbs
|
||||
|
||||
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_left_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_left_split ctx p₁ p₂) := by
|
||||
unfold cooper_left_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_left_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
assumption
|
||||
|
||||
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_ineq_cert p₁ p₂ k b p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_left_split_ineq_cert, cooper_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_dvd_cert p₁ p' a k → a ∣ p'.denote ctx := by
|
||||
simp [cooper_left_split_dvd_cert, cooper_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
private theorem cooper_dvd_right_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm b (b * d / Int.gcd (b * d) c)) fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k ∧
|
||||
b * d ∣ (-c) * q + b * s + (-c) * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
apply orOver_of_exists
|
||||
have hlt := ofNat_lt h₁ h₂
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : -(a * k) + b * p + -(a * q) = b * p + -(a * q) + -(a * k) := by ac_rfl
|
||||
rw [this] at h₃; clear this
|
||||
rw [Int.sub_neg, Int.add_comm] at h₄
|
||||
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
exists k.toNat
|
||||
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
|
||||
|
||||
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
|
||||
|
||||
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
∧ b*d ∣ (p₂.addConst ((-c)*k)).denote' ctx
|
||||
|
||||
theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_right_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_right_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_right_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd1_cert p₂ p' b k → b ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
d' == b*d && p' == p₂.addConst ((-c)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' → d' ∣ p'.denote ctx := by
|
||||
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_right_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver b.natAbs fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true, Int.neg_zero] at h
|
||||
assumption
|
||||
|
||||
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
|
||||
|
||||
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_right_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_right_split ctx p₁ p₂) := by
|
||||
unfold cooper_right_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_right_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
assumption
|
||||
|
||||
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_ineq_cert p₁ p₂ k a p' → p'.denote ctx ≤ 0 := by
|
||||
simp [cooper_right_split_ineq_cert, cooper_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_dvd_cert p₂ p' b k → b ∣ p'.denote ctx := by
|
||||
simp [cooper_right_split_dvd_cert, cooper_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
end Int.Linear
|
||||
|
||||
theorem Int.not_le_eq (a b : Int) : (¬a ≤ b) = (b + 1 ≤ a) := by
|
||||
|
||||
@@ -133,10 +133,10 @@ protected theorem lt_of_not_ge {a b : Int} (h : ¬a ≤ b) : b < a :=
|
||||
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a ≤ b :=
|
||||
(Int.lt_iff_le_not_le.mp h).right
|
||||
|
||||
protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
@[simp] protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
|
||||
|
||||
protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
@[simp] protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
by rw [← Int.not_le, Decidable.not_not]
|
||||
|
||||
protected theorem lt_trichotomy (a b : Int) : a < b ∨ a = b ∨ b < a :=
|
||||
|
||||
@@ -662,6 +662,10 @@ def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α :
|
||||
@[simp] theorem unattach_cons {p : α → Prop} {a : { x // p x }} {l : List { x // p x }} :
|
||||
(a :: l).unattach = a.val :: l.unattach := rfl
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {l : List { x // p x }} {a} :
|
||||
a ∈ l.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ l := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem length_unattach {p : α → Prop} {l : List { x // p x }} :
|
||||
l.unattach.length = l.length := by
|
||||
unfold unattach
|
||||
@@ -766,6 +770,16 @@ and simplifies these to the function directly taking the value.
|
||||
simp [hf, find?_cons]
|
||||
split <;> simp [ih]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.all f = l.unattach.all g := by
|
||||
simp [all_eq, hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.any f = l.unattach.any g := by
|
||||
simp [any_eq, hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : List { x // p x }}
|
||||
|
||||
@@ -212,6 +212,7 @@ def mapMono (as : List α) (f : α → α) : List α :=
|
||||
|
||||
/-! ## Additional lemmas required for bootstrapping `Array`. -/
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
|
||||
(as ++ bs)[i] = as[i] := by
|
||||
induction as generalizing i with
|
||||
@@ -221,6 +222,7 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as
|
||||
| zero => rfl
|
||||
| succ i => apply ih
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i) {h₂} :
|
||||
(as ++ bs)[i]'h₂ =
|
||||
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
|
||||
|
||||
@@ -514,47 +514,6 @@ private theorem findIdx?_go_eq {p : α → Bool} {xs : List α} {i : Nat} :
|
||||
(x :: xs).findIdx? p = if p x then some 0 else (xs.findIdx? p).map fun i => i + 1 := by
|
||||
simp [findIdx?, findIdx?_go_eq]
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_nil {p : α → Bool} : findFinIdx? p [] = none := rfl
|
||||
|
||||
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α → Bool} {i : Nat} {h} :
|
||||
List.findIdx?.go p xs i =
|
||||
(List.findFinIdx?.go p l xs i h).map (·.val) := by
|
||||
unfold findIdx?.go
|
||||
unfold findFinIdx?.go
|
||||
split
|
||||
· simp_all
|
||||
· simp only
|
||||
split
|
||||
· simp
|
||||
· rw [findIdx?_go_eq_map_findFinIdx?_go_val]
|
||||
|
||||
theorem findIdx?_eq_map_findFinIdx?_val {xs : List α} {p : α → Bool} :
|
||||
xs.findIdx? p = (xs.findFinIdx? p).map (·.val) := by
|
||||
simp [findIdx?, findFinIdx?]
|
||||
rw [findIdx?_go_eq_map_findFinIdx?_go_val]
|
||||
|
||||
@[simp] theorem findFinIdx?_cons {p : α → Bool} {x : α} {xs : List α} :
|
||||
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
|
||||
rw [← Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
|
||||
rw [← findIdx?_eq_map_findFinIdx?_val]
|
||||
rw [findIdx?_cons]
|
||||
split
|
||||
· simp
|
||||
· rw [findIdx?_eq_map_findFinIdx?_val]
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp [hf, findFinIdx?_cons]
|
||||
split <;> simp [ih, Function.comp_def]
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
theorem findIdx_cons (p : α → Bool) (b : α) (l : List α) :
|
||||
@@ -976,6 +935,71 @@ theorem findIdx_eq_getD_findIdx? {xs : List α} {p : α → Bool} :
|
||||
simp [hf, findIdx?_cons]
|
||||
split <;> simp [ih, Function.comp_def]
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_nil {p : α → Bool} : findFinIdx? p [] = none := rfl
|
||||
|
||||
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α → Bool} {i : Nat} {h} :
|
||||
List.findIdx?.go p xs i =
|
||||
(List.findFinIdx?.go p l xs i h).map (·.val) := by
|
||||
unfold findIdx?.go
|
||||
unfold findFinIdx?.go
|
||||
split
|
||||
· simp_all
|
||||
· simp only
|
||||
split
|
||||
· simp
|
||||
· rw [findIdx?_go_eq_map_findFinIdx?_go_val]
|
||||
|
||||
theorem findIdx?_eq_map_findFinIdx?_val {xs : List α} {p : α → Bool} :
|
||||
xs.findIdx? p = (xs.findFinIdx? p).map (·.val) := by
|
||||
simp [findIdx?, findFinIdx?]
|
||||
rw [findIdx?_go_eq_map_findFinIdx?_go_val]
|
||||
|
||||
theorem findFinIdx?_eq_pmap_findIdx? {xs : List α} {p : α → Bool} :
|
||||
xs.findFinIdx? p =
|
||||
(xs.findIdx? p).pmap
|
||||
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact ⟨i, m.choose⟩)
|
||||
(fun i h => h) := by
|
||||
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
|
||||
|
||||
@[simp] theorem findFinIdx?_cons {p : α → Bool} {x : α} {xs : List α} :
|
||||
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
|
||||
rw [← Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
|
||||
rw [← findIdx?_eq_map_findFinIdx?_val]
|
||||
rw [findIdx?_cons]
|
||||
split
|
||||
· simp
|
||||
· rw [findIdx?_eq_map_findFinIdx?_val]
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp] theorem findFinIdx?_eq_none_iff {l : List α} {p : α → Bool} :
|
||||
l.findFinIdx? p = none ↔ ∀ x ∈ l, ¬ p x := by
|
||||
simp [findFinIdx?_eq_pmap_findIdx?]
|
||||
|
||||
@[simp]
|
||||
theorem findFinIdx?_eq_some_iff {xs : List α} {p : α → Bool} {i : Fin xs.length} :
|
||||
xs.findFinIdx? p = some i ↔
|
||||
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
|
||||
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
|
||||
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
|
||||
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
|
||||
· rintro ⟨h, w⟩
|
||||
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp [hf, findFinIdx?_cons]
|
||||
split <;> simp [ih, Function.comp_def]
|
||||
|
||||
|
||||
/-! ### idxOf
|
||||
|
||||
The verification API for `idxOf` is still incomplete.
|
||||
@@ -1035,6 +1059,36 @@ theorem idxOf_lt_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∈ l) : l.
|
||||
@[deprecated idxOf_lt_length (since := "2025-01-29")]
|
||||
abbrev indexOf_lt_length := @idxOf_lt_length
|
||||
|
||||
/-! ### finIdxOf?
|
||||
|
||||
The verification API for `finIdxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
@[simp] theorem finIdxOf?_nil [BEq α] : ([] : List α).finIdxOf? a = none := rfl
|
||||
|
||||
@[simp] theorem finIdxOf?_cons [BEq α] (a : α) (xs : List α) :
|
||||
(a :: xs).finIdxOf? b =
|
||||
if a == b then some ⟨0, by simp⟩ else (xs.finIdxOf? b).map (·.succ) := by
|
||||
simp [finIdxOf?]
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
l.finIdxOf? a = none ↔ a ∉ l := by
|
||||
simp only [finIdxOf?, findFinIdx?_eq_none_iff, beq_iff_eq]
|
||||
constructor
|
||||
· intro w m
|
||||
exact w a m rfl
|
||||
· rintro h a m rfl
|
||||
exact h m
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Fin l.length} :
|
||||
l.finIdxOf? a = some i ↔ l[i] = a ∧ ∀ j (_ : j < i), ¬l[j] = a := by
|
||||
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
|
||||
|
||||
/-! ### idxOf?
|
||||
|
||||
The verification API for `idxOf?` is still incomplete.
|
||||
@@ -1060,12 +1114,6 @@ theorem idxOf?_cons [BEq α] (a : α) (xs : List α) (b : α) :
|
||||
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
|
||||
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
|
||||
|
||||
/-! ### finIdxOf? -/
|
||||
|
||||
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
/-! ### lookup -/
|
||||
|
||||
section lookup
|
||||
|
||||
@@ -3086,8 +3086,12 @@ variable [BEq α]
|
||||
@[simp] theorem replace_cons_self [LawfulBEq α] {a : α} : (a::as).replace a b = b::as := by
|
||||
simp [replace_cons]
|
||||
|
||||
@[simp] theorem replace_of_not_mem {l : List α} (h : !l.elem a) : l.replace a b = l := by
|
||||
induction l <;> simp_all [replace_cons]
|
||||
@[simp] theorem replace_of_not_mem [LawfulBEq α] {l : List α} (h : a ∉ l) : l.replace a b = l := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [replace_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem length_replace {l : List α} : (l.replace a b).length = l.length := by
|
||||
induction l with
|
||||
@@ -3170,7 +3174,7 @@ theorem replace_take {l : List α} {i : Nat} :
|
||||
(replicate n a).replace a b = b :: replicate (n - 1) a := by
|
||||
cases n <;> simp_all [replicate_succ, replace_cons]
|
||||
|
||||
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
|
||||
@[simp] theorem replace_replicate_ne [LawfulBEq α] {a b c : α} (h : !b == a) :
|
||||
(replicate n a).replace b c = replicate n a := by
|
||||
rw [replace_of_not_mem]
|
||||
simp_all
|
||||
|
||||
@@ -658,6 +658,40 @@ private theorem insertIdx_loop_toArray (i : Nat) (l : List α) (j : Nat) (hj : j
|
||||
· simp only [size_toArray, Nat.not_le] at h'
|
||||
rw [List.insertIdx_of_length_lt (h := h')]
|
||||
|
||||
@[simp]
|
||||
theorem replace_toArray [BEq α] [LawfulBEq α] (l : List α) (a b : α) :
|
||||
l.toArray.replace a b = (l.replace a b).toArray := by
|
||||
rw [Array.replace]
|
||||
split <;> rename_i i h
|
||||
· simp only [finIdxOf?_toArray, finIdxOf?_eq_none_iff] at h
|
||||
rw [replace_of_not_mem]
|
||||
simpa
|
||||
· simp_all only [finIdxOf?_toArray, finIdxOf?_eq_some_iff, Fin.getElem_fin, set_toArray,
|
||||
mk.injEq]
|
||||
apply List.ext_getElem
|
||||
· simp
|
||||
· intro j h₁ h₂
|
||||
rw [List.getElem_replace, List.getElem_set]
|
||||
by_cases h₃ : j < i
|
||||
· rw [if_neg (by omega), if_neg]
|
||||
simp only [length_set] at h₁ h₃
|
||||
simpa using h.2 ⟨j, by omega⟩ h₃
|
||||
· by_cases h₃ : j = i
|
||||
· rw [if_pos (by omega), if_pos, if_neg]
|
||||
· simp only [mem_take_iff_getElem, not_exists]
|
||||
intro k hk
|
||||
simpa using h.2 ⟨k, by omega⟩ (by show k < i.1; omega)
|
||||
· subst h₃
|
||||
simpa using h.1
|
||||
· rw [if_neg (by omega)]
|
||||
split
|
||||
· rw [if_pos]
|
||||
· simp_all
|
||||
· simp only [mem_take_iff_getElem]
|
||||
simp only [length_set] at h₁
|
||||
exact ⟨i, by omega, h.1⟩
|
||||
· rfl
|
||||
|
||||
@[simp] theorem leftpad_toArray (n : Nat) (a : α) (l : List α) :
|
||||
Array.leftpad n a l.toArray = (leftpad n a l).toArray := by
|
||||
simp [leftpad, Array.leftpad, ← toArray_replicate]
|
||||
|
||||
@@ -80,9 +80,9 @@ instance : OfScientific Float32 where
|
||||
def Float32.ofNat (n : Nat) : Float32 :=
|
||||
OfScientific.ofScientific n false 0
|
||||
|
||||
def Float32.ofInt : Int → Float
|
||||
| Int.ofNat n => Float.ofNat n
|
||||
| Int.negSucc n => Float.neg (Float.ofNat (Nat.succ n))
|
||||
def Float32.ofInt : Int → Float32
|
||||
| Int.ofNat n => Float32.ofNat n
|
||||
| Int.negSucc n => Float32.neg (Float32.ofNat (Nat.succ n))
|
||||
|
||||
instance : OfNat Float32 n := ⟨Float32.ofNat n⟩
|
||||
|
||||
|
||||
@@ -101,6 +101,12 @@ This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
|
||||
| some x, some y => r x y
|
||||
| _, _ => False
|
||||
|
||||
@[inline] protected def le (r : α → β → Prop) : Option α → Option β → Prop
|
||||
| none, some _ => True
|
||||
| none, none => True
|
||||
| some _, none => False
|
||||
| some x, some y => r x y
|
||||
|
||||
instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
|
||||
| none, some _ => isTrue trivial
|
||||
| some x, some y => s x y
|
||||
@@ -217,18 +223,24 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
@[simp] theorem any_none : Option.any p none = false := rfl
|
||||
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
|
||||
|
||||
/-- The minimum of two optional values. -/
|
||||
/--
|
||||
The minimum of two optional values.
|
||||
|
||||
Note this treats `none` as the least element,
|
||||
so `min none x = min x none = none` for all `x : Option α`.
|
||||
Prior to nightly-2025-02-27, we instead had `min none (some x) = min (some x) none = some x`.
|
||||
-/
|
||||
protected def min [Min α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Min.min x y)
|
||||
| some x, none => some x
|
||||
| none, some y => some y
|
||||
| some _, none => none
|
||||
| none, some _ => none
|
||||
| none, none => none
|
||||
|
||||
instance [Min α] : Min (Option α) where min := Option.min
|
||||
|
||||
@[simp] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = some a := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = some b := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = none := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = none := rfl
|
||||
@[simp] theorem min_none_none [Min α] : min (none : Option α) none = none := rfl
|
||||
|
||||
/-- The maximum of two optional values. -/
|
||||
@@ -251,6 +263,9 @@ end Option
|
||||
instance [LT α] : LT (Option α) where
|
||||
lt := Option.lt (· < ·)
|
||||
|
||||
instance [LE α] : LE (Option α) where
|
||||
le := Option.le (· ≤ ·)
|
||||
|
||||
@[always_inline]
|
||||
instance : Functor Option where
|
||||
map := Option.map
|
||||
|
||||
@@ -654,6 +654,11 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
|
||||
Option.map g (pmap f o H) = pmap (fun a h => g (f a h)) o H := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem pmap_map (o : Option α) (f : α → β) {p : β → Prop} (g : ∀ b, p b → γ) (H) :
|
||||
pmap g (o.map f) H =
|
||||
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (mem_map_of_mem f m)) := by
|
||||
cases o <;> simp
|
||||
|
||||
/-! ### pelim -/
|
||||
|
||||
@[simp] theorem pelim_none : pelim none b f = b := rfl
|
||||
@@ -668,4 +673,80 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
|
||||
o.pelim g (fun a h => g' (f a (H a h))) := by
|
||||
cases o <;> simp
|
||||
|
||||
/-! ### LT and LE -/
|
||||
|
||||
@[simp] theorem not_lt_none [LT α] {a : Option α} : ¬ a < none := by cases a <;> simp [LT.lt, Option.lt]
|
||||
@[simp] theorem none_lt_some [LT α] {a : α} : none < some a := by simp [LT.lt, Option.lt]
|
||||
@[simp] theorem some_lt_some [LT α] {a b : α} : some a < some b ↔ a < b := by simp [LT.lt, Option.lt]
|
||||
|
||||
@[simp] theorem none_le [LE α] {a : Option α} : none ≤ a := by cases a <;> simp [LE.le, Option.le]
|
||||
@[simp] theorem not_some_le_none [LE α] {a : α} : ¬ some a ≤ none := by simp [LE.le, Option.le]
|
||||
@[simp] theorem some_le_some [LE α] {a b : α} : some a ≤ some b ↔ a ≤ b := by simp [LE.le, Option.le]
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
theorem min_eq_left [LE α] [Min α] (min_eq_left : ∀ x y : α, x ≤ y → min x y = x)
|
||||
{a b : Option α} (h : a ≤ b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right [LE α] [Min α] (min_eq_right : ∀ x y : α, y ≤ x → min x y = y)
|
||||
{a b : Option α} (h : b ≤ a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_left_of_lt [LT α] [Min α] (min_eq_left : ∀ x y : α, x < y → min x y = x)
|
||||
{a b : Option α} (h : a < b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right_of_lt [LT α] [Min α] (min_eq_right : ∀ x y : α, y < x → min x y = y)
|
||||
{a b : Option α} (h : b < a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_or [LE α] [Min α] (min_eq_or : ∀ x y : α, min x y = x ∨ min x y = y)
|
||||
{a b : Option α} : min a b = a ∨ min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_left [LE α] [Min α] (min_le_left : ∀ x y : α, min x y ≤ x)
|
||||
{a b : Option α} : min a b ≤ a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_right [LE α] [Min α] (min_le_right : ∀ x y : α, min x y ≤ y)
|
||||
{a b : Option α} : min a b ≤ b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem le_min [LE α] [Min α] (le_min : ∀ x y z : α, x ≤ min y z ↔ x ≤ y ∧ x ≤ z)
|
||||
{a b c : Option α} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
theorem max_eq_left [LE α] [Max α] (max_eq_left : ∀ x y : α, x ≤ y → max x y = y)
|
||||
{a b : Option α} (h : a ≤ b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right [LE α] [Max α] (max_eq_right : ∀ x y : α, y ≤ x → max x y = x)
|
||||
{a b : Option α} (h : b ≤ a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_left_of_lt [LT α] [Max α] (max_eq_left : ∀ x y : α, x < y → max x y = y)
|
||||
{a b : Option α} (h : a < b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right_of_lt [LT α] [Max α] (max_eq_right : ∀ x y : α, y < x → max x y = x)
|
||||
{a b : Option α} (h : b < a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_or [LE α] [Max α] (max_eq_or : ∀ x y : α, max x y = x ∨ max x y = y)
|
||||
{a b : Option α} : max a b = a ∨ max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem left_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (left_le_max : ∀ x y : α, x ≤ max x y)
|
||||
{a b : Option α} : a ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem right_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (right_le_max : ∀ x y : α, y ≤ max x y)
|
||||
{a b : Option α} : b ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_le [LE α] [Max α] (max_le : ∀ x y z : α, max x y ≤ z ↔ x ≤ z ∧ y ≤ z)
|
||||
{a b c : Option α} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
end Option
|
||||
|
||||
@@ -8,6 +8,7 @@ import Init.Data.SInt.Basic
|
||||
import Init.Data.SInt.Float
|
||||
import Init.Data.SInt.Float32
|
||||
import Init.Data.SInt.Lemmas
|
||||
import Init.Data.SInt.Bitwise
|
||||
|
||||
/-!
|
||||
This module contains the definitions and basic theory about signed fixed width integer types.
|
||||
|
||||
@@ -77,6 +77,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
|
||||
-/
|
||||
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
|
||||
|
||||
theorem Int8.toBitVec.inj : {x y : Int8} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
|
||||
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
|
||||
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
|
||||
@@ -110,8 +113,8 @@ instance : ReprAtom Int8 := ⟨⟩
|
||||
instance : Hashable Int8 where
|
||||
hash i := i.toUInt8.toUInt64
|
||||
|
||||
instance : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance : Neg Int8 where
|
||||
instance Int8.instOfNat : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance Int8.instNeg : Neg Int8 where
|
||||
neg := Int8.neg
|
||||
|
||||
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
|
||||
@@ -213,6 +216,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
|
||||
-/
|
||||
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
|
||||
|
||||
theorem Int16.toBitVec.inj : {x y : Int16} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
|
||||
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
|
||||
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
|
||||
@@ -250,8 +256,8 @@ instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : Hashable Int16 where
|
||||
hash i := i.toUInt16.toUInt64
|
||||
|
||||
instance : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance : Neg Int16 where
|
||||
instance Int16.instOfNat : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance Int16.instNeg : Neg Int16 where
|
||||
neg := Int16.neg
|
||||
|
||||
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
|
||||
@@ -353,6 +359,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
|
||||
-/
|
||||
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
|
||||
|
||||
theorem Int32.toBitVec.inj : {x y : Int32} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
|
||||
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
|
||||
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
|
||||
@@ -394,8 +403,8 @@ instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : Hashable Int32 where
|
||||
hash i := i.toUInt32.toUInt64
|
||||
|
||||
instance : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance : Neg Int32 where
|
||||
instance Int32.instOfNat : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance Int32.instNeg : Neg Int32 where
|
||||
neg := Int32.neg
|
||||
|
||||
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
|
||||
@@ -497,6 +506,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
|
||||
-/
|
||||
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
|
||||
|
||||
theorem Int64.toBitVec.inj : {x y : Int64} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
|
||||
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
|
||||
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
|
||||
@@ -542,8 +554,8 @@ instance : ReprAtom Int64 := ⟨⟩
|
||||
instance : Hashable Int64 where
|
||||
hash i := i.toUInt64
|
||||
|
||||
instance : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance : Neg Int64 where
|
||||
instance Int64.instOfNat : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance Int64.instNeg : Neg Int64 where
|
||||
neg := Int64.neg
|
||||
|
||||
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
|
||||
@@ -645,6 +657,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
|
||||
-/
|
||||
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
|
||||
|
||||
theorem ISize.toBitVec.inj : {x y : ISize} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
|
||||
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
|
||||
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
|
||||
@@ -700,8 +715,8 @@ instance : ReprAtom ISize := ⟨⟩
|
||||
instance : Hashable ISize where
|
||||
hash i := i.toUSize.toUInt64
|
||||
|
||||
instance : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance : Neg ISize where
|
||||
instance ISize.instOfNat : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance ISize.instNeg : Neg ISize where
|
||||
neg := ISize.neg
|
||||
|
||||
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
|
||||
|
||||
57
src/Init/Data/SInt/Bitwise.lean
Normal file
57
src/Init/Data/SInt/Bitwise.lean
Normal file
@@ -0,0 +1,57 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
macro "declare_bitwise_int_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec.sdiv b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec.srem b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_not {a : $typeName} : (~~~a).toBitVec = ~~~a.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec.sshiftRight' (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_abs (a : $typeName) : a.abs.toBitVec = a.toBitVec.abs := rfl
|
||||
|
||||
end $typeName
|
||||
)
|
||||
declare_bitwise_int_theorems Int8 8
|
||||
declare_bitwise_int_theorems Int16 16
|
||||
declare_bitwise_int_theorems Int32 32
|
||||
declare_bitwise_int_theorems Int64 64
|
||||
declare_bitwise_int_theorems ISize System.Platform.numBits
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt8 {b : Bool} : b.toInt8.toBitVec = (BitVec.ofBool b).setWidth 8 := by
|
||||
cases b <;> simp [toInt8]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt16 {b : Bool} : b.toInt16.toBitVec = (BitVec.ofBool b).setWidth 16 := by
|
||||
cases b <;> simp [toInt16]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt32 {b : Bool} : b.toInt32.toBitVec = (BitVec.ofBool b).setWidth 32 := by
|
||||
cases b <;> simp [toInt32]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt64 {b : Bool} : b.toInt64.toBitVec = (BitVec.ofBool b).setWidth 64 := by
|
||||
cases b <;> simp [toInt64]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toISize {b : Bool} :
|
||||
b.toISize.toBitVec = (BitVec.ofBool b).setWidth System.Platform.numBits := by
|
||||
cases b
|
||||
· simp [toISize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toISize]
|
||||
@@ -5,9 +5,94 @@ Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
open Lean in
|
||||
set_option hygiene false in
|
||||
macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
|
||||
let mut cmds ← Syntax.getArgs <$> `(
|
||||
namespace $typeName
|
||||
|
||||
@[int_toBitVec] theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec.sle b.toBitVec := Iff.rfl
|
||||
@[int_toBitVec] theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec.slt b.toBitVec := Iff.rfl
|
||||
theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec ↔ a = b :=
|
||||
⟨toBitVec.inj, (· ▸ rfl)⟩
|
||||
@[int_toBitVec] theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b ↔ a.toBitVec = b.toBitVec :=
|
||||
toBitVec_inj.symm
|
||||
@[int_toBitVec] theorem ne_iff_toBitVec_ne {a b : $typeName} : a ≠ b ↔ a.toBitVec ≠ b.toBitVec :=
|
||||
Decidable.not_iff_not.2 eq_iff_toBitVec_eq
|
||||
@[simp] theorem toBitVec_ofNat {n : Nat} : toBitVec (ofNat n) = BitVec.ofNat _ n := rfl
|
||||
@[simp, int_toBitVec] theorem toBitVec_ofNatOfNat {n : Nat} : toBitVec (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
|
||||
|
||||
end $typeName
|
||||
)
|
||||
return ⟨mkNullNode cmds⟩
|
||||
|
||||
declare_int_theorems Int8 8
|
||||
declare_int_theorems Int16 16
|
||||
declare_int_theorems Int32 32
|
||||
declare_int_theorems Int64 64
|
||||
declare_int_theorems ISize System.Platform.numBits
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem UInt64.toBitVec_toInt64 (x : UInt64) : x.toInt64.toBitVec = x.toBitVec := rfl
|
||||
@[simp] theorem USize.toBitVec_toISize (x : USize) : x.toISize.toBitVec = x.toBitVec := rfl
|
||||
|
||||
@[simp] theorem Int8.ofBitVec_uInt8ToBitVec (x : UInt8) : Int8.ofBitVec x.toBitVec = x.toInt8 := rfl
|
||||
@[simp] theorem Int16.ofBitVec_uInt16ToBitVec (x : UInt16) : Int16.ofBitVec x.toBitVec = x.toInt16 := rfl
|
||||
@[simp] theorem Int32.ofBitVec_uInt32ToBitVec (x : UInt32) : Int32.ofBitVec x.toBitVec = x.toInt32 := rfl
|
||||
@[simp] theorem Int64.ofBitVec_uInt64ToBitVec (x : UInt64) : Int64.ofBitVec x.toBitVec = x.toInt64 := rfl
|
||||
@[simp] theorem ISize.ofBitVec_uSize8ToBitVec (x : USize) : ISize.ofBitVec x.toBitVec = x.toISize := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUInt8_toInt8 (x : UInt8) : x.toInt8.toUInt8 = x := rfl
|
||||
@[simp] theorem UInt16.toUInt16_toInt16 (x : UInt16) : x.toInt16.toUInt16 = x := rfl
|
||||
@[simp] theorem UInt32.toUInt32_toInt32 (x : UInt32) : x.toInt32.toUInt32 = x := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toInt64 (x : UInt64) : x.toInt64.toUInt64 = x := rfl
|
||||
@[simp] theorem USize.toUSize_toISize (x : USize) : x.toISize.toUSize = x := rfl
|
||||
|
||||
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := rfl
|
||||
@[simp] theorem ISize.toBitVec_zero : (0 : ISize).toBitVec = 0 := rfl
|
||||
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
|
||||
|
||||
@[simp] theorem Int8.neg_zero : -(0 : Int8) = 0 := rfl
|
||||
@[simp] theorem Int16.neg_zero : -(0 : Int16) = 0 := rfl
|
||||
@[simp] theorem Int32.neg_zero : -(0 : Int32) = 0 := rfl
|
||||
@[simp] theorem Int64.neg_zero : -(0 : Int64) = 0 := rfl
|
||||
@[simp] theorem ISize.neg_zero : -(0 : ISize) = 0 := ISize.toBitVec.inj (by simp)
|
||||
|
||||
theorem ISize.toNat_toBitVec_ofNat_of_lt {n : Nat} (h : n < 2^32) :
|
||||
(ofNat n).toBitVec.toNat = n :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le h (by cases USize.size_eq <;> simp_all +decide))
|
||||
|
||||
theorem ISize.toInt_ofInt {n : Int} (hn : -2^31 ≤ n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
|
||||
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self] <;> cases System.Platform.numBits_eq
|
||||
<;> (simp_all; try omega)
|
||||
|
||||
theorem ISize.toNatClampNeg_ofInt_eq_zero {n : Int} (hn : -2^31 ≤ n) (hn' : n ≤ 0) :
|
||||
toNatClampNeg (ofInt n) = 0 := by
|
||||
rwa [toNatClampNeg, toInt_ofInt hn (by omega), Int.toNat_eq_zero]
|
||||
|
||||
theorem ISize.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
|
||||
toBitVec.inj (by simp [BitVec.ofInt_neg])
|
||||
|
||||
theorem ISize.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n :=
|
||||
toBitVec.inj (by simp)
|
||||
|
||||
theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
|
||||
rw [← neg_ofInt, ofInt_eq_ofNat]
|
||||
|
||||
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) :
|
||||
toNatClampNeg (ofNat n) = n := by
|
||||
rw [toNatClampNeg, ← ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
|
||||
|
||||
theorem ISize.toNatClampNeg_neg_ofNat_of_le {n : Nat} (h : n ≤ 2 ^ 31) :
|
||||
toNatClampNeg (-ofNat n) = 0 := by
|
||||
rw [neg_ofNat, toNatClampNeg_ofInt_eq_zero (by omega) (by omega)]
|
||||
|
||||
theorem ISize.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
|
||||
rw [← ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
|
||||
|
||||
theorem ISize.toInt_neg_ofNat_of_le {n : Nat} (h : n ≤ 2 ^ 31) : toInt (-ofNat n) = -n := by
|
||||
rw [← ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]
|
||||
|
||||
@@ -287,6 +287,8 @@ theorem UInt32.size_le_usizeSize : UInt32.size ≤ USize.size := by
|
||||
theorem USize.size_eq_two_pow : USize.size = 2 ^ System.Platform.numBits := rfl
|
||||
theorem USize.toNat_lt_two_pow_numBits (n : USize) : n.toNat < 2 ^ System.Platform.numBits := n.toFin.isLt
|
||||
@[simp] theorem USize.toNat_lt (n : USize) : n.toNat < 2 ^ 64 := Nat.lt_of_lt_of_le n.toFin.isLt size_le
|
||||
theorem USize.size_le_uint64Size : USize.size ≤ UInt64.size := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
theorem UInt8.toNat_lt_usizeSize (n : UInt8) : n.toNat < USize.size :=
|
||||
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
|
||||
@@ -295,6 +297,51 @@ theorem UInt16.toNat_lt_usizeSize (n : UInt16) : n.toNat < USize.size :=
|
||||
theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
|
||||
|
||||
theorem UInt8.size_dvd_usizeSize : UInt8.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem UInt16.size_dvd_usizeSize : UInt16.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem UInt32.size_dvd_usizeSize : UInt32.size ∣ USize.size := by cases USize.size_eq <;> simp_all +decide
|
||||
theorem USize.size_dvd_uInt64Size : USize.size ∣ UInt64.size := by cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
@[simp] theorem mod_usizeSize_uInt8Size (n : Nat) : n % USize.size % UInt8.size = n % UInt8.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt8.size_dvd_usizeSize
|
||||
@[simp] theorem mod_usizeSize_uInt16Size (n : Nat) : n % USize.size % UInt16.size = n % UInt16.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt16.size_dvd_usizeSize
|
||||
@[simp] theorem mod_usizeSize_uInt32Size (n : Nat) : n % USize.size % UInt32.size = n % UInt32.size :=
|
||||
Nat.mod_mod_of_dvd _ UInt32.size_dvd_usizeSize
|
||||
@[simp] theorem mod_uInt64Size_uSizeSize (n : Nat) : n % UInt64.size % USize.size = n % USize.size :=
|
||||
Nat.mod_mod_of_dvd _ USize.size_dvd_uInt64Size
|
||||
|
||||
@[simp] theorem UInt8.toNat_mod_size (n : UInt8) : n.toNat % UInt8.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt8.toNat_mod_uInt16Size (n : UInt8) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uInt32Size (n : UInt8) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uInt64Size (n : UInt8) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uSizeSize (n : UInt8) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt16.toNat_mod_size (n : UInt16) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt16.toNat_mod_uInt32Size (n : UInt16) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt16.toNat_mod_uInt64Size (n : UInt16) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt16.toNat_mod_uSizeSize (n : UInt16) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt32.toNat_mod_size (n : UInt32) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt32.toNat_mod_uInt64Size (n : UInt32) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt32.toNat_mod_uSizeSize (n : UInt32) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
|
||||
|
||||
@[simp] theorem UInt64.toNat_mod_size (n : UInt64) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
|
||||
@[simp] theorem USize.toNat_mod_size (n : USize) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_size
|
||||
@[simp] theorem USize.toNat_mod_uInt64Size (n : USize) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_mod_256 (n : UInt8) : n.toUInt16 % 256 = n.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_mod_256 (n : UInt8) : n.toUInt32 % 256 = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt64_mod_256 (n : UInt8) : n.toUInt64 % 256 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUSize_mod_256 (n : UInt8) : n.toUSize % 256 = n.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_mod_65536 (n : UInt16) : n.toUInt32 % 65536 = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt64_mod_65536 (n : UInt16) : n.toUInt64 % 65536 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_mod_65536 (n : UInt16) : n.toUSize % 65536 = n.toUSize := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_mod_4294967296 (n : UInt32) : n.toUInt64 % 4294967296 = n.toUInt64 := UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem Fin.mk_uInt8ToNat (n : UInt8) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@[simp] theorem Fin.mk_uInt16ToNat (n : UInt16) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@[simp] theorem Fin.mk_uInt32ToNat (n : UInt32) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
|
||||
@@ -328,7 +375,7 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
@[simp] theorem UInt32.toFin_toUSize (n : UInt32) :
|
||||
n.toUSize.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_uint64Size := rfl
|
||||
|
||||
@[simp] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
@@ -349,14 +396,14 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
|
||||
@[simp] theorem UInt16.toBitVec_toUInt64 (n : UInt16) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toUInt64 (n : UInt32) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
|
||||
@[simp] theorem USize.toBitVec_toUInt64 (n : USize) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt (USize.toNat_lt _)])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_toUSize (n : UInt8) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt16.toBitVec_toUSize (n : UInt16) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt32.toBitVec_toUSize (n : UInt32) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem UInt64.toBitVec_toUSize (n : UInt64) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
@@ -420,3 +467,321 @@ theorem USize.ofNatLT_uInt64ToNat (n : UInt64) (h) : USize.ofNatLT n.toNat h = n
|
||||
@[simp] theorem USize.ofFin_uint8ToFin (n : UInt8) : USize.ofFin (n.toFin.castLE UInt8.size_le_usizeSize) = n.toUSize := rfl
|
||||
@[simp] theorem USize.ofFin_uint16ToFin (n : UInt16) : USize.ofFin (n.toFin.castLE UInt16.size_le_usizeSize) = n.toUSize := rfl
|
||||
@[simp] theorem USize.ofFin_uint32ToFin (n : UInt32) : USize.ofFin (n.toFin.castLE UInt32.size_le_usizeSize) = n.toUSize := rfl
|
||||
|
||||
@[simp] theorem Nat.toUInt8_eq {n : Nat} : n.toUInt8 = UInt8.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt16_eq {n : Nat} : n.toUInt16 = UInt16.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt32_eq {n : Nat} : n.toUInt32 = UInt32.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUInt64_eq {n : Nat} : n.toUInt64 = UInt64.ofNat n := rfl
|
||||
@[simp] theorem Nat.toUSize_eq {n : Nat} : n.toUSize = USize.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_uSizeToBitVec (n : USize) :
|
||||
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.ofBitVec_uInt8ToBitVec (n : UInt8) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt16ToBitVec (n : UInt16) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt32ToBitVec (n : UInt32) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofBitVec_uInt64ToBitVec (n : UInt64) :
|
||||
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.ofNat_uInt16ToNat (n : UInt16) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uInt32ToNat (n : UInt32) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uInt64ToNat (n : UInt64) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt8.ofNat_uSizeToNat (n : USize) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt16.ofNat_uInt8ToNat (n : UInt8) : UInt16.ofNat n.toNat = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.ofNat_uInt32ToNat (n : UInt32) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofNat_uInt64ToNat (n : UInt64) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt16.ofNat_uSizeToNat (n : USize) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem UInt32.ofNat_uInt8ToNat (n : UInt8) : UInt32.ofNat n.toNat = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.ofNat_uInt16ToNat (n : UInt16) : UInt32.ofNat n.toNat = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.ofNat_uInt64ToNat (n : UInt64) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt32.ofNat_uSizeToNat (n : USize) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
|
||||
|
||||
@[simp] theorem UInt64.ofNat_uInt8ToNat (n : UInt8) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uInt16ToNat (n : UInt16) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uInt32ToNat (n : UInt32) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.ofNat_uSizeToNat (n : USize) : UInt64.ofNat n.toNat = n.toUInt64 :=
|
||||
UInt64.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.ofNat_uInt8ToNat (n : UInt8) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt16ToNat (n : UInt16) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt32ToNat (n : UInt32) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.ofNat_uInt64ToNat (n : UInt64) : USize.ofNat n.toNat = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
theorem UInt8.ofNatLT_eq_ofNat (n : Nat) {h} : UInt8.ofNatLT n h = UInt8.ofNat n :=
|
||||
UInt8.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt16.ofNatLT_eq_ofNat (n : Nat) {h} : UInt16.ofNatLT n h = UInt16.ofNat n :=
|
||||
UInt16.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt32.ofNatLT_eq_ofNat (n : Nat) {h} : UInt32.ofNatLT n h = UInt32.ofNat n :=
|
||||
UInt32.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem UInt64.ofNatLT_eq_ofNat (n : Nat) {h} : UInt64.ofNatLT n h = UInt64.ofNat n :=
|
||||
UInt64.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
theorem USize.ofNatLT_eq_ofNat (n : Nat) {h} : USize.ofNatLT n h = USize.ofNat n :=
|
||||
USize.toNat.inj (by simp [Nat.mod_eq_of_lt h])
|
||||
|
||||
theorem UInt8.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt8.size) :
|
||||
UInt8.ofNatTruncate n = UInt8.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt8.ofNatLT_eq_ofNat]
|
||||
theorem UInt16.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt16.size) :
|
||||
UInt16.ofNatTruncate n = UInt16.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt16.ofNatLT_eq_ofNat]
|
||||
theorem UInt32.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt32.size) :
|
||||
UInt32.ofNatTruncate n = UInt32.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt32.ofNatLT_eq_ofNat]
|
||||
theorem UInt64.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt64.size) :
|
||||
UInt64.ofNatTruncate n = UInt64.ofNat n := by
|
||||
simp [ofNatTruncate, hn, UInt64.ofNatLT_eq_ofNat]
|
||||
theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
|
||||
USize.ofNatTruncate n = USize.ofNat n := by
|
||||
simp [ofNatTruncate, hn, USize.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem UInt8.ofNatTruncate_toNat (n : UInt8) : UInt8.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt8.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt16.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt16.ofNatTruncate n.toNat = n.toUInt16 := by
|
||||
rw [UInt16.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt16.ofNatTruncate_toNat (n : UInt16) : UInt16.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt16.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt32.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt32.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt32.ofNatTruncate_toNat (n : UInt32) : UInt32.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt32.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_uInt32ToNat (n : UInt32) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
|
||||
exact Nat.lt_trans (n.toNat_lt) (by decide)
|
||||
@[simp] theorem UInt64.ofNatTruncate_toNat (n : UInt64) : UInt64.ofNatTruncate n.toNat = n := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
|
||||
@[simp] theorem UInt64.ofNatTruncate_uSizeToNat (n : USize) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
|
||||
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uSizeToNat]
|
||||
exact n.toNat_lt
|
||||
|
||||
@[simp] theorem USize.ofNatTruncate_uInt8ToNat (n : UInt8) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_uInt16ToNat (n : UInt16) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_uInt32ToNat (n : UInt32) : USize.ofNatTruncate n.toNat = n.toUSize := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
|
||||
exact n.toNat_lt_usizeSize
|
||||
@[simp] theorem USize.ofNatTruncate_toNat (n : USize) : USize.ofNatTruncate n.toNat = n := by
|
||||
rw [USize.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt_size]
|
||||
|
||||
@[simp] theorem UInt8.toUInt8_toUInt16 (n : UInt8) : n.toUInt16.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUInt32 (n : UInt8) : n.toUInt32.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUInt64 (n : UInt8) : n.toUInt64.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt8_toUSize (n : UInt8) : n.toUSize.toUInt8 = n :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_toUInt32 (n : UInt8) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt16_toUInt64 (n : UInt8) : n.toUInt64.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt16_toUSize (n : UInt8) : n.toUSize.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt32_toUInt16 (n : UInt8) : n.toUInt16.toUInt32 = n.toUInt32 := rfl
|
||||
@[simp] theorem UInt8.toUInt32_toUInt64 (n : UInt8) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt8.toUInt32_toUSize (n : UInt8) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt8.toUInt64_toUInt16 (n : UInt8) : n.toUInt16.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_toUInt32 (n : UInt8) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt8.toUInt64_toUSize (n : UInt8) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUSize_toUInt16 (n : UInt8) : n.toUInt16.toUSize = n.toUSize := rfl
|
||||
@[simp] theorem UInt8.toUSize_toUInt32 (n : UInt8) : n.toUInt32.toUSize = n.toUSize := rfl
|
||||
@[simp] theorem UInt8.toUSize_toUInt64 (n : UInt8) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_toUInt32 (n : UInt16) : n.toUInt32.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt16.toUInt8_toUInt64 (n : UInt16) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt16.toUInt8_toUSize (n : UInt16) : n.toUSize.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt16_toUInt8 (n : UInt16) : n.toUInt8.toUInt16 = n % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt16_toUInt32 (n : UInt16) : n.toUInt32.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt16_toUInt64 (n : UInt16) : n.toUInt64.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt16_toUSize (n : UInt16) : n.toUSize.toUInt16 = n :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_toUInt8 (n : UInt16) : n.toUInt8.toUInt32 = n.toUInt32 % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt32_toUInt64 (n : UInt16) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUInt32_toUSize (n : UInt16) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt64_toUInt8 (n : UInt16) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_toUInt32 (n : UInt16) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
|
||||
@[simp] theorem UInt16.toUInt64_toUSize (n : UInt16) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUSize_toUInt8 (n : UInt16) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_toUInt32 (n : UInt16) : n.toUInt32.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt16.toUSize_toUInt64 (n : UInt16) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt8_toUInt16 (n : UInt32) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_toUInt64 (n : UInt32) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
@[simp] theorem UInt32.toUInt8_toUSize (n : UInt32) : n.toUSize.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_toUInt8 (n : UInt32) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt16_toUInt64 (n : UInt32) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
|
||||
@[simp] theorem UInt32.toUInt16_toUSize (n : UInt32) : n.toUSize.toUInt16 = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt32_toUInt8 (n : UInt32) : n.toUInt8.toUInt32 = n % 256 := rfl
|
||||
@[simp] theorem UInt32.toUInt32_toUInt16 (n : UInt32) : n.toUInt16.toUInt32 = n % 65536 := rfl
|
||||
@[simp] theorem UInt32.toUInt32_toUInt64 (n : UInt32) : n.toUInt64.toUInt32 = n :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt32_toUSize (n : UInt32) : n.toUSize.toUInt32 = n :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_toUInt8 (n : UInt32) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem UInt32.toUInt64_toUInt16 (n : UInt32) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
|
||||
@[simp] theorem UInt32.toUInt64_toUSize (n : UInt32) : n.toUSize.toUInt64 = n.toUInt64 := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUSize_toUInt8 (n : UInt32) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUSize_toUInt16 (n : UInt32) : n.toUInt16.toUSize = n.toUSize % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUSize_toUInt64 (n : UInt32) : n.toUInt64.toUSize = n.toUSize :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt8_toUInt16 (n : UInt64) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_toUInt32 (n : UInt64) : n.toUInt32.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_toUSize (n : UInt64) : n.toUSize.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt16_toUInt8 (n : UInt64) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_toUInt32 (n : UInt64) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_toUSize (n : UInt64) : n.toUSize.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_toUInt8 (n : UInt64) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_toUInt16 (n : UInt64) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt32_toUSize (n : UInt64) : n.toUSize.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt64_toUInt8 (n : UInt64) : n.toUInt8.toUInt64 = n % 256 := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toUInt16 (n : UInt64) : n.toUInt16.toUInt64 = n % 65536 := rfl
|
||||
@[simp] theorem UInt64.toUInt64_toUInt32 (n : UInt64) : n.toUInt32.toUInt64 = n % 4294967296 := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUSize_toUInt8 (n : UInt64) : n.toUInt8.toUSize = n.toUSize % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUSize_toUInt16 (n : UInt64) : n.toUInt16.toUSize = n.toUSize % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.toUInt8_toUInt16 (n : USize) : n.toUInt16.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_toUInt32 (n : USize) : n.toUInt32.toUInt8 = n.toUInt8 :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_toUInt64 (n : USize) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt16_toUInt8 (n : USize) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_toUInt32 (n : USize) : n.toUInt32.toUInt16 = n.toUInt16 :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_toUInt64 (n : USize) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt64_toUInt8 (n : USize) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
|
||||
@[simp] theorem USize.toUInt64_toUInt16 (n : USize) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt32_toUInt8 (n : USize) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_toUInt16 (n : USize) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_toUInt64 (n : USize) : n.toUInt64.toUInt32 = n.toUInt32 :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem USize.toUSize_toUInt8 (n : USize) : n.toUInt8.toUSize = n % 256 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUSize_toUInt16 (n : USize) : n.toUInt16.toUSize = n % 65536 :=
|
||||
USize.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUSize_toUInt64 (n : USize) : n.toUInt64.toUSize = n :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
-- Note: we are currently missing the following four results for which there does not seem to
|
||||
-- be a good candidate for the RHS:
|
||||
-- @[simp] theorem UInt64.toUInt64_toUSize (n : UInt64) : n.toUSize.toUInt64 = ? :=
|
||||
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
|
||||
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
|
||||
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
|
||||
|
||||
@@ -7,8 +7,8 @@ prelude
|
||||
import Init.Data.Vector.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Vector
|
||||
|
||||
@@ -473,6 +473,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Vector { x // p x } n) : Vect
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Vector.map_push]
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Vector { x // p x } n} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem unattach_mk {p : α → Prop} {xs : Array { x // p x }} {h : xs.size = n} :
|
||||
(mk xs h).unattach = mk xs.unattach (by simpa using h) := by
|
||||
simp [unattach]
|
||||
@@ -552,6 +556,18 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [Array.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Vector { x // p x } n} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.all f = xs.unattach.all g := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Vector { x // p x } n} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.any f = xs.unattach.any g := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {xs : Vector { x // p x } n} :
|
||||
|
||||
@@ -8,6 +8,7 @@ prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.InsertIdx
|
||||
import Init.Data.Array.Range
|
||||
import Init.Data.Range
|
||||
import Init.Data.Stream
|
||||
|
||||
@@ -17,8 +18,8 @@ import Init.Data.Stream
|
||||
`Vector α n` is a thin wrapper around `Array α` for arrays of fixed size `n`.
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
/-- `Vector α n` is an `Array α` with size `n`. -/
|
||||
structure Vector (α : Type u) (n : Nat) extends Array α where
|
||||
@@ -455,6 +456,9 @@ to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
|
||||
@[inline] def count [BEq α] (a : α) (xs : Vector α n) : Nat :=
|
||||
xs.toArray.count a
|
||||
|
||||
@[inline] def replace [BEq α] (xs : Vector α n) (a b : α) : Vector α n :=
|
||||
⟨xs.toArray.replace a b, by simp⟩
|
||||
|
||||
/--
|
||||
Pad a vector on the left with a given element.
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import Init.Data.Array.Find
|
||||
We are still missing results about `idxOf?`, `findIdx`, and `findIdx?`.
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Vector
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import Init.Data.Array.Find
|
||||
Lemmas about `Vector α n`
|
||||
-/
|
||||
|
||||
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
@@ -246,6 +246,9 @@ abbrev zipWithIndex_mk := @zipIdx_mk
|
||||
@[simp] theorem count_mk [BEq α] (xs : Array α) (h : xs.size = n) (a : α) :
|
||||
(Vector.mk xs h).count a = xs.count a := rfl
|
||||
|
||||
@[simp] theorem replace_mk [BEq α] (xs : Array α) (h : xs.size = n) (a b) :
|
||||
(Vector.mk xs h).replace a b = Vector.mk (xs.replace a b) (by simp [h]) := rfl
|
||||
|
||||
@[simp] theorem eq_mk : xs = Vector.mk as h ↔ xs.toArray = as := by
|
||||
cases xs
|
||||
simp
|
||||
@@ -406,6 +409,9 @@ theorem toArray_mapM_go [Monad m] [LawfulMonad m] (f : α → m β) (xs : Vector
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem replace_toArray [BEq α] (xs : Vector α n) (a b) :
|
||||
xs.toArray.replace a b = (xs.replace a b).toArray := rfl
|
||||
|
||||
@[simp] theorem find?_toArray (p : α → Bool) (xs : Vector α n) :
|
||||
xs.toArray.find? p = xs.find? p := by
|
||||
cases xs
|
||||
@@ -1586,9 +1592,11 @@ theorem getElem_append (xs : Vector α n) (ys : Vector α m) (i : Nat) (hi : i <
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp [Array.getElem_append, hi]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_left {xs : Vector α n} {ys : Vector α m} {i : Nat} (hi : i < n) :
|
||||
(xs ++ ys)[i] = xs[i] := by simp [getElem_append, hi]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_right {xs : Vector α n} {ys : Vector α m} {i : Nat} (h : i < n + m) (hi : n ≤ i) :
|
||||
(xs ++ ys)[i] = ys[i - n] := by
|
||||
rw [getElem_append, dif_neg (by omega)]
|
||||
@@ -2062,6 +2070,12 @@ theorem flatMap_mkArray {β} (f : α → Vector β m) : (mkVector n a).flatMap f
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
theorem getElem_eq_getElem_reverse {xs : Vector α n} {i} (h : i < n) :
|
||||
xs[i] = xs.reverse[n - 1 - i] := by
|
||||
rw [getElem_reverse]
|
||||
congr
|
||||
omega
|
||||
|
||||
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
|
||||
theorem getElem?_reverse' {xs : Vector α n} (i j) (h : i + j + 1 = n) : xs.reverse[i]? = xs[j]? := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
@@ -2468,6 +2482,14 @@ theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Vector α n} {a : α} :
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
|
||||
defeq issues in the implicit size argument.
|
||||
-/
|
||||
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
|
||||
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
|
||||
getElem_pop h
|
||||
|
||||
theorem getElem?_pop (xs : Vector α n) (i : Nat) :
|
||||
xs.pop[i]? = if i < n - 1 then xs[i]? else none := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
@@ -2504,6 +2526,236 @@ theorem pop_append {xs : Vector α n} {ys : Vector α m} :
|
||||
@[simp] theorem pop_mkVector (n) (a : α) : (mkVector n a).pop = mkVector (n - 1) a := by
|
||||
ext <;> simp
|
||||
|
||||
/-! ### replace -/
|
||||
|
||||
section replace
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem replace_cast {xs : Vector α n} {a b : α} :
|
||||
(xs.cast h).replace a b = (xs.replace a b).cast (by simp [h]) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
-- This hypothesis could probably be dropped from some of the lemmas below,
|
||||
-- by proving them direct from the definition rather than going via `List`.
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem replace_of_not_mem {xs : Vector α n} (h : ¬ a ∈ xs) : xs.replace a b = xs := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp_all
|
||||
|
||||
theorem getElem?_replace {xs : Vector α n} {i : Nat} :
|
||||
(xs.replace a b)[i]? = if xs[i]? == some a then if a ∈ xs.take i then some a else some b else xs[i]? := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.getElem?_replace]
|
||||
split <;> rename_i h
|
||||
· rw (occs := [2]) [if_pos]
|
||||
simpa using h
|
||||
· rw [if_neg]
|
||||
simpa using h
|
||||
|
||||
theorem getElem?_replace_of_ne {xs : Vector α n} {i : Nat} (h : xs[i]? ≠ some a) :
|
||||
(xs.replace a b)[i]? = xs[i]? := by
|
||||
simp_all [getElem?_replace]
|
||||
|
||||
theorem getElem_replace {xs : Vector α n} {i : Nat} (h : i < n) :
|
||||
(xs.replace a b)[i] = if xs[i] == a then if a ∈ xs.take i then a else b else xs[i] := by
|
||||
apply Option.some.inj
|
||||
rw [← getElem?_eq_getElem, getElem?_replace]
|
||||
split <;> split <;> simp_all
|
||||
|
||||
theorem getElem_replace_of_ne {xs : Vector α n} {i : Nat} {h : i < n} (h' : xs[i] ≠ a) :
|
||||
(xs.replace a b)[i]'(by simpa) = xs[i]'(h) := by
|
||||
rw [getElem_replace h]
|
||||
simp [h']
|
||||
|
||||
theorem replace_append {xs : Vector α n} {ys : Vector α m} :
|
||||
(xs ++ ys).replace a b = if a ∈ xs then xs.replace a b ++ ys else xs ++ ys.replace a b := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp only [mk_append_mk, replace_mk, eq_mk, Array.replace_append]
|
||||
split <;> simp_all
|
||||
|
||||
theorem replace_append_left {xs : Vector α n} {ys : Vector α m} (h : a ∈ xs) :
|
||||
(xs ++ ys).replace a b = xs.replace a b ++ ys := by
|
||||
simp [replace_append, h]
|
||||
|
||||
theorem replace_append_right {xs : Vector α n} {ys : Vector α m} (h : ¬ a ∈ xs) :
|
||||
(xs ++ ys).replace a b = xs ++ ys.replace a b := by
|
||||
simp [replace_append, h]
|
||||
|
||||
theorem replace_extract {xs : Vector α n} {i : Nat} :
|
||||
(xs.extract 0 i).replace a b = (xs.replace a b).extract 0 i := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.replace_extract]
|
||||
|
||||
@[simp] theorem replace_mkArray_self {a : α} (h : 0 < n) :
|
||||
(mkVector n a).replace a b = (#v[b] ++ mkVector (n - 1) a).cast (by omega) := by
|
||||
match n, h with
|
||||
| n + 1, _ => simp_all [mkVector_succ', replace_append]
|
||||
|
||||
@[simp] theorem replace_mkArray_ne {a b c : α} (h : !b == a) :
|
||||
(mkVector n a).replace b c = mkVector n a := by
|
||||
rw [replace_of_not_mem]
|
||||
simp_all
|
||||
|
||||
end replace
|
||||
|
||||
/-! ## Logic -/
|
||||
|
||||
/-! ### any / all -/
|
||||
|
||||
theorem not_any_eq_all_not (xs : Vector α n) (p : α → Bool) : (!xs.any p) = xs.all fun a => !p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.not_any_eq_all_not]
|
||||
|
||||
theorem not_all_eq_any_not (xs : Vector α n) (p : α → Bool) : (!xs.all p) = xs.any fun a => !p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.not_all_eq_any_not]
|
||||
|
||||
theorem and_any_distrib_left (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(q && xs.any p) = xs.any fun a => q && p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.and_any_distrib_left]
|
||||
|
||||
theorem and_any_distrib_right (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(xs.any p && q) = xs.any fun a => p a && q := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.and_any_distrib_right]
|
||||
|
||||
theorem or_all_distrib_left (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(q || xs.all p) = xs.all fun a => q || p a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.or_all_distrib_left]
|
||||
|
||||
theorem or_all_distrib_right (xs : Vector α n) (p : α → Bool) (q : Bool) :
|
||||
(xs.all p || q) = xs.all fun a => p a || q := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp [Array.or_all_distrib_right]
|
||||
|
||||
theorem any_eq_not_all_not (xs : Vector α n) (p : α → Bool) : xs.any p = !xs.all (!p .) := by
|
||||
simp only [not_all_eq_any_not, Bool.not_not]
|
||||
|
||||
@[simp] theorem any_map {xs : Vector α n} {p : β → Bool} : (xs.map f).any p = xs.any (p ∘ f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_map {xs : Vector α n} {p : β → Bool} : (xs.map f).all p = xs.all (p ∘ f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_filter {xs : Vector α n} {p q : α → Bool} :
|
||||
(xs.filter p).any q = xs.any fun a => p a && q a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_filter {xs : Vector α n} {p q : α → Bool} :
|
||||
(xs.filter p).all q = xs.all fun a => p a → q a := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_filterMap {xs : Vector α n} {f : α → Option β} {p : β → Bool} :
|
||||
(xs.filterMap f).any p = xs.any fun a => match f a with | some b => p b | none => false := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
rfl
|
||||
|
||||
@[simp] theorem all_filterMap {xs : Vector α n} {f : α → Option β} {p : β → Bool} :
|
||||
(xs.filterMap f).all p = xs.all fun a => match f a with | some b => p b | none => true := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
rfl
|
||||
|
||||
@[simp] theorem any_append {xs : Vector α n} {ys : Vector α m} :
|
||||
(xs ++ ys).any f = (xs.any f || ys.any f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_append {xs : Vector α n} {ys : Vector α m} :
|
||||
(xs ++ ys).all f = (xs.all f && ys.all f) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp
|
||||
|
||||
@[congr] theorem anyM_congr [Monad m]
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
xs.anyM p = ys.anyM q := by
|
||||
have : p = q := by funext a; apply h
|
||||
subst this
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[congr] theorem any_congr
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → Bool} (h : ∀ a, p a = q a) :
|
||||
xs.any p = ys.any q := by
|
||||
unfold any
|
||||
apply anyM_congr w h
|
||||
|
||||
@[congr] theorem allM_congr [Monad m]
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
xs.allM p = ys.allM q := by
|
||||
have : p = q := by funext a; apply h
|
||||
subst this
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[congr] theorem all_congr
|
||||
{xs ys : Vector α n} (w : xs = ys) {p q : α → Bool} (h : ∀ a, p a = q a) :
|
||||
xs.all p = ys.all q := by
|
||||
unfold all
|
||||
apply allM_congr w h
|
||||
|
||||
@[simp] theorem any_flatten {xss : Vector (Vector α n) m} : xss.flatten.any f = xss.any (any · f) := by
|
||||
cases xss using vector₂_induction
|
||||
simp
|
||||
|
||||
@[simp] theorem all_flatten {xss : Vector (Vector α n) m} : xss.flatten.all f = xss.all (all · f) := by
|
||||
cases xss using vector₂_induction
|
||||
simp
|
||||
|
||||
@[simp] theorem any_flatMap {xs : Vector α n} {f : α → Vector β m} {p : β → Bool} :
|
||||
(xs.flatMap f).any p = xs.any fun a => (f a).any p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [flatMap_mk, any_mk, Array.size_flatMap, size_toArray, Array.any_flatMap']
|
||||
congr
|
||||
funext
|
||||
congr
|
||||
simp [Vector.size_toArray]
|
||||
|
||||
@[simp] theorem all_flatMap {xs : Vector α n} {f : α → Vector β m} {p : β → Bool} :
|
||||
(xs.flatMap f).all p = xs.all fun a => (f a).all p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [flatMap_mk, all_mk, Array.size_flatMap, size_toArray, Array.all_flatMap']
|
||||
congr
|
||||
funext
|
||||
congr
|
||||
simp [Vector.size_toArray]
|
||||
|
||||
@[simp] theorem any_reverse {xs : Vector α n} : xs.reverse.any f = xs.any f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_reverse {xs : Vector α n} : xs.reverse.all f = xs.all f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_cast {xs : Vector α n} : (xs.cast h).any f = xs.any f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem all_cast {xs : Vector α n} : (xs.cast h).all f = xs.all f := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem any_mkVector {n : Nat} {a : α} :
|
||||
(mkVector n a).any f = if n = 0 then false else f a := by
|
||||
induction n <;> simp_all [mkVector_succ']
|
||||
|
||||
@[simp] theorem all_mkVector {n : Nat} {a : α} :
|
||||
(mkVector n a).all f = if n = 0 then true else f a := by
|
||||
induction n <;> simp_all +contextual [mkVector_succ']
|
||||
|
||||
/-! Content below this point has not yet been aligned with `List` and `Array`. -/
|
||||
|
||||
set_option linter.indexVariables false in
|
||||
@@ -2511,14 +2763,6 @@ set_option linter.indexVariables false in
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
|
||||
defeq issues in the implicit size argument.
|
||||
-/
|
||||
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
|
||||
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
|
||||
getElem_pop h
|
||||
|
||||
@[simp] theorem push_pop_back (xs : Vector α (n + 1)) : xs.pop.push xs.back = xs := by
|
||||
ext i
|
||||
by_cases h : i < n
|
||||
@@ -2582,11 +2826,6 @@ theorem swap_comm (xs : Vector α n) {i j : Nat} {hi hj} :
|
||||
simp only [swap_mk, mk.injEq]
|
||||
rw [Array.swap_comm]
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
|
||||
simp [Vector.range]
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
@[simp] theorem getElem_take (xs : Vector α n) (j : Nat) (hi : i < min n j) :
|
||||
|
||||
@@ -115,6 +115,9 @@ theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
|
||||
simp [Vector.range]
|
||||
|
||||
theorem range_eq_range' (n : Nat) : range n = range' 0 n := by
|
||||
simp [range, range', Array.range_eq_range']
|
||||
|
||||
|
||||
@@ -69,6 +69,11 @@ theorem eq_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a = b) = a := by s
|
||||
theorem eq_congr {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = a₂) (h₂ : b₁ = b₂) : (a₁ = b₁) = (a₂ = b₂) := by simp [*]
|
||||
theorem eq_congr' {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = b₂) (h₂ : b₁ = a₂) : (a₁ = b₁) = (a₂ = b₂) := by rw [h₁, h₂, Eq.comm (a := a₂)]
|
||||
|
||||
/-! Ne -/
|
||||
|
||||
theorem ne_of_ne_of_eq_left {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b ≠ c) : a ≠ c := by simp [*]
|
||||
theorem ne_of_ne_of_eq_right {α : Sort u} {a b c : α} (h₁ : a = c) (h₂ : b ≠ c) : b ≠ a := by simp [*]
|
||||
|
||||
/-! Bool.and -/
|
||||
|
||||
theorem Bool.and_eq_of_eq_true_left {a b : Bool} (h : a = true) : (a && b) = b := by simp [h]
|
||||
|
||||
@@ -111,9 +111,7 @@ def isExact : Constraint → Bool
|
||||
|
||||
theorem not_sat_of_isImpossible (h : isImpossible c) {t} : ¬ c.sat t := by
|
||||
rcases c with ⟨_ | l, _ | u⟩ <;> simp [isImpossible, sat] at h ⊢
|
||||
intro w
|
||||
rw [Int.not_le]
|
||||
exact Int.lt_of_lt_of_le h w
|
||||
exact Int.lt_of_lt_of_le h
|
||||
|
||||
/--
|
||||
Scale a constraint by multiplying by an integer.
|
||||
@@ -139,17 +137,14 @@ theorem scale_sat {c : Constraint} (k) (w : c.sat t) : (scale k c).sat (k * t) :
|
||||
· rcases c with ⟨_ | l, _ | u⟩ <;> split <;> rename_i h <;> simp_all [sat, flip, map]
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· constructor
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.1 (Int.le_of_lt h)
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.2 (Int.le_of_lt h)
|
||||
· replace h := Int.not_lt.mp h
|
||||
constructor
|
||||
· constructor
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.2
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.1
|
||||
|
||||
@@ -181,13 +176,13 @@ theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
|
||||
|
||||
/-- The conjunction of two constraints. -/
|
||||
def combine (x y : Constraint) : Constraint where
|
||||
lowerBound := max x.lowerBound y.lowerBound
|
||||
upperBound := min x.upperBound y.upperBound
|
||||
lowerBound := Option.merge max x.lowerBound y.lowerBound
|
||||
upperBound := Option.merge min x.upperBound y.upperBound
|
||||
|
||||
theorem combine_sat : (c : Constraint) → (c' : Constraint) → (t : Int) →
|
||||
(c.combine c').sat t = (c.sat t ∧ c'.sat t) := by
|
||||
rintro ⟨_ | l₁, _ | u₁⟩ <;> rintro ⟨_ | l₂, _ | u₂⟩ t
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le] at *
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.merge] at *
|
||||
· rw [And.comm]
|
||||
· rw [← and_assoc, And.comm (a := l₂ ≤ t), and_assoc]
|
||||
· rw [and_assoc]
|
||||
@@ -210,21 +205,19 @@ theorem div_sat (c : Constraint) (t : Int) (k : Nat) (n : k ≠ 0) (h : (k : Int
|
||||
· simp_all [sat, div]
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
rw [← Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
constructor
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.1
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
rw [← Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.2
|
||||
|
||||
/--
|
||||
|
||||
@@ -57,6 +57,11 @@ def EIO.catchExceptions (act : EIO ε α) (h : ε → BaseIO α) : BaseIO α :=
|
||||
| EStateM.Result.ok a s => EStateM.Result.ok a s
|
||||
| EStateM.Result.error ex s => h ex s
|
||||
|
||||
def EIO.ofExcept (e : Except ε α) : EIO ε α :=
|
||||
match e with
|
||||
| Except.ok a => pure a
|
||||
| Except.error e => throw e
|
||||
|
||||
open IO (Error) in
|
||||
abbrev IO : Type → Type := EIO Error
|
||||
|
||||
|
||||
@@ -48,7 +48,9 @@ inductive IO.Error where
|
||||
|
||||
| unexpectedEof
|
||||
| userError (msg : String)
|
||||
deriving Inhabited
|
||||
|
||||
instance : Inhabited IO.Error where
|
||||
default := .userError "(`Inhabited.default` for `IO.Error`)"
|
||||
|
||||
@[export lean_mk_io_user_error]
|
||||
def IO.userError (s : String) : IO.Error :=
|
||||
|
||||
@@ -73,5 +73,5 @@ def Promise.result := @Promise.result!
|
||||
/--
|
||||
Like `Promise.result`, but resolves to `dflt` if the promise is dropped without ever being resolved.
|
||||
-/
|
||||
def Promise.resultD (promise : Promise α) (dflt : α): Task α :=
|
||||
@[macro_inline] def Promise.resultD (promise : Promise α) (dflt : α) : Task α :=
|
||||
promise.result?.map (sync := true) (·.getD dflt)
|
||||
|
||||
@@ -8,12 +8,6 @@ import Lean.CoreM
|
||||
|
||||
namespace Lean
|
||||
|
||||
register_builtin_option debug.skipKernelTC : Bool := {
|
||||
defValue := false
|
||||
group := "debug"
|
||||
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
|
||||
}
|
||||
|
||||
/-- Adds given declaration to the environment, respecting `debug.skipKernelTC`. -/
|
||||
def Kernel.Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except Exception Environment :=
|
||||
|
||||
@@ -155,6 +155,7 @@ def emitMainFn : M Unit := do
|
||||
int main(int argc, char ** argv) {
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
lean_object* in; lean_object* res;";
|
||||
if usesLeanAPI then
|
||||
|
||||
@@ -514,7 +514,9 @@ def inferStep : InterpM Bool := do
|
||||
let currentVal ← getFunVal idx
|
||||
withReader (fun ctx => { ctx with currFnIdx := idx }) do
|
||||
decl.params.forM fun p => updateVarAssignment p.fvarId .top
|
||||
decl.value.forCodeM interpCode
|
||||
match decl.value with
|
||||
| .code code .. => interpCode code
|
||||
| .extern .. => updateCurrFnSummary .top
|
||||
let newVal ← getFunVal idx
|
||||
if currentVal != newVal then
|
||||
return true
|
||||
|
||||
@@ -149,8 +149,10 @@ def Decl.reduceArity (decl : Decl) : CompilerM (Array Decl) := do
|
||||
match decl.value with
|
||||
| .code code =>
|
||||
let used ← collectUsedParams decl
|
||||
if used.size == decl.params.size then
|
||||
return #[decl] -- Declarations uses all parameters
|
||||
if used.size == decl.params.size || used.size == 0 then
|
||||
-- Do nothing if all params were used, or if no params were used. In the latter case,
|
||||
-- this would promote the decl to a constant, which could execute unreachable code.
|
||||
return #[decl]
|
||||
else
|
||||
trace[Compiler.reduceArity] "{decl.name}, used params: {used.toList.map mkFVar}"
|
||||
let mask := decl.params.map fun param => used.contains param.fvarId
|
||||
|
||||
@@ -194,7 +194,7 @@ protected def withFreshMacroScope (x : CoreM α) : CoreM α := do
|
||||
|
||||
instance : MonadQuotation CoreM where
|
||||
getCurrMacroScope := return (← read).currMacroScope
|
||||
getMainModule := return (← get).env.mainModule
|
||||
getMainModule := return (← getEnv).mainModule
|
||||
withFreshMacroScope := Core.withFreshMacroScope
|
||||
|
||||
instance : Elab.MonadInfoTree CoreM where
|
||||
@@ -413,6 +413,26 @@ register_builtin_option stderrAsMessages : Bool := {
|
||||
descr := "(server) capture output to the Lean stderr channel (such as from `dbg_trace`) during elaboration of a command as a diagnostic message"
|
||||
}
|
||||
|
||||
/--
|
||||
Creates snapshot reporting given `withIsolatedStreams` output and diagnostics and traces from the
|
||||
given state.
|
||||
-/
|
||||
def mkSnapshot (output : String) (ctx : Context) (st : State)
|
||||
(desc : String := by exact decl_name%.toString) : BaseIO Language.SnapshotTree := do
|
||||
let mut msgs := st.messages
|
||||
if !output.isEmpty then
|
||||
msgs := msgs.add {
|
||||
fileName := ctx.fileName
|
||||
severity := MessageSeverity.information
|
||||
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
|
||||
data := output
|
||||
}
|
||||
return .mk {
|
||||
desc
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgs)
|
||||
traces := st.traceState
|
||||
} st.snapshotTasks
|
||||
|
||||
open Language in
|
||||
/--
|
||||
Wraps the given action for use in `BaseIO.asTask` etc., discarding its final state except for
|
||||
@@ -443,20 +463,7 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (cancelTk? : Option IO.Cance
|
||||
let ctx ← readThe Core.Context
|
||||
return do
|
||||
match (← t.toBaseIO) with
|
||||
| .ok (output, st) =>
|
||||
let mut msgs := st.messages
|
||||
if !output.isEmpty then
|
||||
msgs := msgs.add {
|
||||
fileName := ctx.fileName
|
||||
severity := MessageSeverity.information
|
||||
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
|
||||
data := output
|
||||
}
|
||||
return .mk {
|
||||
desc
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgs)
|
||||
traces := st.traceState
|
||||
} st.snapshotTasks
|
||||
| .ok (output, st) => mkSnapshot output ctx st desc
|
||||
-- interrupt or abort exception as `try catch` above should have caught any others
|
||||
| .error _ => default
|
||||
|
||||
@@ -528,7 +535,9 @@ opaque compileDeclsOld (env : Environment) (opt : @& Options) (decls : @& List N
|
||||
-- `ref?` is used for error reporting if available
|
||||
partial def compileDecls (decls : List Name) (ref? : Option Declaration := none)
|
||||
(logErrors := true) : CoreM Unit := do
|
||||
if !Elab.async.get (← getOptions) then
|
||||
-- When inside `realizeConst`, do compilation synchronously so that `_cstage*` constants are found
|
||||
-- by the replay code
|
||||
if !Elab.async.get (← getOptions) || (← getEnv).isRealizing then
|
||||
doCompile
|
||||
return
|
||||
let env ← getEnv
|
||||
@@ -646,6 +655,11 @@ def logMessageKind (kind : Name) : CoreM Bool := do
|
||||
modify fun s => { s with messages.loggedKinds := s.messages.loggedKinds.insert kind }
|
||||
return true
|
||||
|
||||
@[inherit_doc Environment.enableRealizationsForConst]
|
||||
def enableRealizationsForConst (n : Name) : CoreM Unit := do
|
||||
let env ← (← getEnv).enableRealizationsForConst (← getOptions) n
|
||||
setEnv env
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.async
|
||||
registerTraceClass `Elab.block
|
||||
|
||||
@@ -931,6 +931,7 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
|
||||
for ctor in view.ctors do
|
||||
if (ctor.declId.getPos? (canonicalOnly := true)).isSome then
|
||||
Term.addTermInfo' ctor.declId (← mkConstWithLevelParams ctor.declName) (isBinder := true)
|
||||
enableRealizationsForConst ctor.declName
|
||||
return res
|
||||
|
||||
private def mkAuxConstructions (declNames : Array Name) : TermElabM Unit := do
|
||||
|
||||
@@ -161,6 +161,7 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
|
||||
if compile && shouldGenCodeFor preDef then
|
||||
compileDecl decl
|
||||
if applyAttrAfterCompilation then
|
||||
enableRealizationsForConst preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ Assign final attributes to the definitions. Assumes the EqnInfos to be already p
|
||||
def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
markAsRecursive preDef.declName
|
||||
enableRealizationsForConst preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
-- Unless the user asks for something else, mark the definition as irreducible
|
||||
|
||||
@@ -20,18 +20,23 @@ Simple, coarse-grained equation theorem for nonrecursive definitions.
|
||||
-/
|
||||
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
|
||||
if let some (.defnInfo info) := (← getEnv).find? declName then
|
||||
let name := declName ++ suffix
|
||||
-- determinism: `name` and `info` are dependent only on `declName`, not any later env
|
||||
-- modifications
|
||||
realizeConst declName name (doRealize name info)
|
||||
return some name
|
||||
else
|
||||
return none
|
||||
where
|
||||
doRealize name info :=
|
||||
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
|
||||
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
|
||||
let type ← mkForallFVars xs (← mkEq lhs body)
|
||||
let value ← mkLambdaFVars xs (← mkEqRefl lhs)
|
||||
let name := declName ++ suffix
|
||||
addDecl <| Declaration.thmDecl {
|
||||
addDecl <| .thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return some name
|
||||
else
|
||||
return none
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if (← isRecursiveDefinition declName) then
|
||||
|
||||
@@ -193,6 +193,10 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
for preDef in preDefs do
|
||||
-- must happen in separate loop so realizations can see eqnInfos of all other preDefs
|
||||
enableRealizationsForConst preDef.declName
|
||||
-- must happen after `enableRealizationsForConst`
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option T
|
||||
unless (← isProp preDef.type) do
|
||||
WF.mkUnfoldEq preDef preDefNonRec.declName wfPreprocessProof
|
||||
Mutual.addPreDefAttributes preDefs
|
||||
enableRealizationsForConst preDefNonRec.declName
|
||||
|
||||
builtin_initialize registerTraceClass `Elab.definition.wf
|
||||
|
||||
|
||||
@@ -100,4 +100,7 @@ def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessPr
|
||||
}
|
||||
trace[Elab.definition.wf] "mkUnfoldEq defined {.ofConstName name}"
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.wf.eqns
|
||||
|
||||
end Lean.Elab.WF
|
||||
|
||||
@@ -825,20 +825,18 @@ private partial def checkResultingUniversesForFields (fieldInfos : Array StructF
|
||||
which is not less than or equal to the structure's resulting universe level{indentD u}"
|
||||
throwErrorAt info.ref msg
|
||||
|
||||
@[extern "lean_mk_projections"]
|
||||
private opaque mkProjections (env : Environment) (structName : Name) (projs : List Name) (isClass : Bool) : Except Kernel.Exception Environment
|
||||
|
||||
private def addProjections (r : ElabHeaderResult) (fieldInfos : Array StructFieldInfo) : TermElabM Unit := do
|
||||
if r.type.isProp then
|
||||
if let some fieldInfo ← fieldInfos.findM? (not <$> Meta.isProof ·.fvar) then
|
||||
throwErrorAt fieldInfo.ref m!"failed to generate projections for 'Prop' structure, field '{format fieldInfo.name}' is not a proof"
|
||||
let projNames := fieldInfos |>.filter (!·.isFromSubobject) |>.map (·.declName)
|
||||
let env ← getEnv
|
||||
let env ← ofExceptKernelException (mkProjections env r.view.declName projNames.toList r.view.isClass)
|
||||
setEnv env
|
||||
let projDecls : Array StructProjDecl :=
|
||||
fieldInfos
|
||||
|>.filter (!·.isFromSubobject)
|
||||
|>.map (fun info => { ref := info.ref, projName := info.declName })
|
||||
mkProjections r.view.declName projDecls r.view.isClass
|
||||
for fieldInfo in fieldInfos do
|
||||
if fieldInfo.isSubobject then
|
||||
addDeclarationRangesFromSyntax fieldInfo.declName r.view.ref fieldInfo.ref
|
||||
for decl in projDecls do
|
||||
-- projections may generate equation theorems
|
||||
enableRealizationsForConst decl.projName
|
||||
|
||||
private def registerStructure (structName : Name) (infos : Array StructFieldInfo) : TermElabM Unit := do
|
||||
let fields ← infos.filterMapM fun info => do
|
||||
|
||||
@@ -190,6 +190,26 @@ where
|
||||
return (x, toExpr <| UInt64.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for UInt64 was not 64 bit but {value.w} bit"
|
||||
| Int8.toBitVec x =>
|
||||
if h : value.w = 8 then
|
||||
return (x, toExpr <| Int8.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int8 was not 8 bit but {value.w} bit"
|
||||
| Int16.toBitVec x =>
|
||||
if h : value.w = 16 then
|
||||
return (x, toExpr <| Int16.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int16 was not 16 bit but {value.w} bit"
|
||||
| Int32.toBitVec x =>
|
||||
if h : value.w = 32 then
|
||||
return (x, toExpr <| Int32.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int32 was not 32 bit but {value.w} bit"
|
||||
| Int64.toBitVec x =>
|
||||
if h : value.w = 64 then
|
||||
return (x, toExpr <| Int64.ofBitVec (h ▸ value.bv))
|
||||
else
|
||||
throwError m!"Value for Int64 was not 64 bit but {value.w} bit"
|
||||
| _ =>
|
||||
match var with
|
||||
| .app (.const (.str p s) []) arg =>
|
||||
|
||||
@@ -274,11 +274,11 @@ partial def enumsPass : Pass where
|
||||
|
||||
let simprocs ← Simp.SimprocsArray.add #[] ``enumsPassPost true
|
||||
let ⟨result?, _⟩ ←
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := simprocs)
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := simprocs)
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
postprocess newGoal |>.run' {}
|
||||
where
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Attr
|
||||
import Lean.Elab.Tactic.Simp
|
||||
@@ -14,7 +15,7 @@ This module contains the implementation of the pre processing pass for reducing
|
||||
|
||||
It:
|
||||
1. runs the `int_toBitVec` simp set
|
||||
2. If `USize.toBitVec` is used anywhere looks for equations of the form
|
||||
2. If `USize.toBitVec`/`ISize.toBitVec` is used anywhere looks for equations of the form
|
||||
`System.Platform.numBits = constant` (or flipped) and uses them to convert the system back to
|
||||
fixed width.
|
||||
-/
|
||||
@@ -25,11 +26,12 @@ namespace Frontend.Normalize
|
||||
open Lean.Meta
|
||||
|
||||
/--
|
||||
Contains information for the `USize` elimination pass.
|
||||
Contains information for the `USize`/`ISize` elimination pass.
|
||||
-/
|
||||
structure USizeState where
|
||||
structure SizeState where
|
||||
/--
|
||||
Contains terms of the form `USize.toBitVec e` that we will translate to constant width `BitVec`.
|
||||
Contains terms of the form `USize.toBitVec e` and `ISize.toBitVec e` that we will translate to
|
||||
constant width `BitVec`.
|
||||
-/
|
||||
relevantTerms : Std.HashSet Expr := {}
|
||||
/--
|
||||
@@ -37,16 +39,16 @@ structure USizeState where
|
||||
-/
|
||||
relevantHyps : Std.HashSet FVarId := {}
|
||||
|
||||
private abbrev M := StateRefT USizeState MetaM
|
||||
private abbrev M := StateRefT SizeState MetaM
|
||||
|
||||
namespace M
|
||||
|
||||
@[inline]
|
||||
def addUSizeTerm (e : Expr) : M Unit := do
|
||||
def addSizeTerm (e : Expr) : M Unit := do
|
||||
modify fun s => { s with relevantTerms := s.relevantTerms.insert e }
|
||||
|
||||
@[inline]
|
||||
def addUSizeHyp (f : FVarId) : M Unit := do
|
||||
def addSizeHyp (f : FVarId) : M Unit := do
|
||||
modify fun s => { s with relevantHyps := s.relevantHyps.insert f }
|
||||
|
||||
end M
|
||||
@@ -64,30 +66,30 @@ def intToBitVecPass : Pass where
|
||||
let hyps ← goal.getNondepPropHyps
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := hyps)
|
||||
let some (_, goal) := result? | return none
|
||||
handleUSize goal |>.run' {}
|
||||
handleSize goal |>.run' {}
|
||||
where
|
||||
handleUSize (goal : MVarId) : M MVarId := do
|
||||
if ← detectUSize goal then
|
||||
replaceUSize goal
|
||||
handleSize (goal : MVarId) : M MVarId := do
|
||||
if ← detectSize goal then
|
||||
replaceSize goal
|
||||
else
|
||||
return goal
|
||||
|
||||
detectUSize (goal : MVarId) : M Bool := do
|
||||
detectSize (goal : MVarId) : M Bool := do
|
||||
goal.withContext do
|
||||
for hyp in ← getPropHyps do
|
||||
(← hyp.getType).forEachWhere
|
||||
(stopWhenVisited := true)
|
||||
(·.isAppOfArity ``USize.toBitVec 1)
|
||||
(fun e => e.isAppOfArity ``USize.toBitVec 1 || e.isAppOfArity ``ISize.toBitVec 1)
|
||||
fun e => do
|
||||
M.addUSizeTerm e
|
||||
M.addUSizeHyp hyp
|
||||
M.addSizeTerm e
|
||||
M.addSizeHyp hyp
|
||||
|
||||
return !(← get).relevantTerms.isEmpty
|
||||
|
||||
/--
|
||||
Turn `goal` into a goal containing `BitVec const` instead of `USize`.
|
||||
Turn `goal` into a goal containing `BitVec const` instead of `USize`/`ISize`.
|
||||
-/
|
||||
replaceUSize (goal : MVarId) : M MVarId := do
|
||||
replaceSize (goal : MVarId) : M MVarId := do
|
||||
if let some (numBits, numBitsEq) ← findNumBitsEq goal then
|
||||
goal.withContext do
|
||||
let relevantHyps := (← get).relevantHyps.toArray.map mkFVar
|
||||
@@ -138,13 +140,14 @@ where
|
||||
numBitsEq
|
||||
(mkMVar newGoal)
|
||||
goal.assign <| mkAppN casesOn (relevantTerms ++ abstractedHyps)
|
||||
-- remove all of the hold hypotheses about USize.toBitVec to prevent false counter examples
|
||||
-- remove all of the hold hypotheses about USize.toBitVec/ISize.toBitVec to prevent
|
||||
-- false counter examples
|
||||
(newGoal, _) ← newGoal.tryClearMany' (abstractedHyps.map Expr.fvarId!)
|
||||
-- intro both the new `BitVec const` as well as all hypotheses about them
|
||||
(_, newGoal) ← newGoal.introN (relevantTerms.size + abstractedHyps.size)
|
||||
return newGoal
|
||||
else
|
||||
logWarning m!"Detected USize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
|
||||
logWarning m!"Detected USize/ISize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
|
||||
return goal
|
||||
|
||||
/--
|
||||
|
||||
@@ -15,11 +15,12 @@ structures containing information about supported types into individual parts re
|
||||
|
||||
The implementation runs cases recursively on all "interesting" types where a type is interesting if
|
||||
it is a non recursive structure and at least one of the following conditions hold:
|
||||
- it contains something of type `BitVec`/`UIntX`/`Bool`
|
||||
- it contains something of type `BitVec`/`UIntX`/`IntX`/`Bool`
|
||||
- it is parametrized by an interesting type
|
||||
- it contains another interesting type
|
||||
Afterwards we also apply relevant `injEq` theorems to support at least equality for these types out
|
||||
of the box.
|
||||
Afterwards we also:
|
||||
- apply relevant `injEq` theorems to support at least equality for these types out of the box.
|
||||
- push projections of relevant types inside of `ite` and `cond`.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
@@ -27,6 +28,33 @@ namespace Frontend.Normalize
|
||||
|
||||
open Lean.Meta
|
||||
|
||||
|
||||
def applyIteSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
|
||||
if h : args.size ≠ 0 then
|
||||
let_expr ite α c instDec t e := args.back | return .continue
|
||||
let params := args.pop
|
||||
let projApp := mkAppN proj params
|
||||
let newT := mkApp projApp t
|
||||
let newE := mkApp projApp e
|
||||
let newIf ← mkAppOptM ``ite #[none, c, instDec, newT, newE]
|
||||
let proof ← mkAppOptM ``apply_ite #[α, none, projApp, c, instDec, t, e]
|
||||
return .visit { expr := newIf, proof? := some proof }
|
||||
else
|
||||
return .continue
|
||||
|
||||
def applyCondSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
|
||||
if h : args.size ≠ 0 then
|
||||
let_expr cond α c t e := args.back | return .continue
|
||||
let params := args.pop
|
||||
let projApp := mkAppN proj params
|
||||
let newT := mkApp projApp t
|
||||
let newE := mkApp projApp e
|
||||
let newCond ← mkAppOptM ``cond #[none, c, newT, newE]
|
||||
let proof ← mkAppOptM ``Bool.apply_cond #[α, none, projApp, c, t, e]
|
||||
return .visit { expr := newCond, proof? := some proof }
|
||||
else
|
||||
return .continue
|
||||
|
||||
partial def structuresPass : Pass where
|
||||
name := `structures
|
||||
run' goal := do
|
||||
@@ -43,7 +71,9 @@ partial def structuresPass : Pass where
|
||||
| _ => throwError "structures preprocessor generated more than 1 goal"
|
||||
where
|
||||
postprocess (goal : MVarId) (interesting : Std.HashSet Name) : PreProcessM (Option MVarId) := do
|
||||
let env ← getEnv
|
||||
goal.withContext do
|
||||
let mut simprocs : Simprocs := {}
|
||||
let mut relevantLemmas : SimpTheoremsArray := #[]
|
||||
relevantLemmas ← relevantLemmas.addTheorem (.decl ``ne_eq) (← mkConstWithLevelParams ``ne_eq)
|
||||
for const in interesting do
|
||||
@@ -54,14 +84,43 @@ where
|
||||
trace[Meta.Tactic.bv] m!"Using injEq lemma: {lemmaName}"
|
||||
let statement ← mkConstWithLevelParams lemmaName
|
||||
relevantLemmas ← relevantLemmas.addTheorem (.decl lemmaName) statement
|
||||
let fields := (getStructureInfo env const).fieldNames.size
|
||||
let numParams := constInfo.numParams
|
||||
for proj in [0:fields] do
|
||||
-- We use the simprocs with pre such that we push in projections eagerly in order to
|
||||
-- potentially not have to simplify complex structure expressions that we only project one
|
||||
-- element out of.
|
||||
let path := mkDiscrPathFor const numParams proj ``ite 5
|
||||
simprocs := simprocs.addCore path ``applyIteSimproc false (.inl applyIteSimproc)
|
||||
let path := mkDiscrPathFor const numParams proj ``cond 4
|
||||
simprocs := simprocs.addCore path ``applyCondSimproc false (.inl applyCondSimproc)
|
||||
let cfg ← PreProcessM.getConfig
|
||||
let simpCtx ← Simp.mkContext
|
||||
(config := { failIfUnchanged := false, maxSteps := cfg.maxSteps })
|
||||
(simpTheorems := relevantLemmas)
|
||||
(congrTheorems := ← getSimpCongrTheorems)
|
||||
let ⟨result?, _⟩ ← simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := ← getPropHyps)
|
||||
let ⟨result?, _⟩ ←
|
||||
simpGoal
|
||||
goal
|
||||
(ctx := simpCtx)
|
||||
(simprocs := #[simprocs])
|
||||
(fvarIdsToSimp := ← getPropHyps)
|
||||
let some (_, newGoal) := result? | return none
|
||||
return newGoal
|
||||
|
||||
/--
|
||||
For `Prod.fst` and `ite` this function creates the path: `Prod.fst (ite (Prod _ _) _ _ _ _)`.
|
||||
This path can be used to match on applications of structure projections onto control flow primitives.
|
||||
-/
|
||||
mkDiscrPathFor (struct : Name) (structParams : Nat) (projIdx : Nat) (controlFlow : Name)
|
||||
(controlFlowParams : Nat) : Array DiscrTree.Key := Id.run do
|
||||
let stars := structParams + controlFlowParams - 1
|
||||
let mut path : Array DiscrTree.Key := Array.mkEmpty (3 + stars)
|
||||
path := path.push <| .proj struct projIdx 0
|
||||
path := path.push <| .const controlFlow controlFlowParams
|
||||
path := path.push <| .const struct structParams
|
||||
path := Nat.fold (init := path) stars (fun _ _ acc => acc.push .star)
|
||||
return path
|
||||
|
||||
end Frontend.Normalize
|
||||
end Lean.Elab.Tactic.BVDecide
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Basic
|
||||
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
|
||||
|
||||
/-!
|
||||
@@ -64,6 +65,11 @@ where
|
||||
| UInt32 => return true
|
||||
| UInt64 => return true
|
||||
| USize => return true
|
||||
| Int8 => return true
|
||||
| Int16 => return true
|
||||
| Int32 => return true
|
||||
| Int64 => return true
|
||||
| ISize => return true
|
||||
| Bool => return true
|
||||
| _ =>
|
||||
let some const := expr.getAppFn.constName? | return false
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.RBMap
|
||||
import Init.Data.Nat.Fold
|
||||
import Std.Tactic.BVDecide.LRAT.Actions
|
||||
import Std.Data.HashMap
|
||||
|
||||
@@ -17,7 +17,6 @@ This module implements the LRAT trimming algorithm described in section 4 of
|
||||
namespace Lean.Elab.Tactic.BVDecide
|
||||
namespace LRAT
|
||||
|
||||
open Lean (RBMap)
|
||||
open Std.Tactic.BVDecide.LRAT (IntAction)
|
||||
|
||||
namespace trim
|
||||
@@ -41,16 +40,18 @@ structure Context where
|
||||
|
||||
structure State where
|
||||
/--
|
||||
The set of used proof step ids.
|
||||
For each proof step `i` contains at index `i - initialId` `0` if `i` is unused, `1` if it is
|
||||
used.
|
||||
-/
|
||||
used : RBMap Nat Unit compare := {}
|
||||
used : ByteArray
|
||||
/--
|
||||
A mapping from old proof step ids to new ones. Used such that the proof remains a sequence without
|
||||
For each proof step `i` contains at index `i - initialId` the step that `i` maps to in the new
|
||||
proof or `0` if that step is not yet set. Used such that the proof remains a sequence without
|
||||
gaps.
|
||||
-/
|
||||
mapped : Std.HashMap Nat Nat := {}
|
||||
mapped : Array Nat
|
||||
|
||||
abbrev M : Type → Type := ReaderT Context <| ExceptT String <| StateM State
|
||||
abbrev M : Type → Type := ReaderT Context <| StateM State
|
||||
|
||||
namespace M
|
||||
|
||||
@@ -78,7 +79,9 @@ def run (proof : Array IntAction) (x : M α) : Except String α := do
|
||||
| .addEmpty id .. | .addRup id .. | .addRat id .. => acc.insert id a
|
||||
| .del .. => acc
|
||||
let proof := proof.foldl (init := {}) folder
|
||||
ReaderT.run x { proof, initialId, addEmptyId } |>.run |>.run' {}
|
||||
let used := Nat.fold proof.size (init := ByteArray.mkEmpty proof.size) (fun _ _ acc => acc.push 0)
|
||||
let mapped := Array.mkArray proof.size 0
|
||||
return ReaderT.run x { proof, initialId, addEmptyId } |>.run' { used, mapped }
|
||||
|
||||
@[inline]
|
||||
def getInitialId : M Nat := do
|
||||
@@ -90,6 +93,10 @@ def getEmptyId : M Nat := do
|
||||
let ctx ← read
|
||||
return ctx.addEmptyId
|
||||
|
||||
@[inline]
|
||||
private def idIndex (id : Nat) : M Nat := do
|
||||
return id - (← M.getInitialId)
|
||||
|
||||
@[inline]
|
||||
def getProofStep (id : Nat) : M (Option IntAction) := do
|
||||
let ctx ← read
|
||||
@@ -98,22 +105,20 @@ def getProofStep (id : Nat) : M (Option IntAction) := do
|
||||
@[inline]
|
||||
def isUsed (id : Nat) : M Bool := do
|
||||
let s ← get
|
||||
return s.used.contains id
|
||||
return s.used[← idIndex id]! == 1
|
||||
|
||||
@[inline]
|
||||
def markUsed (id : Nat) : M Unit := do
|
||||
-- If we are referring to a proof step that is not part of the proof, it is part of the CNF.
|
||||
-- We do not trim the CNF so just forget about the fact that this step was used.
|
||||
if (← getProofStep id).isSome then
|
||||
modify (fun s => { s with used := s.used.insert id () })
|
||||
if id >= (← M.getInitialId) then
|
||||
let idx ← idIndex id
|
||||
modify (fun s => { s with used := s.used.set! idx 1 })
|
||||
|
||||
@[inline]
|
||||
def getUsedSet : M (RBMap Nat Unit Ord.compare) := do
|
||||
let s ← get
|
||||
return s.used
|
||||
|
||||
def registerIdMap (oldId : Nat) (newId : Nat) : M Unit := do
|
||||
modify (fun s => { s with mapped := s.mapped.insert oldId newId })
|
||||
let idx ← idIndex oldId
|
||||
modify (fun s => { s with mapped := s.mapped.set! idx newId })
|
||||
|
||||
def mapStep (step : IntAction) : M IntAction := do
|
||||
match step with
|
||||
@@ -139,8 +144,12 @@ def mapStep (step : IntAction) : M IntAction := do
|
||||
where
|
||||
@[inline]
|
||||
mapIdent (ident : Nat) : M Nat := do
|
||||
let s ← get
|
||||
return s.mapped[ident]? |>.getD ident
|
||||
if ident < (← getInitialId) then
|
||||
return ident
|
||||
else
|
||||
let s ← get
|
||||
let newId := s.mapped[← idIndex ident]!
|
||||
return newId
|
||||
|
||||
end M
|
||||
|
||||
@@ -150,14 +159,17 @@ up with DFS.
|
||||
-/
|
||||
partial def useAnalysis : M Unit := do
|
||||
let emptyId ← M.getEmptyId
|
||||
go [emptyId]
|
||||
go #[emptyId]
|
||||
where
|
||||
go (workList : List Nat) : M Unit := do
|
||||
match workList with
|
||||
| [] => return ()
|
||||
| id :: workList =>
|
||||
go (worklist : Array Nat) : M Unit := do
|
||||
let mut worklist := worklist
|
||||
if h : worklist.size = 0 then
|
||||
return ()
|
||||
else
|
||||
let id := worklist.back
|
||||
worklist := worklist.pop
|
||||
if ← M.isUsed id then
|
||||
go workList
|
||||
go worklist
|
||||
else
|
||||
M.markUsed id
|
||||
let step? ← M.getProofStep id
|
||||
@@ -165,36 +177,37 @@ where
|
||||
| some step =>
|
||||
match step with
|
||||
| .addEmpty _ hints =>
|
||||
let workList := hints.toList ++ workList
|
||||
go workList
|
||||
worklist := worklist ++ hints
|
||||
go worklist
|
||||
| .addRup _ _ hints =>
|
||||
let workList := hints.toList ++ workList
|
||||
go workList
|
||||
worklist := worklist ++ hints
|
||||
go worklist
|
||||
| .addRat _ _ _ rupHints ratHints =>
|
||||
let folder acc a :=
|
||||
a.fst :: a.snd.toList ++ acc
|
||||
let ratHints := ratHints.foldl (init := []) folder
|
||||
let workList := rupHints.toList ++ ratHints ++ workList
|
||||
go workList
|
||||
| .del .. => go workList
|
||||
| none => go workList
|
||||
let folder acc a := acc.push a.fst ++ a.snd
|
||||
let ratHints := ratHints.foldl (init := Array.mkEmpty ratHints.size) folder
|
||||
worklist := worklist ++ ratHints ++ rupHints
|
||||
go worklist
|
||||
| .del .. => go worklist
|
||||
| none => go worklist
|
||||
|
||||
/--
|
||||
Map the set of used proof steps to a new LRAT proof that has no holes in the sequence of proof
|
||||
identifiers.
|
||||
-/
|
||||
def mapping : M (Array IntAction) := do
|
||||
let used ← M.getUsedSet
|
||||
let mut nextMapped ← M.getInitialId
|
||||
let mut newProof := Array.mkEmpty used.size
|
||||
for (id, _) in used do
|
||||
M.registerIdMap id nextMapped
|
||||
-- This should never panic as the use def analysis has already marked this step as being used
|
||||
-- so it must exist.
|
||||
let step := (← M.getProofStep id).get!
|
||||
let newStep ← M.mapStep step
|
||||
newProof := newProof.push newStep
|
||||
nextMapped := nextMapped + 1
|
||||
let emptyId ← M.getEmptyId
|
||||
let initialId ← M.getInitialId
|
||||
let mut nextMapped := initialId
|
||||
let mut newProof := #[]
|
||||
for id in [initialId:emptyId+1] do
|
||||
if ← M.isUsed id then
|
||||
M.registerIdMap id nextMapped
|
||||
-- This should never panic as the use def analysis has already marked this step as being used
|
||||
-- so it must exist.
|
||||
let step := (← M.getProofStep id).get!
|
||||
let newStep ← M.mapStep step
|
||||
newProof := newProof.push newStep
|
||||
nextMapped := nextMapped + 1
|
||||
return newProof
|
||||
|
||||
def go : M (Array IntAction) := do
|
||||
@@ -207,7 +220,7 @@ end trim
|
||||
Trim the LRAT `proof` by removing all steps that are not used in reaching the empty clause
|
||||
conclusion.
|
||||
-/
|
||||
def trim (proof : Array IntAction) : Except String (Array IntAction) :=
|
||||
def trim (proof : Array IntAction) : Except String (Array IntAction) := do
|
||||
trim.go.run proof
|
||||
|
||||
end LRAT
|
||||
|
||||
@@ -173,6 +173,10 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
|
||||
addNewArg arg
|
||||
loop
|
||||
| _ =>
|
||||
let s ← get
|
||||
let ctx ← read
|
||||
unless s.targetPos = ctx.targets.size do
|
||||
throwError "unexpected number of targets for '{elimInfo.elimExpr}'"
|
||||
pure ()
|
||||
let (_, s) ← (loop).run { elimInfo := elimInfo, targets := targets }
|
||||
|>.run { f := elimInfo.elimExpr, fType := elimInfo.elimType, motive := none }
|
||||
|
||||
@@ -24,28 +24,70 @@ Implementation of the `exact?` tactic.
|
||||
def exact? (ref : Syntax) (required : Option (Array (TSyntax `term))) (requireClose : Bool) :
|
||||
TacticM Unit := do
|
||||
let mvar ← getMainGoal
|
||||
let initialState ← saveState
|
||||
let (_, goal) ← (← getMainGoal).intros
|
||||
goal.withContext do
|
||||
let required := (← (required.getD #[]).mapM getFVarId).toList.map .fvar
|
||||
let tactic := fun exfalso =>
|
||||
solveByElim required (exfalso := exfalso) (maxDepth := 6)
|
||||
solveByElim required (exfalso := exfalso) (maxDepth := 6)
|
||||
let allowFailure := fun g => do
|
||||
let g ← g.withContext (instantiateMVars (.mvar g))
|
||||
return required.all fun e => e.occurs g
|
||||
match ← librarySearch goal tactic allowFailure with
|
||||
match (← librarySearch goal tactic allowFailure) with
|
||||
-- Found goal that closed problem
|
||||
| none =>
|
||||
addExactSuggestion ref (← instantiateMVars (mkMVar mvar)).headBeta
|
||||
addSuggestionIfValid ref mvar initialState
|
||||
-- Found suggestions
|
||||
| some suggestions =>
|
||||
if requireClose then throwError
|
||||
"`exact?` could not close the goal. Try `apply?` to see partial suggestions."
|
||||
if requireClose then
|
||||
let hint := if suggestions.isEmpty then "" else " Try `apply?` to see partial suggestions."
|
||||
throwError "`exact?` could not close the goal.{hint}"
|
||||
reportOutOfHeartbeats `apply? ref
|
||||
for (_, suggestionMCtx) in suggestions do
|
||||
withMCtx suggestionMCtx do
|
||||
addExactSuggestion ref (← instantiateMVars (mkMVar mvar)).headBeta (addSubgoalsMsg := true)
|
||||
addSuggestionIfValid ref mvar initialState (addSubgoalsMsg := true) (errorOnInvalid := false)
|
||||
if suggestions.isEmpty then logError "apply? didn't find any relevant lemmas"
|
||||
admitGoal goal
|
||||
where
|
||||
/--
|
||||
Executes `tac` in `savedState` (then restores the current state). Used to ensure that a suggested
|
||||
tactic is valid.
|
||||
|
||||
Remark: we don't merely elaborate the proof term's syntax because it may successfully round-trip
|
||||
(d)elaboration but still produce an invalid tactic (see the example in #5407).
|
||||
-/
|
||||
evalTacticWithState (savedState : Tactic.SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
|
||||
let currState ← saveState
|
||||
savedState.restore
|
||||
try
|
||||
Term.withoutErrToSorry <| withoutRecover <| evalTactic tac
|
||||
finally
|
||||
currState.restore
|
||||
|
||||
/--
|
||||
Suggests using the value of `goal` as a proof term if the corresponding tactic is valid at
|
||||
`origGoal`, or else informs the user that a proof exists but is not syntactically valid.
|
||||
-/
|
||||
addSuggestionIfValid (ref : Syntax) (goal : MVarId) (initialState : Tactic.SavedState)
|
||||
(addSubgoalsMsg := false) (errorOnInvalid := true) : TacticM Unit := do
|
||||
let proofExpr := (← instantiateMVars (mkMVar goal)).headBeta
|
||||
let proofMVars ← getMVars proofExpr
|
||||
let hasMVars := !proofMVars.isEmpty
|
||||
let suggestion ← mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := false)
|
||||
let mut exposeNames := false
|
||||
try evalTacticWithState initialState suggestion
|
||||
catch _ =>
|
||||
exposeNames := true
|
||||
let suggestion' ← mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := true)
|
||||
try evalTacticWithState initialState suggestion'
|
||||
catch _ =>
|
||||
let suggestionStr ← SuggestionText.prettyExtra suggestion
|
||||
-- Pretty-print the version without `expose_names` so variable names match the Infoview
|
||||
let msg := m!"found a {if hasMVars then "partial " else ""}proof, \
|
||||
but the corresponding tactic failed:{indentD suggestionStr}"
|
||||
if errorOnInvalid then throwError msg else logInfo msg
|
||||
return
|
||||
addExactSuggestion ref proofExpr (addSubgoalsMsg := addSubgoalsMsg) (exposeNames := exposeNames)
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.exact?]
|
||||
def evalExact : Tactic := fun stx => do
|
||||
@@ -69,7 +111,7 @@ def elabExact?Term : TermElab := fun stx expectedType? => do
|
||||
introdGoal.withContext do
|
||||
if let some suggestions ← librarySearch introdGoal then
|
||||
if suggestions.isEmpty then logError "`exact?%` didn't find any relevant lemmas"
|
||||
else logError "`exact?%` could not close the goal. Try `by apply` to see partial suggestions."
|
||||
else logError "`exact?%` could not close the goal. Try `by apply?` to see partial suggestions."
|
||||
mkLabeledSorry expectedType (synthetic := true) (unique := true)
|
||||
else
|
||||
addTermSuggestion stx (← instantiateMVars goal).headBeta
|
||||
|
||||
@@ -48,14 +48,6 @@ private def isExprAccessible (e : Expr) : MetaM Bool := do
|
||||
let (_, s) ← e.collectFVars |>.run {}
|
||||
s.fvarIds.allM isAccessible
|
||||
|
||||
/-- Creates a temporary local context where all names are exposed, and executes `k`-/
|
||||
private def withExposedNames (k : MetaM α) : MetaM α := do
|
||||
withNewMCtxDepth do
|
||||
-- Create a helper goal to apply
|
||||
let mvarId := (← mkFreshExprMVar (mkConst ``True)).mvarId!
|
||||
let mvarId ← mvarId.exposeNames
|
||||
mvarId.withContext do k
|
||||
|
||||
/-- Executes `tac` in the saved state. This function is used to validate a tactic before suggesting it. -/
|
||||
def checkTactic (savedState : SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
|
||||
let currState ← saveState
|
||||
|
||||
@@ -18,8 +18,10 @@ import Lean.Util.Path
|
||||
import Lean.Util.FindExpr
|
||||
import Lean.Util.Profile
|
||||
import Lean.Util.InstantiateLevelParams
|
||||
import Lean.Util.FoldConsts
|
||||
import Lean.PrivateName
|
||||
import Lean.LoadDynlib
|
||||
import Init.Dynamic
|
||||
|
||||
/-!
|
||||
# Note [Environment Branches]
|
||||
@@ -65,6 +67,12 @@ paths back together.
|
||||
-/
|
||||
|
||||
namespace Lean
|
||||
register_builtin_option debug.skipKernelTC : Bool := {
|
||||
defValue := false
|
||||
group := "debug"
|
||||
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
|
||||
}
|
||||
|
||||
/-- Opaque environment extension state. -/
|
||||
opaque EnvExtensionStateSpec : (α : Type) × Inhabited α := ⟨Unit, ⟨()⟩⟩
|
||||
def EnvExtensionState : Type := EnvExtensionStateSpec.fst
|
||||
@@ -252,6 +260,28 @@ inductive Exception where
|
||||
| excessiveMemory
|
||||
| deepRecursion
|
||||
| interrupted
|
||||
deriving Nonempty
|
||||
|
||||
/-- Basic `Exception` formatting without `MessageData` dependency. -/
|
||||
private def Exception.toRawString : Kernel.Exception → String
|
||||
| unknownConstant _ constName => s!"(kernel) unknown constant '{constName}'"
|
||||
| alreadyDeclared _ constName => s!"(kernel) constant has already been declared '{constName}'"
|
||||
| declTypeMismatch _ _ _ => s!"(kernel) declaration type mismatch"
|
||||
| declHasMVars _ constName _ => s!"(kernel) declaration has metavariables '{constName}'"
|
||||
| declHasFVars _ constName _ => s!"(kernel) declaration has free variables '{constName}'"
|
||||
| funExpected _ _ e => s!"(kernel) function expected: {e}"
|
||||
| typeExpected _ _ e => s!"(kernel) type expected: {e}"
|
||||
| letTypeMismatch _ _ n _ _ => s!"(kernel) let-declaration type mismatch '{n}'"
|
||||
| exprTypeMismatch _ _ e _ => s!"(kernel) type mismatch at {e}"
|
||||
| appTypeMismatch _ _ e fnType argType =>
|
||||
s!"application type mismatch: {e}\nargument has type {argType}\nbut function has type {fnType}"
|
||||
| invalidProj _ _ e => s!"(kernel) invalid projection {e}"
|
||||
| thmTypeIsNotProp _ constName type => s!"(kernel) type of theorem '{constName}' is not a proposition: {type}"
|
||||
| other msg => s!"(kernel) {msg}"
|
||||
| deterministicTimeout => "(kernel) deterministic timeout"
|
||||
| excessiveMemory => "(kernel) excessive memory consumption detected"
|
||||
| deepRecursion => "(kernel) deep recursion detected"
|
||||
| interrupted => "(kernel) interrupted"
|
||||
|
||||
namespace Environment
|
||||
|
||||
@@ -346,6 +376,7 @@ structure AsyncConstantInfo where
|
||||
sig : Task ConstantVal
|
||||
/-- The final, complete constant info, potentially filled asynchronously. -/
|
||||
constInfo : Task ConstantInfo
|
||||
deriving Inhabited
|
||||
|
||||
namespace AsyncConstantInfo
|
||||
|
||||
@@ -365,21 +396,25 @@ end AsyncConstantInfo
|
||||
|
||||
/--
|
||||
Information about the current branch of the environment representing asynchronous elaboration.
|
||||
|
||||
Use `Environment.enterAsync` instead of `mkRaw`.
|
||||
-/
|
||||
structure AsyncContext where
|
||||
private structure AsyncContext where mkRaw ::
|
||||
/--
|
||||
Name of the declaration asynchronous elaboration was started for. All constants added to this
|
||||
environment branch must have the name as a prefix, after erasing macro scopes and private name
|
||||
prefixes.
|
||||
-/
|
||||
declPrefix : Name
|
||||
/-- Whether we are in `realizeConst`, used to restrict env ext modifications. -/
|
||||
realizing : Bool
|
||||
deriving Nonempty
|
||||
|
||||
/--
|
||||
Checks whether a declaration named `n` may be added to the environment in the given context. See
|
||||
also `AsyncContext.declPrefix`.
|
||||
-/
|
||||
def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
|
||||
private def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
|
||||
ctx.declPrefix.isPrefixOf <| privateToUserName n.eraseMacroScopes
|
||||
|
||||
/--
|
||||
@@ -394,28 +429,50 @@ structure AsyncConst where
|
||||
exts? : Option (Task (Array EnvExtensionState))
|
||||
|
||||
/-- Data structure holding a sequence of `AsyncConst`s optimized for efficient access. -/
|
||||
structure AsyncConsts where
|
||||
toArray : Array AsyncConst := #[]
|
||||
private structure AsyncConsts where
|
||||
size : Nat
|
||||
revList : List AsyncConst
|
||||
/-- Map from declaration name to const for fast direct access. -/
|
||||
private map : NameMap AsyncConst := {}
|
||||
map : NameMap AsyncConst
|
||||
/-- Trie of declaration names without private name prefixes for fast longest-prefix access. -/
|
||||
private normalizedTrie : NameTrie AsyncConst := {}
|
||||
normalizedTrie : NameTrie AsyncConst
|
||||
deriving Inhabited
|
||||
|
||||
def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
|
||||
{ aconsts with
|
||||
toArray := aconsts.toArray.push aconst
|
||||
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
|
||||
let normalizedName := privateToUserName aconst.constInfo.name
|
||||
if let some aconst' := aconsts.normalizedTrie.find? normalizedName then
|
||||
panic! s!"AsyncConsts.add: duplicate normalized declaration name {aconst.constInfo.name} vs. {aconst'.constInfo.name}"
|
||||
else { aconsts with
|
||||
size := aconsts.size + 1
|
||||
revList := aconst :: aconsts.revList
|
||||
map := aconsts.map.insert aconst.constInfo.name aconst
|
||||
normalizedTrie := aconsts.normalizedTrie.insert (privateToUserName aconst.constInfo.name) aconst
|
||||
normalizedTrie := aconsts.normalizedTrie.insert normalizedName aconst
|
||||
}
|
||||
|
||||
def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
private def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
aconsts.map.find? declName
|
||||
|
||||
/-- Finds the constant in the collection that is a prefix of `declName`, if any. -/
|
||||
def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
-- as macro scopes are a strict suffix,
|
||||
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName.eraseMacroScopes)
|
||||
private def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
|
||||
-- as macro scopes are a strict suffix, we do not have to remove them before calling
|
||||
-- `findLongestPrefix?`
|
||||
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName)
|
||||
|
||||
/-- Context for `realizeConst` established by `enableRealizationsForConst`. -/
|
||||
private structure RealizationContext where
|
||||
/--
|
||||
Saved `Environment`, untyped to avoid cyclic reference. Import environment for imported constants.
|
||||
-/
|
||||
env : NonScalar
|
||||
/-- Saved options. Empty for imported constants. -/
|
||||
opts : Options
|
||||
/--
|
||||
`realizeConst _ c ..` adds a mapping from `c` to a task of the realization results: the newly
|
||||
added constants (incl. extension data in `AsyncConst.exts?`), a function for replaying the
|
||||
changes onto a derived kernel environment, and auxiliary data (always `SnapshotTree` in builtin
|
||||
uses, but untyped to avoid cyclic module references).
|
||||
-/
|
||||
constsRef : IO.Ref (NameMap (Task (List AsyncConst × (Kernel.Environment → Kernel.Environment) × Dynamic)))
|
||||
|
||||
/--
|
||||
Elaboration-specific extension of `Kernel.Environment` that adds tracking of asynchronously
|
||||
@@ -443,19 +500,32 @@ structure Environment where
|
||||
-/
|
||||
checked : Task Kernel.Environment := .pure checkedWithoutAsync
|
||||
/--
|
||||
Container of asynchronously elaborated declarations, i.e.
|
||||
`checked = checkedWithoutAsync ⨃ asyncConsts`.
|
||||
Container of asynchronously elaborated declarations. For consistency, `updateBaseAfterKernelAdd`
|
||||
makes sure this contains constants added even synchronously, i.e. this is a superset of
|
||||
`checkedWithoutAsync` except for imported constants.
|
||||
-/
|
||||
private asyncConsts : AsyncConsts := {}
|
||||
private asyncConsts : AsyncConsts := default
|
||||
/-- Information about this asynchronous branch of the environment, if any. -/
|
||||
private asyncCtx? : Option AsyncContext := none
|
||||
/--
|
||||
Realized constants belonging to imported declarations. Must be initialized by calling
|
||||
`enableRealizationsForImports`.
|
||||
-/
|
||||
private realizedImportedConsts? : Option RealizationContext
|
||||
/--
|
||||
Realized constants belonging to local declarations. This is a map from local declarations, which
|
||||
need to be registered synchronously using `enableRealizationsForConst`, to their realization
|
||||
context incl. a ref of realized constants.
|
||||
-/
|
||||
private realizedLocalConsts : NameMap RealizationContext := {}
|
||||
deriving Nonempty
|
||||
|
||||
namespace Environment
|
||||
|
||||
-- used only when the kernel calls into the interpreter, and in `Lean.Kernel.Exception.mkCtx`
|
||||
@[export lean_elab_environment_of_kernel_env]
|
||||
def ofKernelEnv (env : Kernel.Environment) : Environment :=
|
||||
{ checkedWithoutAsync := env }
|
||||
{ checkedWithoutAsync := env, realizedImportedConsts? := none }
|
||||
|
||||
@[export lean_elab_environment_to_kernel_env]
|
||||
def toKernelEnv (env : Environment) : Kernel.Environment :=
|
||||
@@ -469,6 +539,10 @@ private def modifyCheckedAsync (env : Environment) (f : Kernel.Environment → K
|
||||
private def setCheckedSync (env : Environment) (newChecked : Kernel.Environment) : Environment :=
|
||||
{ env with checked := .pure newChecked, checkedWithoutAsync := newChecked }
|
||||
|
||||
/-- True while inside `realizeConst`'s `realize`. -/
|
||||
def isRealizing (env : Environment) : Bool :=
|
||||
env.asyncCtx?.any (·.realizing)
|
||||
|
||||
/--
|
||||
Checks whether the given declaration name may potentially added, or have been added, to the current
|
||||
environment branch, which is the case either if this is the main branch or if the declaration name
|
||||
@@ -574,6 +648,45 @@ def findConstVal? (env : Environment) (n : Name) : Option ConstantVal := do
|
||||
return asyncConst.constInfo.toConstantVal
|
||||
else env.findNoAsync n |>.map (·.toConstantVal)
|
||||
|
||||
/--
|
||||
Allows `realizeConst` calls for imported declarations in all derived environment branches.
|
||||
Realizations will run using the given environment and options to ensure deterministic results.
|
||||
This function should be called directly after `setMainModule` to ensure that all realized constants
|
||||
use consistent private prefixes.
|
||||
-/
|
||||
def enableRealizationsForImports (env : Environment) (opts : Options) : BaseIO Environment :=
|
||||
return { env with realizedImportedConsts? := some {
|
||||
-- safety: `RealizationContext` is private
|
||||
env := unsafe unsafeCast env
|
||||
opts
|
||||
constsRef := (← IO.mkRef {})
|
||||
}
|
||||
}
|
||||
|
||||
/--
|
||||
Allows `realizeConst` calls for the given declaration in all derived environment branches.
|
||||
Realizations will run using the given environment and options to ensure deterministic results. Note
|
||||
that while we check that the function isn't called too *early*, i.e. before the declaration is
|
||||
actually added to the environment, we cannot automatically check that it isn't called too *late*,
|
||||
i.e. before all environment extensions that may be relevant to realizations have been set. We do
|
||||
check that we are not calling it from a different branch than `c` was added on, which would be
|
||||
definitely too late.
|
||||
-/
|
||||
def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
|
||||
BaseIO Environment := do
|
||||
if env.findAsync? c |>.isNone then
|
||||
panic! s!"Environment.enableRealizationsForConst: declaration {c} not found in environment"
|
||||
if let some asyncCtx := env.asyncCtx? then
|
||||
if !asyncCtx.mayContain c then
|
||||
panic! s!"Environment.enableRealizationsForConst: {c} is outside current context {asyncCtx.declPrefix}"
|
||||
if env.realizedLocalConsts.contains c then
|
||||
return env
|
||||
return { env with realizedLocalConsts := env.realizedLocalConsts.insert c {
|
||||
-- safety: `RealizationContext` is private
|
||||
env := unsafe unsafeCast env
|
||||
opts
|
||||
constsRef := (← IO.mkRef {}) } }
|
||||
|
||||
/--
|
||||
Looks up the given declaration name in the environment, blocking on the corresponding elaboration
|
||||
task if not yet complete.
|
||||
@@ -590,9 +703,14 @@ def find? (env : Environment) (n : Name) : Option ConstantInfo :=
|
||||
def dbgFormatAsyncState (env : Environment) : BaseIO String :=
|
||||
return s!"\
|
||||
asyncCtx.declPrefix: {repr <| env.asyncCtx?.map (·.declPrefix)}\
|
||||
\nasyncConsts: {repr <| env.asyncConsts.toArray.map (·.constInfo.name)}\
|
||||
\ncheckedWithoutAsync.constants.map₂: {repr <|
|
||||
env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
|
||||
\nasyncConsts: {repr <| env.asyncConsts.revList.reverse.map (·.constInfo.name)}\
|
||||
\nrealizedLocalConsts: {repr (← env.realizedLocalConsts.toList.mapM fun (n, ctx) => do
|
||||
let consts := (← ctx.constsRef.get).toList
|
||||
return (n, consts.map (·.1)))}
|
||||
\nrealizedImportedConsts?: {repr <| (← env.realizedImportedConsts?.mapM fun ctx => do
|
||||
return (← ctx.constsRef.get).toList.map fun (n, m?) =>
|
||||
(n, m?.get.1.map (fun c : AsyncConst => c.constInfo.name.toString) |> toString))}
|
||||
\ncheckedWithoutAsync.constants.map₂: {repr <| env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
|
||||
|
||||
/-- Returns debug output about the synchronous state of the environment. -/
|
||||
def dbgFormatCheckedSyncState (env : Environment) : BaseIO String :=
|
||||
@@ -614,6 +732,13 @@ structure PromiseCheckedResult where
|
||||
asyncEnv : Environment
|
||||
private checkedEnvPromise : IO.Promise Kernel.Environment
|
||||
|
||||
/-- Creates an async context for the given declaration name, normalizing it for use as a prefix. -/
|
||||
private def enterAsync (declName : Name) (realizing := false) (env : Environment) : Environment :=
|
||||
{ env with asyncCtx? := some {
|
||||
declPrefix := privateToUserName declName.eraseMacroScopes
|
||||
-- `realizing` is sticky
|
||||
realizing := realizing || env.asyncCtx?.any (·.realizing) } }
|
||||
|
||||
/--
|
||||
Starts an asynchronous modification of the kernel environment. The environment is split into a
|
||||
"main" branch that will block on access to the kernel environment until
|
||||
@@ -626,10 +751,8 @@ def promiseChecked (env : Environment) : BaseIO PromiseCheckedResult := do
|
||||
checked := checkedEnvPromise.result?.bind (sync := true) fun
|
||||
| some kenv => .pure kenv
|
||||
| none => env.checked }
|
||||
asyncEnv := { env with
|
||||
-- Do not allow adding new constants
|
||||
asyncCtx? := some { declPrefix := `__reserved__Environment_promiseChecked }
|
||||
}
|
||||
-- Do not allow adding new constants
|
||||
asyncEnv := env.enterAsync `__reserved__Environment_promiseChecked
|
||||
checkedEnvPromise
|
||||
}
|
||||
|
||||
@@ -664,28 +787,14 @@ structure AddConstAsyncResult where
|
||||
private extensionsPromise : IO.Promise (Array EnvExtensionState)
|
||||
private checkedEnvPromise : IO.Promise Kernel.Environment
|
||||
|
||||
/--
|
||||
Starts the asynchronous addition of a constant to the environment. The environment is split into a
|
||||
"main" branch that holds a reference to the constant to be added but will block on access until the
|
||||
corresponding information has been added on the "async" environment branch and committed there; see
|
||||
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
|
||||
information.
|
||||
-/
|
||||
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
|
||||
IO AddConstAsyncResult := do
|
||||
assert! env.asyncMayContain constName
|
||||
let sigPromise ← IO.Promise.new
|
||||
let infoPromise ← IO.Promise.new
|
||||
let extensionsPromise ← IO.Promise.new
|
||||
let checkedEnvPromise ← IO.Promise.new
|
||||
|
||||
-- fallback info in case promises are dropped unfulfilled
|
||||
let fallbackVal := {
|
||||
/-- Creates fallback info to be used in case promises are dropped unfulfilled. -/
|
||||
private def mkFallbackConstInfo (constName : Name) (kind : ConstantKind) : ConstantInfo :=
|
||||
let fallbackVal : ConstantVal := {
|
||||
name := constName
|
||||
levelParams := []
|
||||
type := mkApp2 (mkConst ``sorryAx [0]) (mkSort 0) (mkConst ``true)
|
||||
type := mkApp2 (mkConst ``sorryAx [1]) (mkSort 0) (mkConst ``true)
|
||||
}
|
||||
let fallbackInfo := match kind with
|
||||
match kind with
|
||||
| .defn => .defnInfo { fallbackVal with
|
||||
value := mkApp2 (mkConst ``sorryAx [0]) fallbackVal.type (mkConst ``true)
|
||||
hints := .abbrev
|
||||
@@ -697,16 +806,38 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
|
||||
| .axiom => .axiomInfo { fallbackVal with
|
||||
isUnsafe := false
|
||||
}
|
||||
| k => panic! s!"AddConstAsyncResult.addConstAsync: unsupported constant kind {repr k}"
|
||||
| k => panic! s!"Environment.mkFallbackConstInfo: unsupported constant kind {repr k}"
|
||||
|
||||
/--
|
||||
Starts the asynchronous addition of a constant to the environment. The environment is split into a
|
||||
"main" branch that holds a reference to the constant to be added but will block on access until the
|
||||
corresponding information has been added on the "async" environment branch and committed there; see
|
||||
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
|
||||
information.
|
||||
-/
|
||||
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true)
|
||||
(checkMayContain := true) :
|
||||
IO AddConstAsyncResult := do
|
||||
if checkMayContain then
|
||||
if let some ctx := env.asyncCtx? then
|
||||
if !ctx.mayContain constName then
|
||||
throw <| .userError s!"cannot add declaration {constName} to environment as it is \
|
||||
restricted to the prefix {ctx.declPrefix}"
|
||||
let sigPromise ← IO.Promise.new
|
||||
let infoPromise ← IO.Promise.new
|
||||
let extensionsPromise ← IO.Promise.new
|
||||
let checkedEnvPromise ← IO.Promise.new
|
||||
|
||||
let fallbackConstInfo := mkFallbackConstInfo constName kind
|
||||
|
||||
let asyncConst := {
|
||||
constInfo := {
|
||||
name := constName
|
||||
kind
|
||||
sig := sigPromise.resultD fallbackVal
|
||||
constInfo := infoPromise.resultD fallbackInfo
|
||||
sig := sigPromise.resultD fallbackConstInfo.toConstantVal
|
||||
constInfo := infoPromise.resultD fallbackConstInfo
|
||||
}
|
||||
exts? := guard reportExts *> some (extensionsPromise.resultD #[])
|
||||
exts? := guard reportExts *> some (extensionsPromise.resultD env.toKernelEnv.extensions)
|
||||
}
|
||||
return {
|
||||
constName, kind
|
||||
@@ -715,9 +846,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
|
||||
checked := checkedEnvPromise.result?.bind (sync := true) fun
|
||||
| some kenv => .pure kenv
|
||||
| none => env.checked }
|
||||
asyncEnv := { env with
|
||||
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
|
||||
}
|
||||
asyncEnv := env.enterAsync constName
|
||||
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
|
||||
}
|
||||
|
||||
@@ -783,7 +912,10 @@ def imports (env : Environment) : Array Import :=
|
||||
def allImportedModuleNames (env : Environment) : Array Name :=
|
||||
env.header.moduleNames
|
||||
|
||||
def setMainModule (env : Environment) (m : Name) : Environment :=
|
||||
def setMainModule (env : Environment) (m : Name) : Environment := Id.run do
|
||||
if env.realizedImportedConsts?.isSome then
|
||||
panic! "Environment.setMainModule: cannot set after `enableRealizationsForImports`"
|
||||
return env
|
||||
env.modifyCheckedAsync ({ · with header.mainModule := m })
|
||||
|
||||
def mainModule (env : Environment) : Name :=
|
||||
@@ -880,6 +1012,9 @@ inductive EnvExtension.AsyncMode where
|
||||
| async
|
||||
deriving Inhabited
|
||||
|
||||
abbrev ReplayFn (σ : Type) :=
|
||||
(oldState : σ) → (newState : σ) → (newConsts : List Name) → σ → σ
|
||||
|
||||
/--
|
||||
Environment extension, can only be generated by `registerEnvExtension` that allocates a unique index
|
||||
for this extension into each environment's extension state's array.
|
||||
@@ -888,6 +1023,13 @@ structure EnvExtension (σ : Type) where private mk ::
|
||||
idx : Nat
|
||||
mkInitial : IO σ
|
||||
asyncMode : EnvExtension.AsyncMode
|
||||
/--
|
||||
Optional function that, given state before and after realization and newly added constants,
|
||||
replays this change onto a state from another (derived) environment. This function is used only
|
||||
when making changes to an extension inside a `realizeConst` call, in which case it must be
|
||||
present.
|
||||
-/
|
||||
replay? : Option (ReplayFn σ)
|
||||
deriving Inhabited
|
||||
|
||||
namespace EnvExtension
|
||||
@@ -949,19 +1091,21 @@ from different environment branches are reconciled.
|
||||
Note that in modes `sync` and `async`, `f` will be called twice, on the local and on the `checked`
|
||||
state.
|
||||
-/
|
||||
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ → σ) : Environment := Id.run do
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
match ext.asyncMode with
|
||||
| .mainOnly =>
|
||||
if let some asyncCtx := env.asyncCtx? then
|
||||
let _ : Inhabited Environment := ⟨env⟩
|
||||
panic! s!"Environment.modifyState: environment extension is marked as `mainOnly` but used in \
|
||||
async context '{asyncCtx.declPrefix}'"
|
||||
else
|
||||
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
{if asyncCtx.realizing then "realization" else "async"} context '{asyncCtx.declPrefix}'"
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| .local =>
|
||||
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
|
||||
| _ =>
|
||||
if ext.replay?.isNone then
|
||||
if let some asyncCtx := env.asyncCtx?.filter (·.realizing) then
|
||||
panic! s!"Environment.modifyState: environment extension must set `replay?` field to be \
|
||||
used in realization context '{asyncCtx.declPrefix}'"
|
||||
env.modifyCheckedAsync fun env =>
|
||||
{ env with extensions := unsafe ext.modifyStateImpl env.extensions f }
|
||||
|
||||
@@ -992,6 +1136,24 @@ recommended and should be considered only for important optimizations.
|
||||
opaque getState {σ : Type} [Inhabited σ] (ext : EnvExtension σ) (env : Environment)
|
||||
(asyncMode := ext.asyncMode) : σ
|
||||
|
||||
-- `unsafe` fails to infer `Nonempty` here
|
||||
private unsafe def findStateAsyncUnsafe {σ : Type} [Inhabited σ]
|
||||
(ext : EnvExtension σ) (env : Environment) (declPrefix : Name) : σ :=
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
|
||||
ext.getStateImpl exts.get
|
||||
else
|
||||
ext.getStateImpl env.checkedWithoutAsync.extensions
|
||||
|
||||
/--
|
||||
Returns the final extension state on the environment branch corresponding to the passed declaration
|
||||
name, if any, or otherwise the state on the current branch. In other words, at most one environment
|
||||
branch will be blocked on.
|
||||
-/
|
||||
@[implemented_by findStateAsyncUnsafe]
|
||||
opaque findStateAsync {σ : Type} [Inhabited σ] (ext : EnvExtension σ)
|
||||
(env : Environment) (declPrefix : Name) : σ
|
||||
|
||||
end EnvExtension
|
||||
|
||||
/-- Environment extensions can only be registered during initialization.
|
||||
@@ -1002,12 +1164,13 @@ end EnvExtension
|
||||
Note that by default, extension state is *not* stored in .olean files and will not propagate across `import`s.
|
||||
For that, you need to register a persistent environment extension. -/
|
||||
def registerEnvExtension {σ : Type} (mkInitial : IO σ)
|
||||
(replay? : Option (ReplayFn σ) := none)
|
||||
(asyncMode : EnvExtension.AsyncMode := .mainOnly) : IO (EnvExtension σ) := do
|
||||
unless (← initializing) do
|
||||
throw (IO.userError "failed to register environment, extensions can only be registered during initialization")
|
||||
let exts ← EnvExtension.envExtensionsRef.get
|
||||
let idx := exts.size
|
||||
let ext : EnvExtension σ := { idx, mkInitial, asyncMode }
|
||||
let ext : EnvExtension σ := { idx, mkInitial, asyncMode, replay? }
|
||||
-- safety: `EnvExtensionState` is opaque, so we can upcast to it
|
||||
EnvExtension.envExtensionsRef.modify fun exts => exts.push (unsafe unsafeCast ext)
|
||||
pure ext
|
||||
@@ -1019,7 +1182,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
|
||||
let initializing ← IO.initializing
|
||||
if initializing then throw (IO.userError "environment objects cannot be created during initialization")
|
||||
let exts ← mkInitialExtensionStates
|
||||
pure {
|
||||
return {
|
||||
checkedWithoutAsync := {
|
||||
const2ModIdx := {}
|
||||
constants := {}
|
||||
@@ -1027,6 +1190,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
|
||||
extraConstNames := {}
|
||||
extensions := exts
|
||||
}
|
||||
realizedImportedConsts? := none
|
||||
}
|
||||
|
||||
structure PersistentEnvExtensionState (α : Type) (σ : Type) where
|
||||
@@ -1117,8 +1281,9 @@ def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
|
||||
{ s with state := state }
|
||||
|
||||
/-- Get the current state of the given extension in the given environment. -/
|
||||
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment) : σ :=
|
||||
(ext.toEnvExtension.getState env).state
|
||||
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment)
|
||||
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
|
||||
(ext.toEnvExtension.getState (asyncMode := asyncMode) env).state
|
||||
|
||||
/-- Set the current state of the given extension in the given environment. -/
|
||||
def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (s : σ) : Environment :=
|
||||
@@ -1128,23 +1293,11 @@ def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
|
||||
def modifyState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
ext.toEnvExtension.modifyState env fun ps => { ps with state := f (ps.state) }
|
||||
|
||||
-- `unsafe` fails to infer `Nonempty` here
|
||||
private unsafe def findStateAsyncUnsafe {α β σ : Type} [Inhabited σ]
|
||||
(ext : PersistentEnvExtension α β σ) (env : Environment) (declPrefix : Name) : σ :=
|
||||
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
|
||||
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
|
||||
ext.toEnvExtension.getStateImpl exts.get |>.state
|
||||
else
|
||||
ext.toEnvExtension.getStateImpl env.checkedWithoutAsync.extensions |>.state
|
||||
@[inherit_doc EnvExtension.findStateAsync]
|
||||
def findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
|
||||
(env : Environment) (declPrefix : Name) : σ :=
|
||||
ext.toEnvExtension.findStateAsync env declPrefix |>.state
|
||||
|
||||
/--
|
||||
Returns the final extension state on the environment branch corresponding to the passed declaration
|
||||
name, if any, or otherwise the state on the current branch. In other words, at most one environment
|
||||
branch will be blocked on.
|
||||
-/
|
||||
@[implemented_by findStateAsyncUnsafe]
|
||||
opaque findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
|
||||
(env : Environment) (declPrefix : Name) : σ
|
||||
|
||||
end PersistentEnvExtension
|
||||
|
||||
@@ -1158,11 +1311,14 @@ structure PersistentEnvExtensionDescr (α β σ : Type) where
|
||||
exportEntriesFn : σ → Array α
|
||||
statsFn : σ → Format := fun _ => Format.nil
|
||||
asyncMode : EnvExtension.AsyncMode := .mainOnly
|
||||
replay? : Option (ReplayFn σ) := none
|
||||
|
||||
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
if pExts.any (fun ext => ext.name == descr.name) then throw (IO.userError s!"invalid environment extension, '{descr.name}' has already been used")
|
||||
let ext ← registerEnvExtension (asyncMode := descr.asyncMode) do
|
||||
let replay? := descr.replay?.map fun replay =>
|
||||
fun oldState newState newConsts s => { s with state := replay oldState.state newState.state newConsts s.state }
|
||||
let ext ← registerEnvExtension (asyncMode := descr.asyncMode) (replay? := replay?) do
|
||||
let initial ← descr.mkInitial
|
||||
let s : PersistentEnvExtensionState α σ := {
|
||||
importedEntries := #[],
|
||||
@@ -1206,6 +1362,9 @@ def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr :
|
||||
exportEntriesFn := fun s => descr.toArrayFn s.1.reverse,
|
||||
statsFn := fun s => format "number of local entries: " ++ format s.1.length
|
||||
asyncMode := descr.asyncMode
|
||||
replay? := some fun oldState newState _ (entries, s) =>
|
||||
let newEntries := newState.1.drop oldState.1.length
|
||||
(newEntries ++ entries, newEntries.foldl descr.addEntryFn s)
|
||||
}
|
||||
|
||||
namespace SimplePersistentEnvExtension
|
||||
@@ -1219,8 +1378,9 @@ def getEntries {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension
|
||||
(PersistentEnvExtension.getState ext env).1
|
||||
|
||||
/-- Get the current state of the given `SimplePersistentEnvExtension`. -/
|
||||
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment) : σ :=
|
||||
(PersistentEnvExtension.getState ext env).2
|
||||
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment)
|
||||
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
|
||||
(PersistentEnvExtension.getState (asyncMode := asyncMode) ext env).2
|
||||
|
||||
/-- Set the current state of the given `SimplePersistentEnvExtension`. This change is *not* persisted across files. -/
|
||||
def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (s : σ) : Environment :=
|
||||
@@ -1230,6 +1390,11 @@ def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : En
|
||||
def modifyState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (f : σ → σ) : Environment :=
|
||||
PersistentEnvExtension.modifyState ext env (fun ⟨entries, s⟩ => (entries, f s))
|
||||
|
||||
@[inherit_doc PersistentEnvExtension.findStateAsync]
|
||||
def findStateAsync {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ)
|
||||
(env : Environment) (declPrefix : Name) : σ :=
|
||||
PersistentEnvExtension.findStateAsync ext env declPrefix |>.2
|
||||
|
||||
end SimplePersistentEnvExtension
|
||||
|
||||
/-- Environment extension for tagging declarations.
|
||||
@@ -1329,8 +1494,12 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
|
||||
|
||||
def mkModuleData (env : Environment) : IO ModuleData := do
|
||||
let pExts ← persistentEnvExtensionsRef.get
|
||||
let entries := pExts.map fun pExt =>
|
||||
let state := pExt.getState env
|
||||
let entries := pExts.map fun pExt => Id.run do
|
||||
-- get state from `checked` at the end if `async`; it would otherwise panic
|
||||
let mut asyncMode := pExt.toEnvExtension.asyncMode
|
||||
if asyncMode matches .async then
|
||||
asyncMode := .sync
|
||||
let state := pExt.getState (asyncMode := asyncMode) env
|
||||
(pExt.name, pExt.exportEntriesFn state)
|
||||
let kenv := env.toKernelEnv
|
||||
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
|
||||
@@ -1403,7 +1572,9 @@ where
|
||||
let pExtDescrs ← persistentEnvExtensionsRef.get
|
||||
if h : i < pExtDescrs.size then
|
||||
let extDescr := pExtDescrs[i]
|
||||
let s := extDescr.toEnvExtension.getState env
|
||||
-- `local` as `async` does not allow for `getState` but it's all safe here as there is only
|
||||
-- one branch so far.
|
||||
let s := extDescr.toEnvExtension.getState (asyncMode := .local) env
|
||||
let prevSize := (← persistentEnvExtensionsRef.get).size
|
||||
let prevAttrSize ← getNumBuiltinAttributes
|
||||
let newState ← extDescr.addImportedFn s.importedEntries { env := env, opts := opts }
|
||||
@@ -1522,6 +1693,7 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
|
||||
moduleData := s.moduleData
|
||||
}
|
||||
}
|
||||
realizedImportedConsts? := none
|
||||
}
|
||||
env ← setImportedEntries env s.moduleData
|
||||
if leakEnv then
|
||||
@@ -1583,6 +1755,9 @@ builtin_initialize namespacesExt : SimplePersistentEnvExtension Name NameSSet
|
||||
let map := mkStateFromImportedEntries (fun map name => map.insert name ()) map as
|
||||
SMap.fromHashMap map |>.switch
|
||||
addEntryFn := fun s n => s.insert n
|
||||
-- Namespaces from local helper constants can be disregarded in other environment branches. We
|
||||
-- do *not* want `getNamespaceSet` to have to wait on all prior branches.
|
||||
asyncMode := .local
|
||||
}
|
||||
|
||||
@[inherit_doc Kernel.Environment.enableDiag]
|
||||
@@ -1616,8 +1791,18 @@ def getNamespaceSet (env : Environment) : NameSSet :=
|
||||
namespacesExt.getState env
|
||||
|
||||
@[export lean_elab_environment_update_base_after_kernel_add]
|
||||
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) : Environment :=
|
||||
{ env with checked := .pure kernel, checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions } }
|
||||
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) (decl : Declaration) : Environment :=
|
||||
{ env with
|
||||
checked := .pure kernel
|
||||
checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions }
|
||||
-- make constants available in `asyncConsts` as well; see its docstring
|
||||
asyncConsts := decl.getNames.foldl (init := env.asyncConsts) fun asyncConsts n =>
|
||||
if asyncConsts.find? n |>.isNone then
|
||||
asyncConsts.add {
|
||||
constInfo := .ofConstantInfo (kernel.find? n |>.get!)
|
||||
exts? := none
|
||||
}
|
||||
else asyncConsts }
|
||||
|
||||
@[export lean_display_stats]
|
||||
def displayStats (env : Environment) : IO Unit := do
|
||||
@@ -1666,6 +1851,107 @@ def hasUnsafe (env : Environment) (e : Expr) : Bool :=
|
||||
| _ => false;
|
||||
c?.isSome
|
||||
|
||||
/-- Plumbing function for `Lean.Meta.realizeConst`; see documentation there. -/
|
||||
def realizeConst (env : Environment) (forConst : Name) (constName : Name)
|
||||
(realize : Environment → Options → BaseIO (Environment × Dynamic)) :
|
||||
IO (Environment × Dynamic) := do
|
||||
let mut env := env
|
||||
-- find `RealizationContext` for `forConst` in `realizedImportedConsts?` or `realizedLocalConsts`
|
||||
let ctx ← if env.checkedWithoutAsync.const2ModIdx.contains forConst then
|
||||
env.realizedImportedConsts?.getDM <|
|
||||
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
|
||||
else
|
||||
match env.realizedLocalConsts.find? forConst with
|
||||
| some ctx => pure ctx
|
||||
| none =>
|
||||
throw <| .userError s!"trying to realize {constName} but `enableRealizationsForConst` must be called for '{forConst}' first"
|
||||
let prom ← IO.Promise.new
|
||||
-- ensure `prom` is not left unresolved from stray exceptions
|
||||
BaseIO.toIO do
|
||||
-- atomically check whether we are the first branch to realize `constName`
|
||||
let existingConsts? ← ctx.constsRef.modifyGet fun m => match m.find? constName with
|
||||
| some prom' => (some prom', m)
|
||||
| none => (none, m.insert constName prom.result!)
|
||||
let (consts, replay, dyn) ← if let some existingConsts := existingConsts? then
|
||||
pure existingConsts.get
|
||||
else
|
||||
-- safety: `RealizationContext` is private
|
||||
let realizeEnv : Environment := unsafe unsafeCast ctx.env
|
||||
let realizeEnv := { realizeEnv with
|
||||
-- allow realizations to recursively realize other constants for `forConst`. Do note that
|
||||
-- this allows for recursive realization of `constName` itself, which will deadlock.
|
||||
realizedLocalConsts := realizeEnv.realizedLocalConsts.insert forConst ctx
|
||||
realizedImportedConsts? := env.realizedImportedConsts?
|
||||
}
|
||||
-- ensure realized constants are nested below `forConst` and that environment extension
|
||||
-- modifications know they are in an async context
|
||||
let realizeEnv := realizeEnv.enterAsync (realizing := true) forConst
|
||||
-- skip kernel in `realize`, we'll re-typecheck anyway
|
||||
let realizeOpts := debug.skipKernelTC.set ctx.opts true
|
||||
let (realizeEnv', dyn) ← realize realizeEnv realizeOpts
|
||||
-- We could check that `c` was indeed added here but in practice `realize` has already
|
||||
-- reported an error so we don't.
|
||||
|
||||
-- find new constants incl. nested realizations, add current extension state, and compute
|
||||
-- closure
|
||||
let numNewConsts := realizeEnv'.asyncConsts.size - realizeEnv.asyncConsts.size
|
||||
let consts := realizeEnv'.asyncConsts.revList.take numNewConsts |>.reverse
|
||||
let consts := consts.map fun c =>
|
||||
if c.exts?.isNone then
|
||||
{ c with exts? := some <| .pure realizeEnv'.checkedWithoutAsync.extensions }
|
||||
else c
|
||||
let exts ← EnvExtension.envExtensionsRef.get
|
||||
let replay := (maybeAddToKernelEnv realizeEnv realizeEnv' consts · exts)
|
||||
prom.resolve (consts, replay, dyn)
|
||||
pure (consts, replay, dyn)
|
||||
return ({ env with
|
||||
asyncConsts := consts.foldl (init := env.asyncConsts) fun consts c =>
|
||||
if consts.find? c.constInfo.name |>.isSome then
|
||||
consts
|
||||
else
|
||||
consts.add c
|
||||
checked := env.checked.map replay
|
||||
}, dyn)
|
||||
where
|
||||
-- Adds `consts` if they haven't already been added by a previous branch. Note that this
|
||||
-- conditional is deterministic because of the linearizing effect of `env.checked`.
|
||||
maybeAddToKernelEnv (oldEnv newEnv : Environment) (consts : List AsyncConst)
|
||||
(kenv : Kernel.Environment)
|
||||
(exts : Array (EnvExtension EnvExtensionState)) : Kernel.Environment := Id.run do
|
||||
let mut kenv := kenv
|
||||
for c in consts do
|
||||
if kenv.find? c.constInfo.name |>.isSome then
|
||||
continue
|
||||
let info := c.constInfo.toConstantInfo
|
||||
if info.isUnsafe then
|
||||
-- Checking unsafe declarations is not necessary for consistency, and it is necessary to
|
||||
-- avoid checking them in the case of the old code generator, which adds ill-typed constants
|
||||
-- to the kernel environment. We can delete this branch after removing the old code
|
||||
-- generator.
|
||||
kenv := kenv.add info
|
||||
continue
|
||||
let decl := match info with
|
||||
| .thmInfo thm => .thmDecl thm
|
||||
| .defnInfo defn => .defnDecl defn
|
||||
| _ => panic! s!"Environment.realizeConst: {c.constInfo.name} must be definition/theorem"
|
||||
-- realized kernel additions cannot be interrupted - which would be bad anyway as they can be
|
||||
-- reused between snapshots
|
||||
match kenv.addDeclCore 0 decl none with
|
||||
| .ok kenv' => kenv := kenv'
|
||||
| .error e =>
|
||||
let _ : Inhabited Kernel.Environment := ⟨kenv⟩
|
||||
panic! s!"Environment.realizeConst: failed to add {c.constInfo.name} to environment\n{e.toRawString}"
|
||||
for ext in exts do
|
||||
if let some replay := ext.replay? then
|
||||
kenv := { kenv with
|
||||
-- safety: like in `modifyState`, but that one takes an elab env instead of a kernel env
|
||||
extensions := unsafe (ext.modifyStateImpl kenv.extensions <|
|
||||
replay
|
||||
(ext.getStateImpl oldEnv.toKernelEnv.extensions)
|
||||
(ext.getStateImpl newEnv.toKernelEnv.extensions)
|
||||
(consts.map (·.constInfo.name))) }
|
||||
return kenv
|
||||
|
||||
end Environment
|
||||
|
||||
namespace Kernel
|
||||
@@ -1721,4 +2007,13 @@ def mkDefinitionValInferrringUnsafe [Monad m] [MonadEnv m] (name : Name) (levelP
|
||||
let safety := if env.hasUnsafe type || env.hasUnsafe value then DefinitionSafety.unsafe else DefinitionSafety.safe
|
||||
return { name, levelParams, type, value, hints, safety }
|
||||
|
||||
def getMaxHeight (env : Environment) (e : Expr) : UInt32 :=
|
||||
e.foldConsts 0 fun constName max =>
|
||||
match env.find? constName with
|
||||
| ConstantInfo.defnInfo val =>
|
||||
match val.hints with
|
||||
| ReducibilityHints.regular h => if h > max then h else max
|
||||
| _ => max
|
||||
| _ => max
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -1272,11 +1272,23 @@ This operation traverses the expression tree.
|
||||
@[extern "lean_expr_has_loose_bvar"]
|
||||
opaque hasLooseBVar (e : @& Expr) (bvarIdx : @& Nat) : Bool
|
||||
|
||||
/-- Return true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter, or in the range if `tryRange == true`. -/
|
||||
def hasLooseBVarInExplicitDomain : Expr → Nat → Bool → Bool
|
||||
| Expr.forallE _ d b bi, bvarIdx, tryRange =>
|
||||
(bi.isExplicit && hasLooseBVar d bvarIdx) || hasLooseBVarInExplicitDomain b (bvarIdx+1) tryRange
|
||||
| e, bvarIdx, tryRange => tryRange && hasLooseBVar e bvarIdx
|
||||
/--
|
||||
Returns true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter,
|
||||
or in the range if `considerRange == true`.
|
||||
Additionally, if the bound variable appears in an implicit parameter,
|
||||
it transitively looks for that implicit parameter.
|
||||
-/
|
||||
-- This should be kept in sync with `lean::has_loose_bvars_in_domain`
|
||||
def hasLooseBVarInExplicitDomain (e : Expr) (bvarIdx : Nat) (considerRange : Bool) : Bool :=
|
||||
match e with
|
||||
| Expr.forallE _ d b bi =>
|
||||
(hasLooseBVar d bvarIdx
|
||||
&& (bi.isExplicit
|
||||
-- "Transitivity": bvar occurs in current implicit argument,
|
||||
-- so we search for the current argument in the body.
|
||||
|| hasLooseBVarInExplicitDomain b 0 considerRange))
|
||||
|| hasLooseBVarInExplicitDomain b (bvarIdx+1) considerRange
|
||||
| e => considerRange && hasLooseBVar e bvarIdx
|
||||
|
||||
/--
|
||||
Lower the loose bound variables `>= s` in `e` by `d`.
|
||||
@@ -1297,16 +1309,16 @@ opaque liftLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
|
||||
It marks any parameter with an explicit binder annotation if there is another explicit arguments that depends on it or
|
||||
the resulting type if `considerRange == true`.
|
||||
|
||||
Remark: we use this function to infer the bind annotations of inductive datatype constructors, and structure projections.
|
||||
When the `{}` annotation is used in these commands, we set `considerRange == false`.
|
||||
Remark: we use this function to infer the binder annotations of structure projections.
|
||||
-/
|
||||
def inferImplicit : Expr → Nat → Bool → Expr
|
||||
| Expr.forallE n d b bi, i+1, considerRange =>
|
||||
-- This should be kept in synch with `lean::infer_implicit`
|
||||
def inferImplicit (e : Expr) (numParams : Nat) (considerRange : Bool) : Expr :=
|
||||
match e, numParams with
|
||||
| Expr.forallE n d b bi, i + 1 =>
|
||||
let b := inferImplicit b i considerRange
|
||||
let newInfo := if bi.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else bi
|
||||
mkForall n newInfo d b
|
||||
| e, 0, _ => e
|
||||
| e, _, _ => e
|
||||
| e, _ => e
|
||||
|
||||
/--
|
||||
Instantiates the loose bound variables in `e` using the `subst` array,
|
||||
|
||||
@@ -184,7 +184,7 @@ structure SnapshotTree where
|
||||
element : Snapshot
|
||||
/-- The asynchronously available children of the snapshot tree node. -/
|
||||
children : Array (SnapshotTask SnapshotTree)
|
||||
deriving Inhabited
|
||||
deriving Inhabited, TypeName
|
||||
|
||||
/--
|
||||
Helper class for projecting a heterogeneous hierarchy of snapshot classes to a homogeneous
|
||||
|
||||
@@ -425,6 +425,7 @@ where
|
||||
return { diagnostics, result? := none }
|
||||
|
||||
let headerEnv := headerEnv.setMainModule setup.mainModuleName
|
||||
let headerEnv ← headerEnv.enableRealizationsForImports setup.opts
|
||||
let mut traceState := default
|
||||
if trace.profiler.output.get? setup.opts |>.isSome then
|
||||
traceState := {
|
||||
|
||||
@@ -241,6 +241,11 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
|
||||
| nCtx, ctx, compose d₁ d₂ => return (← formatAux nCtx ctx d₁) ++ (← formatAux nCtx ctx d₂)
|
||||
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, trace data header children => do
|
||||
let childFmts ← children.mapM (formatAux nCtx ctx)
|
||||
if data.cls.isAnonymous then
|
||||
-- Sequence of top-level traces collected by `addTraceAsMessages`, do not indent.
|
||||
return .joinSep childFmts.toList "\n"
|
||||
|
||||
let mut msg := f!"[{data.cls}]"
|
||||
if data.startTime != 0 then
|
||||
msg := f!"{msg} [{data.stopTime - data.startTime}]"
|
||||
@@ -250,7 +255,6 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
|
||||
if maxNum > 0 && children.size > maxNum then
|
||||
children := children.take maxNum |>.push <|
|
||||
ofFormat f!"{children.size - maxNum} more entries... (increase `maxTraceChildren` to see more)"
|
||||
let childFmts ← children.mapM (formatAux nCtx ctx)
|
||||
return .nest 2 (.joinSep (msg::childFmts.toList) "\n")
|
||||
| nCtx, ctx?, ofLazy pp _ => do
|
||||
let dyn ← pp (ctx?.map (mkPPContext nCtx))
|
||||
|
||||
@@ -2203,10 +2203,103 @@ def instantiateMVarsIfMVarApp (e : Expr) : MetaM Expr := do
|
||||
else
|
||||
return e
|
||||
|
||||
private partial def setAllDiagRanges (snap : Language.SnapshotTree) (pos endPos : Position) :
|
||||
BaseIO Language.SnapshotTree := do
|
||||
let msgLog := snap.element.diagnostics.msgLog
|
||||
let msgLog := { msgLog with unreported := msgLog.unreported.map fun diag =>
|
||||
{ diag with pos, endPos } }
|
||||
return {
|
||||
element.diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog msgLog)
|
||||
children := (← snap.children.mapM fun task => return { task with
|
||||
stx? := none
|
||||
task := (← BaseIO.mapTask (t := task.task) (setAllDiagRanges · pos endPos)) })
|
||||
}
|
||||
|
||||
open Language
|
||||
|
||||
private structure RealizeConstantResult where
|
||||
snap : SnapshotTree
|
||||
error? : Option Exception
|
||||
deriving TypeName
|
||||
|
||||
/--
|
||||
Makes the helper constant `constName` that is derived from `forConst` available in the environment.
|
||||
`enableRealizationsForConst forConst` must have been called first on this environment branch. If
|
||||
this is the first environment branch requesting `constName` to be realized (atomically), `realize`
|
||||
is called with the environment and options at the time of calling `enableRealizationsForConst` if
|
||||
`forConst` is from the current module and the state just after importing (when
|
||||
`enableRealizationsForImports` should be called) otherwise, thus helping achieve deterministic
|
||||
results despite the non-deterministic choice of which thread is tasked with realization. In other
|
||||
words, the state after calling `realizeConst` is *as if* `realize` had been called immediately after
|
||||
`enableRealizationsForConst forConst`, though the effects of this call are visible only after
|
||||
calling `realizeConst`. See below for more details on the replayed effects.
|
||||
|
||||
`realizeConst` cannot check what other data is captured in the `realize` closure,
|
||||
so it is best practice to extract it into a separate function and pay close attention to the passed
|
||||
arguments, if any. `realize` must return with `constName` added to the environment,
|
||||
at which point all callers of `realizeConst` with this `constName` will be unblocked
|
||||
and have access to an updated version of their own environment containing any new constants
|
||||
`realize` added, including recursively realized constants. Traces, diagnostics, and raw std stream
|
||||
output are reported at all callers via `Core.logSnapshotTask` (so that the location of generated
|
||||
diagnostics is deterministic). Note that, as `realize` is run using the options at declaration time
|
||||
of `forConst`, trace options must be set prior to that (or, for imported constants, on the cmdline)
|
||||
in order to be active. The environment extension state at the end of `realize` is available to each
|
||||
caller via `EnvExtension.findStateAsync` for `constName`. If `realize` throws an exception or fails
|
||||
to add `constName` to the environment, an appropriate diagnostic is reported to all callers but no
|
||||
constants are added to the environment.
|
||||
-/
|
||||
def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
|
||||
MetaM Unit := do
|
||||
let env ← getEnv
|
||||
if env.contains constName then
|
||||
return
|
||||
withTraceNode `Meta.realizeConst (fun _ => return constName) do
|
||||
let coreCtx ← readThe Core.Context
|
||||
-- these fields should be invariant throughout the file
|
||||
let coreCtx := { fileName := coreCtx.fileName, fileMap := coreCtx.fileMap }
|
||||
let (env, dyn) ← env.realizeConst forConst constName (realizeAndReport coreCtx)
|
||||
if let some res := dyn.get? RealizeConstantResult then
|
||||
let mut snap := res.snap
|
||||
-- localize diagnostics
|
||||
if let some range := (← getRef).getRange? then
|
||||
let fileMap ← getFileMap
|
||||
snap ← setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
|
||||
Core.logSnapshotTask <| .finished (stx? := none) snap
|
||||
if let some e := res.error? then
|
||||
throw e
|
||||
setEnv env
|
||||
where
|
||||
-- similar to `wrapAsyncAsSnapshot` but not sufficiently so to share code
|
||||
realizeAndReport (coreCtx : Core.Context) env opts := do
|
||||
let coreCtx := { coreCtx with options := opts }
|
||||
let act :=
|
||||
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get opts) do
|
||||
-- catch all exceptions
|
||||
let _ : MonadExceptOf _ MetaM := MonadAlwaysExcept.except
|
||||
try
|
||||
realize
|
||||
if !(← getEnv).contains constName then
|
||||
throwError "Lean.Meta.realizeConst: {constName} was not added to the environment"
|
||||
finally
|
||||
addTraceAsMessages
|
||||
let res? ← act |>.run' |>.run coreCtx { env } |>.toBaseIO
|
||||
match res? with
|
||||
| .ok ((output, ()), st) => pure (st.env, .mk {
|
||||
snap := (← Core.mkSnapshot output coreCtx st)
|
||||
error? := none
|
||||
: RealizeConstantResult
|
||||
})
|
||||
| .error e => pure (env, .mk {
|
||||
snap := toSnapshotTree { diagnostics := .empty : Language.SnapshotLeaf}
|
||||
error? := some e
|
||||
: RealizeConstantResult
|
||||
})
|
||||
|
||||
end Meta
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Meta.isLevelDefEq.postponed
|
||||
registerTraceClass `Meta.realizeConst
|
||||
|
||||
export Meta (MetaM)
|
||||
|
||||
|
||||
@@ -784,6 +784,7 @@ def mkMatcherAuxDefinition (name : Name) (type : Expr) (value : Expr) : MetaM (E
|
||||
modifyEnv fun env => matcherExt.modifyState env fun s => s.insert (result.value, compile) name
|
||||
addMatcherInfo name mi
|
||||
setInlineAttribute name
|
||||
enableRealizationsForConst name
|
||||
if compile then
|
||||
compileDecl decl
|
||||
return (mkMatcherConst name, some addMatcher)
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
|
||||
Additional helper methods that require `MetaM` infrastructure.
|
||||
Authors: Leonardo de Moura, Kyle Miller
|
||||
-/
|
||||
prelude
|
||||
import Lean.AddDecl
|
||||
import Lean.Structure
|
||||
import Lean.Meta.AppBuilder
|
||||
|
||||
/-!
|
||||
# Structure methods that require `MetaM` infrastructure
|
||||
-/
|
||||
|
||||
namespace Lean.Meta
|
||||
|
||||
/--
|
||||
If `struct` is an application of the form `S ..` with `S` a constant for a structure,
|
||||
returns the name of the structure, otherwise throws an error.
|
||||
-/
|
||||
def getStructureName (struct : Expr) : MetaM Name :=
|
||||
match struct.getAppFn with
|
||||
| Expr.const declName .. => do
|
||||
@@ -19,4 +26,87 @@ def getStructureName (struct : Expr) : MetaM Name :=
|
||||
return declName
|
||||
| _ => throwError "expected structure"
|
||||
|
||||
/--
|
||||
Structure projection declaration for `mkProjections`.
|
||||
-/
|
||||
structure StructProjDecl where
|
||||
ref : Syntax
|
||||
projName : Name
|
||||
|
||||
/--
|
||||
Adds projection functions to the environment for the one-constructor inductive type named `n`.
|
||||
- The `projName`s in each `StructProjDecl` are used for the names of the declarations added to the environment.
|
||||
- If `instImplicit` is true, then generates projections with `self` being instance implicit.
|
||||
|
||||
Notes:
|
||||
- This function supports everything that `Expr.proj` supports (see `lean::type_checker::infer_proj`).
|
||||
This means we can generate projections for inductive types with one-constructor,
|
||||
even if it is an indexed family (which is not supported by the `structure` command).
|
||||
- We throw errors in the cases that `Expr.proj` is not type-correct.
|
||||
-/
|
||||
def mkProjections (n : Name) (projDecls : Array StructProjDecl) (instImplicit : Bool) : MetaM Unit :=
|
||||
withLCtx {} {} do
|
||||
let indVal ← getConstInfoInduct n
|
||||
if indVal.numCtors != 1 then
|
||||
throwError "cannot generate projections for '{.ofConstName n}', does not have exactly one constructor"
|
||||
let ctorVal ← getConstInfoCtor indVal.ctors.head!
|
||||
let isPredicate ← isPropFormerType indVal.type
|
||||
let lvls := indVal.levelParams.map mkLevelParam
|
||||
forallBoundedTelescope ctorVal.type indVal.numParams fun params ctorType => do
|
||||
if params.size != indVal.numParams then
|
||||
throwError "projection generation failed, '{.ofConstName n}' is an ill-formed inductive datatype"
|
||||
let selfType := mkAppN (.const n lvls) params
|
||||
let selfBI : BinderInfo := if instImplicit then .instImplicit else .default
|
||||
withLocalDecl `self selfBI selfType fun self => do
|
||||
let projArgs := params.push self
|
||||
-- Make modifications to parameter binder infos that apply to all projections
|
||||
let mut lctx ← getLCtx
|
||||
for param in params do
|
||||
let fvarId := param.fvarId!
|
||||
let decl ← fvarId.getDecl
|
||||
if !decl.binderInfo.isInstImplicit && !decl.type.isOutParam then
|
||||
/- We reset the implicit binder to have it be inferred by `Expr.inferImplicit`.
|
||||
However, outparams must be implicit. -/
|
||||
lctx := lctx.setBinderInfo fvarId .default
|
||||
else if decl.binderInfo.isInstImplicit && instImplicit then
|
||||
lctx := lctx.setBinderInfo fvarId .implicit
|
||||
-- Construct the projection functions:
|
||||
let mut ctorType := ctorType
|
||||
for h : i in [0:projDecls.size] do
|
||||
let {ref, projName} := projDecls[i]
|
||||
unless ctorType.isForall do
|
||||
throwErrorAt ref "\
|
||||
failed to generate projection '{projName}' for '{.ofConstName n}', \
|
||||
not enough constructor fields"
|
||||
let resultType := ctorType.bindingDomain!.consumeTypeAnnotations
|
||||
let isProp ← isProp resultType
|
||||
if isPredicate && !isProp then
|
||||
throwErrorAt ref "\
|
||||
failed to generate projection '{projName}' for the 'Prop'-valued type '{.ofConstName n}', \
|
||||
field must be a proof, but it has type\
|
||||
{indentExpr resultType}"
|
||||
let projType := lctx.mkForall projArgs resultType
|
||||
let projType := projType.inferImplicit indVal.numParams (considerRange := true)
|
||||
let projVal := lctx.mkLambda projArgs <| Expr.proj n i self
|
||||
let cval : ConstantVal := { name := projName, levelParams := indVal.levelParams, type := projType }
|
||||
withRef ref do
|
||||
if isProp then
|
||||
let env ← getEnv
|
||||
addDecl <|
|
||||
if env.hasUnsafe projType || env.hasUnsafe projVal then
|
||||
-- Theorems cannot be unsafe, using opaque instead.
|
||||
Declaration.opaqueDecl { cval with value := projVal, isUnsafe := true }
|
||||
else
|
||||
Declaration.thmDecl { cval with value := projVal }
|
||||
else
|
||||
let decl ← mkDefinitionValInferrringUnsafe projName indVal.levelParams projType projVal ReducibilityHints.abbrev
|
||||
-- Projections have special compiler support. No need to compile.
|
||||
addDecl <| Declaration.defnDecl decl
|
||||
-- Recall: we want instance projections to be in "reducible canonical form"
|
||||
if !instImplicit then
|
||||
setReducibleAttribute projName
|
||||
modifyEnv fun env => addProjectionFnInfo env projName ctorVal.name indVal.numParams i instImplicit
|
||||
let proj := mkApp (mkAppN (.const projName lvls) params) self
|
||||
ctorType := ctorType.bindingBody!.instantiate1 proj
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -120,6 +120,8 @@ where
|
||||
else
|
||||
collect (b.instantiate1 (← mkFreshExprMVar d)) (argIdx+1) targetIdx implicits targets'
|
||||
| _ =>
|
||||
unless targetIdx = targets.size do
|
||||
throwError "extra targets for '{elimInfo.elimExpr}'"
|
||||
return (implicits, targets')
|
||||
|
||||
structure CustomEliminator where
|
||||
|
||||
@@ -7,15 +7,13 @@ prelude
|
||||
import Lean.Meta.Tactic.Util
|
||||
|
||||
namespace Lean.Meta
|
||||
/--
|
||||
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
|
||||
If no local declarations require renaming, the original goal is returned unchanged.
|
||||
-/
|
||||
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
mvarId.checkNotAssigned `expose_names
|
||||
|
||||
/-- Returns a copy of the local context in which all declarations have clear, accessible names. -/
|
||||
private def getLCtxWithExposedNames : MetaM LocalContext := do
|
||||
let mut map : Std.HashMap Name FVarId := {}
|
||||
let mut toRename := #[]
|
||||
for localDecl in (← getLCtx) do
|
||||
let mut lctx ← getLCtx
|
||||
for localDecl in lctx do
|
||||
let userName := localDecl.userName
|
||||
if userName.hasMacroScopes then
|
||||
toRename := toRename.push localDecl.fvarId
|
||||
@@ -25,9 +23,8 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
|
||||
toRename := toRename.push fvarId
|
||||
map := map.insert userName localDecl.fvarId
|
||||
if toRename.isEmpty then
|
||||
return mvarId
|
||||
return lctx
|
||||
let mut next : Std.HashMap Name Nat := {}
|
||||
let mut lctx ← getLCtx
|
||||
-- Remark: Shadowed variables may be inserted later.
|
||||
toRename := toRename.qsort fun fvarId₁ fvarId₂ =>
|
||||
(lctx.get! fvarId₁).index < (lctx.get! fvarId₂).index
|
||||
@@ -49,8 +46,21 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
|
||||
next := next.insert baseName i
|
||||
map := map.insert userName fvarId
|
||||
lctx := lctx.modifyLocalDecl fvarId (·.setUserName userName)
|
||||
return lctx
|
||||
|
||||
/--
|
||||
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
|
||||
If no local declarations require renaming, the original goal is returned unchanged.
|
||||
-/
|
||||
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
mvarId.checkNotAssigned `expose_names
|
||||
let lctx ← getLCtxWithExposedNames
|
||||
let mvarNew ← mkFreshExprMVarAt lctx (← getLocalInstances) (← mvarId.getType) .syntheticOpaque (← mvarId.getTag)
|
||||
mvarId.assign mvarNew
|
||||
return mvarNew.mvarId!
|
||||
|
||||
/-- Creates a temporary local context where all names are exposed, and executes `k` -/
|
||||
def withExposedNames (k : MetaM α) : MetaM α := do
|
||||
withNewMCtxDepth <| withLCtx (← getLCtxWithExposedNames) (← getLocalInstances) k
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -28,6 +28,7 @@ import Lean.Meta.Tactic.Grind.Arith
|
||||
import Lean.Meta.Tactic.Grind.Ext
|
||||
import Lean.Meta.Tactic.Grind.MatchCond
|
||||
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
|
||||
import Lean.Meta.Tactic.Grind.Diseq
|
||||
|
||||
namespace Lean
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Types
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
namespace Lean
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat
|
||||
builtin_initialize registerTraceClass `grind.cutsat.model
|
||||
builtin_initialize registerTraceClass `grind.cutsat.subst
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.eq.unsat (inherited := true)
|
||||
@@ -43,4 +46,12 @@ builtin_initialize registerTraceClass `grind.cutsat.le.upper (inherited := true)
|
||||
builtin_initialize registerTraceClass `grind.cutsat.assign
|
||||
builtin_initialize registerTraceClass `grind.cutsat.conflict
|
||||
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.cutsat.diseq.trivial (inherited := true)
|
||||
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.eq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq.split
|
||||
builtin_initialize registerTraceClass `grind.debug.cutsat.backtrack
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -59,7 +59,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
let .add a₁ x p₁ := c.p | c.throwUnexpected
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
if let some c' := (← get').dvdCnstrs[x]! then
|
||||
if let some c' := (← get').dvds[x]! then
|
||||
trace[grind.cutsat.dvd.solve] "{← c.pp}, {← c'.pp}"
|
||||
let d₂ := c'.d
|
||||
let .add a₂ _ p₂ := c'.p | c'.throwUnexpected
|
||||
@@ -76,7 +76,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
let β_d₁_p₂ := p₂.mul (β*d₁)
|
||||
let combine ← mkDvdCnstr (d₁*d₂) (.add d x (α_d₂_p₁.combine β_d₁_p₂)) (.solveCombine c c')
|
||||
trace[grind.cutsat.dvd.solve.combine] "{← combine.pp}"
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set x none}
|
||||
modify' fun s => { s with dvds := s.dvds.set x none}
|
||||
combine.assert
|
||||
let a₂_p₁ := p₁.mul a₂
|
||||
let a₁_p₂ := p₂.mul (-a₁)
|
||||
@@ -86,7 +86,7 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
|
||||
else
|
||||
trace[grind.cutsat.dvd.update] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set x (some c) }
|
||||
modify' fun s => { s with dvds := s.dvds.set x (some c) }
|
||||
|
||||
builtin_grind_propagator propagateDvd ↓Dvd.dvd := fun e => do
|
||||
let_expr Dvd.dvd _ inst a b ← e | return ()
|
||||
|
||||
@@ -4,14 +4,18 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Diseq
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
private def _root_.Int.Linear.Poly.substVar (p : Poly) : GoalM (Option (Var × EqCnstr × Poly)) := do
|
||||
let some (a, x, c) ← p.findVarToSubst | return none
|
||||
let b := c.p.coeff x
|
||||
let p := p.mul (-b) |>.combine (c.p.mul a)
|
||||
return some (x, c, p)
|
||||
|
||||
def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
|
||||
let c ← if c.p.isSorted then
|
||||
@@ -19,6 +23,75 @@ def EqCnstr.norm (c : EqCnstr) : GoalM EqCnstr := do
|
||||
else
|
||||
mkEqCnstr c.p.norm (.norm c)
|
||||
|
||||
def mkDiseqCnstr (p : Poly) (h : DiseqCnstrProof) : GoalM DiseqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
|
||||
def DiseqCnstr.norm (c : DiseqCnstr) : GoalM DiseqCnstr := do
|
||||
let c ← if c.p.isSorted then
|
||||
pure c
|
||||
else
|
||||
mkDiseqCnstr c.p.norm (.norm c)
|
||||
|
||||
/--
|
||||
Given an equation `c₁` containing the monomial `a*x`, and a disequality constraint `c₂`
|
||||
containing the monomial `b*x`, eliminate `x` by applying substitution.
|
||||
-/
|
||||
def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DiseqCnstr) : GoalM DiseqCnstr := do
|
||||
let p := c₁.p
|
||||
let q := c₂.p
|
||||
let p := p.mul b |>.combine (q.mul (-a))
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
|
||||
mkDiseqCnstr p (.subst x c₁ c₂)
|
||||
|
||||
partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIncRecDepth do
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
let c ← mkDiseqCnstr p (.subst x c₁ c)
|
||||
applySubsts c
|
||||
|
||||
/--
|
||||
Given a disequality `c`, tries to find an inequality to be refined using
|
||||
`p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
|
||||
-/
|
||||
private def DiseqCnstr.findLe (c : DiseqCnstr) : GoalM Bool := do
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
let s ← get'
|
||||
let go (atLower : Bool) : GoalM Bool := do
|
||||
let cs' := if atLower then s.lowers[x]! else s.uppers[x]!
|
||||
for c' in cs' do
|
||||
if c.p == c'.p || c.p.isNegEq c'.p then
|
||||
c'.erase
|
||||
let le ← mkLeCnstr (c'.p.addConst 1) (.ofLeDiseq c' c)
|
||||
le.assert
|
||||
return true
|
||||
return false
|
||||
go true <||> go false
|
||||
|
||||
def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm
|
||||
let c ← c.applySubsts
|
||||
if c.p.isUnsatDiseq then
|
||||
setInconsistent (.diseq c)
|
||||
return ()
|
||||
if c.isTrivial then
|
||||
trace[grind.cutsat.diseq.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
let c ← if k == 1 then
|
||||
pure c
|
||||
else
|
||||
mkDiseqCnstr (c.p.div k) (.divCoeffs c)
|
||||
if (← c.findLe) then
|
||||
return ()
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
c.p.updateOccs
|
||||
trace[grind.cutsat.diseq] "{← c.pp}"
|
||||
modify' fun s => { s with diseqs := s.diseqs.modify x (·.push c) }
|
||||
if (← c.satisfied) == .false then
|
||||
resetAssignmentFrom x
|
||||
|
||||
/--
|
||||
Selects the variable in the given linear polynomial whose coefficient has the smallest absolute value.
|
||||
-/
|
||||
@@ -39,18 +112,16 @@ where
|
||||
go k x p
|
||||
|
||||
partial def EqCnstr.applySubsts (c : EqCnstr) : GoalM EqCnstr := withIncRecDepth do
|
||||
let some (a, x, c₁) ← c.p.findVarToSubst | return c
|
||||
let some (x, c₁, p) ← c.p.substVar | return c
|
||||
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
|
||||
let b := c₁.p.coeff x
|
||||
let p := c.p.mul (-b) |>.combine (c₁.p.mul a)
|
||||
let c ← mkEqCnstr p (.subst x c₁ c)
|
||||
applySubsts c
|
||||
|
||||
private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
let some c' := (← get').dvdCnstrs[y]! | return ()
|
||||
let some c' := (← get').dvds[y]! | return ()
|
||||
let b := c'.p.coeff x
|
||||
if b == 0 then return ()
|
||||
modify' fun s => { s with dvdCnstrs := s.dvdCnstrs.set y none }
|
||||
modify' fun s => { s with dvds := s.dvds.set y none }
|
||||
let c' ← c'.applyEq a x c b
|
||||
c'.assert
|
||||
|
||||
@@ -93,10 +164,31 @@ private def updateUppers (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Uni
|
||||
modify' fun s => { s with uppers := s.uppers.set y uppers' }
|
||||
updateLeCnstrs a x c todo
|
||||
|
||||
private def splitDiseqs (x : Var) (cs : PArray DiseqCnstr) : GoalM (PArray DiseqCnstr × Array (Int × DiseqCnstr)) := do
|
||||
let mut cs' := {}
|
||||
let mut todo := #[]
|
||||
for c in cs do
|
||||
let b := c.p.coeff x
|
||||
if b == 0 then
|
||||
cs' := cs'.push c
|
||||
else
|
||||
todo := todo.push (b, c)
|
||||
return (cs', todo)
|
||||
|
||||
private def updateDiseqs (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
let (diseqs', todo) ← splitDiseqs x (← get').diseqs[y]!
|
||||
modify' fun s => { s with diseqs := s.diseqs.set y diseqs' }
|
||||
for (b, c₂) in todo do
|
||||
let c₂ ← c₂.applyEq a x c b
|
||||
c₂.assert
|
||||
if (← inconsistent) then return ()
|
||||
|
||||
private def updateOccsAt (k : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
|
||||
updateDvdCnstr k x c y
|
||||
updateLowers k x c y
|
||||
updateUppers k x c y
|
||||
updateDiseqs k x c y
|
||||
|
||||
private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
|
||||
let ys := (← get').occurs[x]!
|
||||
@@ -105,7 +197,8 @@ private def updateOccs (k : Int) (x : Var) (c : EqCnstr) : GoalM Unit := do
|
||||
for y in ys do
|
||||
updateOccsAt k x c y
|
||||
|
||||
def EqCnstr.assert (c : EqCnstr) : GoalM Unit := do
|
||||
@[export lean_grind_cutsat_assert_eq]
|
||||
def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
trace[grind.cutsat.assert] "{← c.pp}"
|
||||
let c ← c.norm
|
||||
@@ -151,14 +244,16 @@ private def exprAsPoly (a : Expr) : GoalM Poly := do
|
||||
|
||||
@[export lean_process_cutsat_eq]
|
||||
def processNewEqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {b}"
|
||||
let p₁ ← exprAsPoly a
|
||||
let p₂ ← exprAsPoly b
|
||||
let p := p₁.combine (p₂.mul (-1))
|
||||
let c ← mkEqCnstr p (.core p₁ p₂ (← mkEqProof a b))
|
||||
c.assert
|
||||
|
||||
@[export lean_process_new_cutsat_lit]
|
||||
@[export lean_process_cutsat_eq_lit]
|
||||
def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.eq] "{a} = {ke}"
|
||||
let some k ← getIntValue? ke | return ()
|
||||
let p₁ ← exprAsPoly a
|
||||
let h ← mkEqProof a ke
|
||||
@@ -170,6 +265,20 @@ def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
|
||||
mkEqCnstr p (.core p₁ p₂ h)
|
||||
c.assert
|
||||
|
||||
@[export lean_process_cutsat_diseq]
|
||||
def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
|
||||
trace[grind.debug.cutsat.diseq] "{a} ≠ {b}"
|
||||
let p₁ ← exprAsPoly a
|
||||
let some h ← mkDiseqProof? a b
|
||||
| throwError "internal `grind` error, failed to build disequality proof for{indentExpr a}\nand{indentExpr b}"
|
||||
let c ← if let some 0 ← getIntValue? b then
|
||||
mkDiseqCnstr p₁ (.expr h)
|
||||
else
|
||||
let p₂ ← exprAsPoly b
|
||||
let p := p₁.combine (p₂.mul (-1))
|
||||
mkDiseqCnstr p (.core p₁ p₂ h)
|
||||
c.assert
|
||||
|
||||
/-- Different kinds of terms internalized by this module. -/
|
||||
private inductive SupportedTermKind where
|
||||
| add | mul | num
|
||||
|
||||
@@ -59,11 +59,11 @@ def checkUppers : GoalM Unit := do
|
||||
assert! s.uppers.size == s.vars.size
|
||||
checkLeCnstrs s.uppers (isLower := false)
|
||||
|
||||
def checkDvdCnstrs : GoalM Unit := do
|
||||
def checkDvds : GoalM Unit := do
|
||||
let s ← get'
|
||||
assert! s.vars.size == s.dvdCnstrs.size
|
||||
assert! s.vars.size == s.dvds.size
|
||||
let mut x := 0
|
||||
for c? in s.dvdCnstrs do
|
||||
for c? in s.dvds do
|
||||
if let some c := c? then
|
||||
c.p.checkCnstrOf x
|
||||
assert! c.d > 1
|
||||
@@ -97,12 +97,23 @@ def checkElimStack : GoalM Unit := do
|
||||
for x in (← get').elimStack do
|
||||
assert! (← eliminated x)
|
||||
|
||||
def checkDiseqCnstrs : GoalM Unit := do
|
||||
let s ← get'
|
||||
assert! s.vars.size == s.diseqs.size
|
||||
let mut x := 0
|
||||
for cs in s.diseqs do
|
||||
for c in cs do
|
||||
c.p.checkCnstrOf x
|
||||
x := x + 1
|
||||
return ()
|
||||
|
||||
def checkInvariants : GoalM Unit := do
|
||||
checkVars
|
||||
checkDvdCnstrs
|
||||
checkDvds
|
||||
checkLowers
|
||||
checkUppers
|
||||
checkElimEqs
|
||||
checkElimStack
|
||||
checkDiseqCnstrs
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -45,6 +45,57 @@ partial def LeCnstr.applySubsts (c : LeCnstr) : GoalM LeCnstr := withIncRecDepth
|
||||
let c ← c.applyEq a x c₁ b
|
||||
applySubsts c
|
||||
|
||||
def _root_.Int.Linear.Poly.isNegEq (p₁ p₂ : Poly) : Bool :=
|
||||
match p₁, p₂ with
|
||||
| .num k₁, .num k₂ => k₁ == -k₂
|
||||
| .add a₁ x p₁, .add a₂ y p₂ => a₁ == -a₂ && x == y && isNegEq p₁ p₂
|
||||
| _, _ => false
|
||||
|
||||
def LeCnstr.erase (c : LeCnstr) : GoalM Unit := do
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
if a < 0 then
|
||||
modify' fun s => { s with lowers := s.lowers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
|
||||
else
|
||||
modify' fun s => { s with uppers := s.uppers.modify x fun cs' => cs'.filter fun c' => c'.p != c.p }
|
||||
|
||||
/--
|
||||
Given a lower (upper) bound constraint `c`, tries to find
|
||||
an imply equality by searching a upper (lower) bound constraint `c'` such that
|
||||
`c.p == -c'.p`
|
||||
-/
|
||||
private def findEq (c : LeCnstr) : GoalM Bool := do
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
let s ← get'
|
||||
let cs' := if a < 0 then s.uppers[x]! else s.lowers[x]!
|
||||
for c' in cs' do
|
||||
if c.p.isNegEq c'.p then
|
||||
c'.erase
|
||||
let eq ← mkEqCnstr c.p (.ofLeGe c c')
|
||||
eq.assert
|
||||
return true
|
||||
return false
|
||||
|
||||
/--
|
||||
Applies `p ≤ 0 → p ≠ 0 → p + 1 ≤ 0`
|
||||
-/
|
||||
private def refineWithDiseq (c : LeCnstr) : GoalM LeCnstr := do
|
||||
let .add _ x _ := c.p | c.throwUnexpected
|
||||
let mut c := c
|
||||
repeat
|
||||
let some c' ← refineWithDiseqStep? x c | return c
|
||||
c := c'
|
||||
return c
|
||||
where
|
||||
refineWithDiseqStep? (x : Var) (c : LeCnstr) : GoalM (Option LeCnstr) := do
|
||||
let s ← get'
|
||||
let cs' := s.diseqs[x]!
|
||||
for c' in cs' do
|
||||
if c.p == c'.p || c.p.isNegEq c'.p then
|
||||
-- Remove `c'`
|
||||
modify' fun s => { s with diseqs := s.diseqs.modify x fun cs' => cs'.filter fun c => c.p != c'.p }
|
||||
return some (← mkLeCnstr (c.p.addConst 1) (.ofLeDiseq c c'))
|
||||
return none
|
||||
|
||||
def LeCnstr.assert (c : LeCnstr) : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
let c ← c.norm
|
||||
@@ -56,6 +107,9 @@ def LeCnstr.assert (c : LeCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.le.trivial] "{← c.pp}"
|
||||
return ()
|
||||
let .add a x _ := c.p | c.throwUnexpected
|
||||
if (← findEq c) then
|
||||
return ()
|
||||
let c ← refineWithDiseq c
|
||||
if a < 0 then
|
||||
trace[grind.cutsat.le.lower] "{← c.pp}"
|
||||
c.p.updateOccs
|
||||
|
||||
99
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean
Normal file
99
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/Model.lean
Normal file
@@ -0,0 +1,99 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Types
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
private def isIntENode (n : ENode) : MetaM Bool :=
|
||||
withDefault do isDefEq (← inferType n.self) Int.mkType
|
||||
|
||||
private def getCutsatAssignment? (goal : Goal) (node : ENode) : Option Rat := Id.run do
|
||||
let some e := node.cutsat? | return none
|
||||
let some x := goal.arith.cutsat.varMap.find? { expr := e } | return none
|
||||
if h : x < goal.arith.cutsat.assignment.size then
|
||||
return goal.arith.cutsat.assignment[x]
|
||||
else
|
||||
return none
|
||||
|
||||
private partial def satisfyDiseqs (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (v : Int) : Bool := Id.run do
|
||||
let some parents := goal.parents.find? { expr := e } | return true
|
||||
for parent in parents do
|
||||
let_expr Eq _ lhs rhs := parent | continue
|
||||
let some root := goal.getRoot? parent | continue
|
||||
if root.isConstOf ``False then
|
||||
let some lhsRoot := goal.getRoot? lhs | continue
|
||||
let some rhsRoot := goal.getRoot? rhs | continue
|
||||
if lhsRoot == e && !checkDiseq rhsRoot then return false
|
||||
if rhsRoot == e && !checkDiseq lhsRoot then return false
|
||||
return true
|
||||
where
|
||||
checkDiseq (other : Expr) : Bool :=
|
||||
if let some v' := a[other]? then
|
||||
v' != v
|
||||
else
|
||||
true
|
||||
|
||||
private partial def pickUnusedValue (goal : Goal) (a : Std.HashMap Expr Rat) (e : Expr) (next : Int) (alreadyUsed : Std.HashSet Int) : Int :=
|
||||
go next
|
||||
where
|
||||
go (next : Int) : Int :=
|
||||
if alreadyUsed.contains next then
|
||||
go (next+1)
|
||||
else if satisfyDiseqs goal a e next then
|
||||
next
|
||||
else
|
||||
go (next + 1)
|
||||
|
||||
private def assignEqc (goal : Goal) (e : Expr) (v : Rat) (a : Std.HashMap Expr Rat) : Std.HashMap Expr Rat := Id.run do
|
||||
let mut a := a
|
||||
for e in goal.getEqc e do
|
||||
a := a.insert e v
|
||||
return a
|
||||
|
||||
private def isInterpretedTerm (e : Expr) : Bool :=
|
||||
isIntNum e || e.isAppOf ``HAdd.hAdd || e.isAppOf ``HMul.hMul || e.isAppOf ``HSub.hSub
|
||||
|| e.isAppOf ``Neg.neg -- TODO add missing ones
|
||||
|
||||
/--
|
||||
Construct a model that statisfies all constraints in the cutsat model.
|
||||
It also assigns values to integer terms that have not been internalized by the
|
||||
cutsat model.
|
||||
|
||||
Remark: it uses rational numbers because cutsat may have failed to build an
|
||||
integer model.
|
||||
-/
|
||||
def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
|
||||
let mut used : Std.HashSet Int := {}
|
||||
let mut nextVal : Int := 0
|
||||
let mut model := {}
|
||||
let nodes := goal.getENodes
|
||||
-- Assign on expressions associated with cutsat terms or interpreted terms
|
||||
for node in nodes do
|
||||
if isSameExpr node.root node.self then
|
||||
if (← isIntENode node) then
|
||||
if let some v := getCutsatAssignment? goal node then
|
||||
model := assignEqc goal node.self v model
|
||||
if v.den == 1 then used := used.insert v.num
|
||||
else if let some v ← getIntValue? node.self then
|
||||
model := assignEqc goal node.self v model
|
||||
used := used.insert v
|
||||
-- Assign the remaining ones with values not used by cutsat
|
||||
for node in nodes do
|
||||
if isSameExpr node.root node.self then
|
||||
if (← isIntENode node) then
|
||||
if (← getIntValue? node.self).isNone &&
|
||||
(getCutsatAssignment? goal node).isNone then
|
||||
let v := pickUnusedValue goal model node.self nextVal used
|
||||
model := assignEqc goal node.self v model
|
||||
used := used.insert v
|
||||
let mut r := #[]
|
||||
for (e, v) in model do
|
||||
unless isInterpretedTerm e do
|
||||
r := r.push (e, v)
|
||||
return r
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
@@ -14,6 +14,26 @@ private def DvdCnstr.get_d_a (c : DvdCnstr) : GoalM (Int × Int) := do
|
||||
return (d, a)
|
||||
|
||||
mutual
|
||||
partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
return h
|
||||
| .core p₁ p₂ h =>
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_of_core) (← getContext) (toExpr p₁) (toExpr p₂) (toExpr c'.p) reflBoolTrue h
|
||||
| .norm c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_norm) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .divCoeffs c =>
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_coeff) (← getContext) (toExpr c.p) (toExpr c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
|
||||
| .subst x c₁ c₂ =>
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_eq_subst)
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
| .ofLeGe c₁ c₂ =>
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_of_le_ge)
|
||||
(← getContext) (toExpr c₁.p) (toExpr c₂.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
|
||||
partial def DvdCnstr.toExprProof (c' : DvdCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
@@ -72,41 +92,131 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := c'.caching do
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue
|
||||
(← c₁.toExprProof) (← c₂.toExprProof)
|
||||
| .ofLeDiseq c₁ c₂ =>
|
||||
return mkApp7 (mkConst ``Int.Linear.le_of_le_diseq)
|
||||
(← getContext) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
| .ofDiseqSplit c₁ fvarId h _ =>
|
||||
let p₂ := c₁.p.addConst 1
|
||||
let hFalse ← h.toExprProofCore
|
||||
let hNot := mkLambda `h .default (mkIntLE (← p₂.denoteExpr') (mkIntLit 0)) (hFalse.abstract #[mkFVar fvarId])
|
||||
return mkApp7 (mkConst ``Int.Linear.diseq_split_resolve)
|
||||
(← getContext) (toExpr c₁.p) (toExpr p₂) (toExpr c'.p) reflBoolTrue (← c₁.toExprProof) hNot
|
||||
|
||||
partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := c'.caching do
|
||||
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := c'.caching do
|
||||
match c'.h with
|
||||
| .expr h =>
|
||||
return h
|
||||
| .core p₁ p₂ h =>
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_of_core) (← getContext) (toExpr p₁) (toExpr p₂) (toExpr c'.p) reflBoolTrue h
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_of_core) (← getContext) (toExpr p₁) (toExpr p₂) (toExpr c'.p) reflBoolTrue h
|
||||
| .norm c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_norm) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
return mkApp5 (mkConst ``Int.Linear.diseq_norm) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .divCoeffs c =>
|
||||
let k := c.p.gcdCoeffs c.p.getConst
|
||||
return mkApp6 (mkConst ``Int.Linear.eq_coeff) (← getContext) (toExpr c.p) (toExpr c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
|
||||
return mkApp6 (mkConst ``Int.Linear.diseq_coeff) (← getContext) (toExpr c.p) (toExpr c'.p) (toExpr k) reflBoolTrue (← c.toExprProof)
|
||||
| .neg c =>
|
||||
return mkApp5 (mkConst ``Int.Linear.diseq_neg) (← getContext) (toExpr c.p) (toExpr c'.p) reflBoolTrue (← c.toExprProof)
|
||||
| .subst x c₁ c₂ =>
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_eq_subst)
|
||||
return mkApp8 (mkConst ``Int.Linear.eq_diseq_subst)
|
||||
(← getContext) (toExpr x) (toExpr c₁.p) (toExpr c₂.p) (toExpr c'.p)
|
||||
reflBoolTrue (← c₁.toExprProof) (← c₂.toExprProof)
|
||||
|
||||
partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
|
||||
match h with
|
||||
| .le c =>
|
||||
trace[grind.cutsat.le.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .dvd c =>
|
||||
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
|
||||
return mkApp5 (mkConst ``Int.Linear.dvd_unsat) (← getContext) (toExpr c.d) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .eq c =>
|
||||
trace[grind.cutsat.eq.unsat] "{← c.pp}"
|
||||
if c.p.isUnsatEq then
|
||||
return mkApp4 (mkConst ``Int.Linear.eq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
else
|
||||
let k := c.p.gcdCoeffs'
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) (← getContext) (toExpr c.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
|
||||
| .diseq c =>
|
||||
trace[grind.cutsat.diseq.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.diseq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
|
||||
end
|
||||
|
||||
def UnsatProof.toExprProof (h : UnsatProof) : GoalM Expr := do
|
||||
withProofContext do h.toExprProofCore
|
||||
|
||||
def setInconsistent (h : UnsatProof) : GoalM Unit := do
|
||||
let hf ← withProofContext do
|
||||
match h with
|
||||
| .le c =>
|
||||
trace[grind.cutsat.le.unsat] "{← c.pp}"
|
||||
return mkApp4 (mkConst ``Int.Linear.le_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .dvd c =>
|
||||
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
|
||||
return mkApp5 (mkConst ``Int.Linear.dvd_unsat) (← getContext) (toExpr c.d) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
| .eq c =>
|
||||
trace[grind.cutsat.eq.unsat] "{← c.pp}"
|
||||
if c.p.isUnsatEq then
|
||||
return mkApp4 (mkConst ``Int.Linear.eq_unsat) (← getContext) (toExpr c.p) reflBoolTrue (← c.toExprProof)
|
||||
else
|
||||
let k := c.p.gcdCoeffs'
|
||||
return mkApp5 (mkConst ``Int.Linear.eq_unsat_coeff) (← getContext) (toExpr c.p) (toExpr (Int.ofNat k)) reflBoolTrue (← c.toExprProof)
|
||||
closeGoal hf
|
||||
if (← get').caseSplits then
|
||||
-- Let the search procedure in `SearchM` resolve the conflict.
|
||||
modify' fun s => { s with conflict? := some h }
|
||||
else
|
||||
let h ← h.toExprProof
|
||||
closeGoal h
|
||||
|
||||
/-!
|
||||
A cutsat proof may depend on decision variables.
|
||||
We collect them and perform non chronological backtracking.
|
||||
-/
|
||||
|
||||
structure CollectDecVars.State where
|
||||
visited : Std.HashSet Nat := {}
|
||||
found : FVarIdSet := {}
|
||||
|
||||
abbrev CollectDecVarsM := ReaderT FVarIdSet (StateM CollectDecVars.State)
|
||||
|
||||
private def alreadyVisited (id : Nat) : CollectDecVarsM Bool := do
|
||||
if (← get).visited.contains id then return true
|
||||
modify fun s => { s with visited := s.visited.insert id }
|
||||
return false
|
||||
|
||||
private def markAsFound (fvarId : FVarId) : CollectDecVarsM Unit := do
|
||||
modify fun s => { s with found := s.found.insert fvarId }
|
||||
|
||||
private def collectExpr (e : Expr) : CollectDecVarsM Unit := do
|
||||
let .fvar fvarId := e | return ()
|
||||
if (← read).contains fvarId then
|
||||
markAsFound fvarId
|
||||
|
||||
mutual
|
||||
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .core .. => return () -- Equalities coming from the core never contain cutsat decision variables
|
||||
| .norm c | .divCoeffs c => c.collectDecVars
|
||||
| .subst _ c₁ c₂ | .ofLeGe c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
partial def DvdCnstr.collectDecVars (c' : DvdCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .norm c | .elim c | .divCoeffs c | .ofEq _ c => c.collectDecVars
|
||||
| .solveCombine c₁ c₂ | .solveElim c₁ c₂ | .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .notExpr .. => return () -- This kind of proof is used for connecting with the `grind` core.
|
||||
| .norm c | .divCoeffs c => c.collectDecVars
|
||||
| .combine c₁ c₂ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
| .ofDiseqSplit _ _ _ decVars =>
|
||||
-- Recall that we cache the decision variables used in this kind of proof
|
||||
for fvar in decVars do
|
||||
markAsFound fvar
|
||||
|
||||
partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c'.id) do
|
||||
match c'.h with
|
||||
| .expr h => collectExpr h
|
||||
| .core .. => return () -- Disequalities coming from the core never contain cutsat decision variables
|
||||
| .norm c | .divCoeffs c | .neg c => c.collectDecVars
|
||||
| .subst _ c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
|
||||
|
||||
end
|
||||
|
||||
def UnsatProof.collectDecVars (h : UnsatProof) : CollectDecVarsM Unit := do
|
||||
match h with
|
||||
| .le c | .dvd c | .eq c | .diseq c => c.collectDecVars
|
||||
|
||||
abbrev CollectDecVarsM.run (x : CollectDecVarsM Unit) (decVars : FVarIdSet) : FVarIdSet :=
|
||||
let (_, s) := x decVars |>.run {}
|
||||
s.found
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
@@ -8,16 +8,62 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.DvdCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.LeCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
def getBestLower? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
private def checkIsNextVar (x : Var) : GoalM Unit := do
|
||||
if x != (← get').assignment.size then
|
||||
throwError "`grind` internal error, assigning variable out of order"
|
||||
|
||||
private def traceAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
|
||||
|
||||
private def setAssignment (x : Var) (v : Rat) : GoalM Unit := do
|
||||
checkIsNextVar x
|
||||
traceAssignment x v
|
||||
modify' fun s => { s with assignment := s.assignment.push v }
|
||||
|
||||
private def skipAssignment (x : Var) : GoalM Unit := do
|
||||
checkIsNextVar x
|
||||
modify' fun s => { s with assignment := s.assignment.push 0 }
|
||||
|
||||
/-- Assign eliminated variables using `elimEqs` field. -/
|
||||
private def assignElimVars : GoalM Unit := do
|
||||
if (← inconsistent) then return ()
|
||||
go (← get').elimStack
|
||||
where
|
||||
go (xs : List Var) : GoalM Unit := do
|
||||
match xs with
|
||||
| [] => return ()
|
||||
| x :: xs =>
|
||||
let some c := (← get').elimEqs[x]!
|
||||
| throwError "`grind` internal error, eliminated variable must have equation associated with it"
|
||||
-- `x` may not be the max variable
|
||||
let a := c.p.coeff x
|
||||
if a == 0 then c.throwUnexpected
|
||||
-- ensure `x` is 0 when evaluating `c.p`
|
||||
modify' fun s => { s with assignment := s.assignment.set x 0 }
|
||||
let some v ← c.p.eval? | c.throwUnexpected
|
||||
let v := (-v) / a
|
||||
traceAssignment x v
|
||||
modify' fun s => { s with assignment := s.assignment.set x v }
|
||||
go xs
|
||||
|
||||
/--
|
||||
Assuming all variables smaller than `x` have already been assigned,
|
||||
returns the best lower bound for `x` using the given partial assignment and
|
||||
inequality constraints where `x` is the maximal variable.
|
||||
-/
|
||||
def getBestLower? (x : Var) : GoalM (Option (Rat × LeCnstr)) := do
|
||||
let s ← get'
|
||||
let mut best? := none
|
||||
for c in s.lowers[x]! do
|
||||
let .add k _ p := c.p | c.throwUnexpected
|
||||
let some v ← p.eval? | c.throwUnexpected
|
||||
let lower' := Int.Linear.cdiv v (-k)
|
||||
let lower' := v / (-k)
|
||||
if let some (lower, _) := best? then
|
||||
if lower' > lower then
|
||||
best? := some (lower', c)
|
||||
@@ -25,7 +71,12 @@ def getBestLower? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
best? := some (lower', c)
|
||||
return best?
|
||||
|
||||
def getBestUpper? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
/--
|
||||
Assuming all variables smaller than `x` have already been assigned,
|
||||
returns the best upper bound for `x` using the given partial assignment and
|
||||
inequality constraints where `x` is the maximal variable.
|
||||
-/
|
||||
def getBestUpper? (x : Var) : GoalM (Option (Rat × LeCnstr)) := do
|
||||
let s ← get'
|
||||
let mut best? := none
|
||||
for c in s.uppers[x]! do
|
||||
@@ -39,10 +90,40 @@ def getBestUpper? (x : Var) : GoalM (Option (Int × LeCnstr)) := do
|
||||
best? := some (upper', c)
|
||||
return best?
|
||||
|
||||
def DvdCnstr.getSolutions? (c : DvdCnstr) : GoalM (Option (Int × Int)) := do
|
||||
/-- Returns values we cannot assign `x` because of disequality constraints. -/
|
||||
def getDiseqValues (x : Var) : SearchM (Array (Rat × DiseqCnstr)) := do
|
||||
let s ← get'
|
||||
let mut r := #[]
|
||||
for c in s.diseqs[x]! do
|
||||
let .add k _ p := c.p | c.throwUnexpected
|
||||
let some v ← p.eval? | c.throwUnexpected
|
||||
if (← isApprox) then
|
||||
r := r.push (((-v)/k), c)
|
||||
else
|
||||
-- We are building an integer model,
|
||||
-- if `k` does not divide `v`, we can just ignore the disequality.
|
||||
let v := v.num
|
||||
if v % k == 0 then
|
||||
r := r.push (v / k, c)
|
||||
return r
|
||||
|
||||
/--
|
||||
Solution space for a divisibility constraint of the form `d ∣ a*x + b`
|
||||
See `DvdCnstr.getSolutions?` to understand how it is computed.
|
||||
-/
|
||||
structure DvdSolution where
|
||||
d : Int := 1
|
||||
b : Int := 0
|
||||
|
||||
def DvdCnstr.getSolutions? (c : DvdCnstr) : SearchM (Option DvdSolution) := do
|
||||
let d := c.d
|
||||
let .add a _ p := c.p | c.throwUnexpected
|
||||
let some b ← p.eval? | c.throwUnexpected
|
||||
if b.den != 1 then
|
||||
-- `b` is a rational number, mark model as imprecise, and ignore the constraint
|
||||
setImprecise
|
||||
return none
|
||||
let b := b.num
|
||||
-- We must solve `d ∣ a*x + b`
|
||||
let g := d.gcd a
|
||||
if b % g != 0 then
|
||||
@@ -58,30 +139,7 @@ def DvdCnstr.getSolutions? (c : DvdCnstr) : GoalM (Option (Int × Int)) := do
|
||||
-- `a*x = -b (mod d)`
|
||||
-- `x = -b*a' (mod d)`
|
||||
-- `x = k*d + -b*a'` for any k
|
||||
return some (d, -b*a')
|
||||
|
||||
private partial def setAssignment (x : Var) (v : Int) : GoalM Unit := do
|
||||
if x == (← get').assignment.size then
|
||||
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
|
||||
modify' fun s => { s with assignment := s.assignment.push v }
|
||||
else if x > (← get').assignment.size then
|
||||
modify' fun s => { s with assignment := s.assignment.push 0 }
|
||||
setAssignment x v
|
||||
else
|
||||
throwError "`grind` internal error, variable is already assigned"
|
||||
|
||||
def resolveLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
|
||||
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
|
||||
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
|
||||
if (← p.satisfiedLe) == .false then
|
||||
-- If current assignment does not satisfy the real shadow, we use it even if it is not precise when
|
||||
-- `a₁.natAbs != 1 && a₂.natAbs != 1`
|
||||
(← mkLeCnstr p (.combine c₁ c₂)).assert
|
||||
else
|
||||
assert! a₁.natAbs != 1 && a₂.natAbs != 1
|
||||
throwError "NIY"
|
||||
return some { d, b := -b*a' }
|
||||
|
||||
def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
|
||||
trace[grind.cutsat.conflict] "{← c.pp}"
|
||||
@@ -89,72 +147,267 @@ def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
|
||||
let .add a _ p := c.p | c.throwUnexpected
|
||||
(← mkDvdCnstr (a.gcd d) p (.elim c)).assert
|
||||
|
||||
def decideVar (x : Var) : GoalM Unit := do
|
||||
/--
|
||||
Given a divisibility constraint solution space `s := { b, d }`,
|
||||
and a candidate assignment `v`, we want to find
|
||||
an assignment `w` such that `w ≥ v` such that exists `k`, `w = k*d + b`
|
||||
Thus,
|
||||
- `k*d + b ≥ v`
|
||||
- `k ≥ cdiv (v - b) d`
|
||||
So, we take `w = (cdiv (v - b) d)*d + b`
|
||||
-/
|
||||
def DvdSolution.ge (s : DvdSolution) (v : Int) : Int :=
|
||||
(Int.Linear.cdiv (v - s.b) s.d)*s.d + s.b
|
||||
|
||||
/--
|
||||
Given a divisibility constraint solution space `s := { b, d }`,
|
||||
and a candidate assignment `v`, we want to find
|
||||
an assignment `w` such that `w ≤ v` such that exists `k`, `w = k*d + b`
|
||||
Thus,
|
||||
- `k*d + b ≤ v`
|
||||
- `k ≤ (v - b) / d`
|
||||
So, we take `w = ((v - b) / d)*d + b`
|
||||
-/
|
||||
def DvdSolution.le (s : DvdSolution) (v : Int) : Int :=
|
||||
((v - s.b)/s.d)*s.d + s.b
|
||||
|
||||
def findDiseq? (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
|
||||
(·.2) <$> dvals.find? fun (d, _) =>
|
||||
d.den == 1 && d.num == v
|
||||
|
||||
def inDiseqValues (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Bool :=
|
||||
Option.isSome <| findDiseq? v dvals
|
||||
|
||||
def findRatDiseq? (v : Rat) (dvals : Array (Rat × DiseqCnstr)) : Option DiseqCnstr :=
|
||||
(·.2) <$> dvals.find? fun (d, _) => v == d
|
||||
|
||||
partial def DvdSolution.geAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
|
||||
let v := s.ge v
|
||||
if inDiseqValues v dvals then
|
||||
geAvoiding s (v+1) dvals
|
||||
else
|
||||
v
|
||||
|
||||
partial def DvdSolution.leAvoiding (s : DvdSolution) (v : Int) (dvals : Array (Rat × DiseqCnstr)) : Int :=
|
||||
let v := s.le v
|
||||
if inDiseqValues v dvals then
|
||||
geAvoiding s (v-1) dvals
|
||||
else
|
||||
v
|
||||
|
||||
inductive FindIntValResult where
|
||||
| found (val : Int)
|
||||
| diseq (c : DiseqCnstr)
|
||||
| dvd
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
Tries to find an integer `v` s.t. `lower ≤ v ≤ upper`, `v ∉ dvals`, and `v ∈ s`.
|
||||
Returns `.found v` if result was found, `.dvd` if it failed because of the divisibility constraint,
|
||||
and `.diseq c` because of the disequality constraint `c`.
|
||||
-/
|
||||
partial def findIntVal (s : DvdSolution) (lower : Int) (upper : Int) (dvals : Array (Rat × DiseqCnstr)) : FindIntValResult :=
|
||||
let v := s.ge lower
|
||||
if v > upper then
|
||||
.dvd
|
||||
else
|
||||
go v
|
||||
where
|
||||
go (v : Int) : FindIntValResult :=
|
||||
if let some c := findDiseq? v dvals then
|
||||
let v := s.ge (v+1)
|
||||
if v > upper then .diseq c else go v
|
||||
else
|
||||
.found v
|
||||
|
||||
partial def findRatVal (lower upper : Rat) (diseqVals : Array (Rat × DiseqCnstr)) : Rat :=
|
||||
let v := (lower + upper)/2
|
||||
if (findRatDiseq? v diseqVals).isSome then
|
||||
findRatVal lower v diseqVals
|
||||
else
|
||||
v
|
||||
|
||||
def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
|
||||
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
|
||||
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
|
||||
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
|
||||
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
|
||||
if (← p.satisfiedLe) != .false then
|
||||
return false
|
||||
else
|
||||
let c ← mkLeCnstr p (.combine c₁ c₂)
|
||||
c.assert
|
||||
return true
|
||||
|
||||
def resolveCooperLeft (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-left NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
def resolveCooperRight (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-right NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
def resolveCooper (c₁ c₂ : LeCnstr) : GoalM Unit := do
|
||||
if c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs then
|
||||
resolveCooperLeft c₁ c₂
|
||||
else
|
||||
resolveCooperRight c₁ c₂
|
||||
|
||||
def resolveCooperDvdLeft (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-dvd-left NIY {← c₁.pp} {← c₂.pp} {← c.pp}"
|
||||
|
||||
def resolveCooperDvdRight (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-dvd-right NIY {← c₁.pp} {← c₂.pp} {← c.pp}"
|
||||
|
||||
def resolveCooperDvd (c₁ c₂ : LeCnstr) (c : DvdCnstr) : GoalM Unit := do
|
||||
if c₁.p.leadCoeff.natAbs < c₂.p.leadCoeff.natAbs then
|
||||
resolveCooperDvdLeft c₁ c₂ c
|
||||
else
|
||||
resolveCooperDvdRight c₁ c₂ c
|
||||
|
||||
def resolveCooperDiseq (c₁ : DiseqCnstr) (c₂ : LeCnstr) (_c? : Option DvdCnstr) : GoalM Unit := do
|
||||
throwError "Cooper-diseq NIY {← c₁.pp} {← c₂.pp}"
|
||||
|
||||
/--
|
||||
Given `c₁` of the form `-a₁*x + p₁ ≤ 0`, and `c` of the form `b*x + p ≠ 0`,
|
||||
splits `c` and resolve with `c₁`.
|
||||
Recall that a disequality
|
||||
-/
|
||||
def resolveRatDiseq (c₁ : LeCnstr) (c : DiseqCnstr) : SearchM Unit := do
|
||||
let c ← if c.p.leadCoeff < 0 then
|
||||
mkDiseqCnstr (c.p.mul (-1)) (.neg c)
|
||||
else
|
||||
pure c
|
||||
let fvarId ← if let some fvarId := (← get').diseqSplits.find? c.p then
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, reusing {fvarId.name}"
|
||||
pure fvarId
|
||||
else
|
||||
let fvarId ← mkCase (.diseq c)
|
||||
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, {fvarId.name}"
|
||||
modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
|
||||
pure fvarId
|
||||
let p₂ := c.p.addConst 1
|
||||
let c₂ ← mkLeCnstr p₂ (.expr (mkFVar fvarId))
|
||||
let b ← resolveRealLowerUpperConflict c₁ c₂
|
||||
assert! b
|
||||
|
||||
def processVar (x : Var) : SearchM Unit := do
|
||||
if (← eliminated x) then
|
||||
/-
|
||||
Variable has been eliminated, and will be assigned later after we have assigned
|
||||
variables that have not been eliminated.
|
||||
-/
|
||||
skipAssignment x
|
||||
return ()
|
||||
-- Solution space for divisibility constraint is `x = k*d + b`
|
||||
let dvdSol ← if let some c := (← get').dvds[x]! then
|
||||
if let some solutions ← c.getSolutions? then
|
||||
pure solutions
|
||||
else
|
||||
resolveDvdConflict c
|
||||
return ()
|
||||
else
|
||||
pure {}
|
||||
let lower? ← getBestLower? x
|
||||
let upper? ← getBestUpper? x
|
||||
let dvd? := (← get').dvdCnstrs[x]!
|
||||
match lower?, upper?, dvd? with
|
||||
| none, none, none =>
|
||||
setAssignment x 0
|
||||
| some (lower, _), none, none =>
|
||||
setAssignment x lower
|
||||
| none, some (upper, _), none =>
|
||||
setAssignment x upper
|
||||
| some (lower, c₁), some (upper, c₂), none =>
|
||||
if lower ≤ upper then
|
||||
setAssignment x lower
|
||||
else
|
||||
trace[grind.cutsat.conflict] "{lower} ≤ {← getVar x} ≤ {upper}"
|
||||
resolveLowerUpperConflict c₁ c₂
|
||||
| none, none, some c =>
|
||||
if let some (_, v) ← c.getSolutions? then
|
||||
let diseqVals ← getDiseqValues x
|
||||
match lower?, upper? with
|
||||
| none, none =>
|
||||
let v := dvdSol.geAvoiding 0 diseqVals
|
||||
setAssignment x v
|
||||
| some (lower, _), none =>
|
||||
let lower := lower.ceil
|
||||
let v := dvdSol.geAvoiding lower diseqVals
|
||||
setAssignment x v
|
||||
| none, some (upper, _) =>
|
||||
let upper := upper.floor
|
||||
let v := dvdSol.leAvoiding upper diseqVals
|
||||
setAssignment x v
|
||||
| some (lower, c₁), some (upper, c₂) =>
|
||||
if lower > upper then
|
||||
let .true ← resolveRealLowerUpperConflict c₁ c₂
|
||||
| throwError "`grind` internal error, conflict resolution failed"
|
||||
return ()
|
||||
-- `lower ≤ upper` here
|
||||
if lower.ceil > upper.floor then
|
||||
if (← resolveRealLowerUpperConflict c₁ c₂) then
|
||||
-- Resolved conflict using "real" shadow
|
||||
return ()
|
||||
if !(← isApprox) then
|
||||
resolveCooper c₁ c₂
|
||||
return ()
|
||||
let r := findIntVal dvdSol lower.ceil upper.floor diseqVals
|
||||
if let .found v := r then
|
||||
setAssignment x v
|
||||
return ()
|
||||
if (← isApprox) then
|
||||
if lower < upper then
|
||||
setAssignment x <| findRatVal lower upper diseqVals
|
||||
else if let some c := findRatDiseq? lower diseqVals then
|
||||
resolveRatDiseq c₁ c
|
||||
else
|
||||
setAssignment x lower
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| some (lower, _), none, some c =>
|
||||
if let some (d, b) ← c.getSolutions? then
|
||||
/-
|
||||
- `x ≥ lower ∧ x = k*d + b`
|
||||
- `k*d + b ≥ lower`
|
||||
- `k ≥ cdiv (lower - b) d`
|
||||
- So, we take `x = (cdiv (lower - b) d)*d + b`
|
||||
-/
|
||||
setAssignment x ((Int.Linear.cdiv (lower - b) d)*d + b)
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| none, some (upper, _), some c =>
|
||||
if let some (d, b) ← c.getSolutions? then
|
||||
/-
|
||||
- `x ≤ upper ∧ x = k*d + b`
|
||||
- `k*d + b ≤ upper`
|
||||
- `k ≤ (upper - b)/d`
|
||||
- So, we take `x = ((upper - b)/d)*d + b`
|
||||
-/
|
||||
setAssignment x (((upper - b)/d)*d + b)
|
||||
else
|
||||
resolveDvdConflict c
|
||||
| _, _, _ =>
|
||||
-- TODO: cases containing a divisibility constraint.
|
||||
-- TODO: remove the following
|
||||
setAssignment x 0
|
||||
match r with
|
||||
| .dvd => resolveCooperDvd c₁ c₂ (← get').dvds[x]!.get!
|
||||
| .diseq c => resolveCooperDiseq c c₂ (← get').dvds[x]!
|
||||
| _ => unreachable!
|
||||
|
||||
/-- Returns `true` if we already have a complete assignment / model. -/
|
||||
def hasAssignment : GoalM Bool := do
|
||||
return (← get').vars.size == (← get').assignment.size
|
||||
|
||||
private def isDone : GoalM Bool := do
|
||||
if (← hasAssignment) then
|
||||
private def findCase (decVars : FVarIdSet) : SearchM Case := do
|
||||
repeat
|
||||
let numCases := (← get).cases.size
|
||||
assert! numCases > 0
|
||||
let case := (← get).cases[numCases-1]!
|
||||
modify fun s => { s with cases := s.cases.pop }
|
||||
if decVars.contains case.fvarId then
|
||||
return case
|
||||
-- Conflict does not depend on this case.
|
||||
trace[grind.debug.cutsat.backtrack] "skipping {case.fvarId.name}"
|
||||
unreachable!
|
||||
|
||||
def resolveConflict (h : UnsatProof) : SearchM Bool := do
|
||||
let decVars := h.collectDecVars.run (← get).decVars
|
||||
if decVars.isEmpty then
|
||||
closeGoal (← h.toExprProof)
|
||||
return false
|
||||
let c ← findCase decVars
|
||||
modify' fun _ => c.saved
|
||||
match c.kind with
|
||||
| .diseq c₁ =>
|
||||
let decVars := decVars.erase c.fvarId |>.toArray
|
||||
let p' := c₁.p.mul (-1) |>.addConst 1
|
||||
let c' ← mkLeCnstr p' (.ofDiseqSplit c₁ c.fvarId h decVars)
|
||||
trace[grind.debug.cutsat.backtrack] "resolved diseq split: {← c'.pp}"
|
||||
c'.assert
|
||||
return true
|
||||
if (← inconsistent) then
|
||||
return true
|
||||
return false
|
||||
| _ => throwError "NIY resolve conflict"
|
||||
|
||||
/-- Search for an assignment/model for the linear constraints. -/
|
||||
def searchAssigment : GoalM Unit := do
|
||||
def searchAssigmentMain : SearchM Unit := do
|
||||
repeat
|
||||
if (← isDone) then
|
||||
if (← hasAssignment) then
|
||||
return ()
|
||||
if (← isInconsistent) then
|
||||
-- `grind` state is inconsistent
|
||||
return ()
|
||||
if let some c := (← get').conflict? then
|
||||
unless (← resolveConflict c) do
|
||||
return ()
|
||||
let x : Var := (← get').assignment.size
|
||||
decideVar x
|
||||
processVar x
|
||||
|
||||
def traceModel : GoalM Unit := do
|
||||
if (← isTracingEnabledFor `grind.cutsat.model) then
|
||||
for (x, v) in (← mkModel (← get)) do
|
||||
trace[grind.cutsat.model] "{quoteIfNotAtom x} := {v}"
|
||||
|
||||
def searchAssigment : GoalM Unit := do
|
||||
-- TODO: .int case
|
||||
-- TODO:
|
||||
searchAssigmentMain .rat |>.run' {}
|
||||
assignElimVars
|
||||
traceModel
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
83
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean
Normal file
83
src/Lean/Meta/Tactic/Grind/Arith/Cutsat/SearchM.lean
Normal file
@@ -0,0 +1,83 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
|
||||
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
/--
|
||||
In principle, we only need to support two kinds of case split.
|
||||
- Disequalities.
|
||||
- Cooper-Left, but we have 4 different variants of this one.
|
||||
-/
|
||||
inductive CaseKind where
|
||||
| diseq (d : DiseqCnstr)
|
||||
| copperLeft
|
||||
| copperDvdLeft
|
||||
| cooperRight
|
||||
| cooperDvdRight
|
||||
deriving Inhabited
|
||||
|
||||
structure Case where
|
||||
kind : CaseKind
|
||||
/--
|
||||
Decision variable used to represent the case-split.
|
||||
For example, suppose we are splitting on `p ≠ 0`. Then,
|
||||
we create a decision variable `h : p + 1 ≤ 0`
|
||||
-/
|
||||
fvarId : FVarId
|
||||
/--
|
||||
Snapshot of the cutsat state for backtracking purposes.
|
||||
We do not use a trail stack.
|
||||
-/
|
||||
saved : State
|
||||
deriving Inhabited
|
||||
|
||||
inductive Search.Kind where
|
||||
| /--
|
||||
Allow variables to be assigned to rational numbers during model
|
||||
construction.
|
||||
-/
|
||||
rat
|
||||
| /--
|
||||
Variables must be assigned to integer numbers.
|
||||
Cooper case splits are required in this mode.
|
||||
-/
|
||||
int
|
||||
deriving Inhabited, BEq
|
||||
|
||||
/--
|
||||
State of the model search procedure.
|
||||
-/
|
||||
structure Search.State where
|
||||
/-- Decision stack (aka case-split stack) -/
|
||||
cases : PArray Case := {}
|
||||
/-- `precise := false` if not all constraints were satisfied during the search. -/
|
||||
precise : Bool := true
|
||||
/-- Set of decision variables in `cases`. -/
|
||||
decVars : FVarIdSet := {}
|
||||
|
||||
abbrev SearchM := ReaderT Search.Kind (StateRefT Search.State GoalM)
|
||||
|
||||
/-- Returns `true` if approximations are allowed. -/
|
||||
def isApprox : SearchM Bool :=
|
||||
return (← read) == .rat
|
||||
|
||||
/-- Sets `precise` to `false` to indicate that some constraint was not satisfied. -/
|
||||
def setImprecise : SearchM Unit := do
|
||||
modify fun s => { s with precise := false }
|
||||
|
||||
def mkCase (kind : CaseKind) : SearchM FVarId := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
let saved ← get'
|
||||
modify fun s => { s with
|
||||
cases := s.cases.push { saved, fvarId, kind }
|
||||
decVars := s.decVars.insert fvarId
|
||||
}
|
||||
modify' fun s => { s with caseSplits := true }
|
||||
return fvarId
|
||||
|
||||
end Lean.Meta.Grind.Arith.Cutsat
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Linear
|
||||
import Std.Internal.Rat
|
||||
import Lean.Data.PersistentArray
|
||||
import Lean.Meta.Tactic.Grind.ENodeKey
|
||||
import Lean.Meta.Tactic.Grind.Arith.Util
|
||||
@@ -12,6 +13,57 @@ import Lean.Meta.Tactic.Grind.Arith.Util
|
||||
namespace Lean.Meta.Grind.Arith.Cutsat
|
||||
|
||||
export Int.Linear (Var Poly)
|
||||
export Std.Internal (Rat)
|
||||
|
||||
deriving instance Hashable for Poly
|
||||
|
||||
/-!
|
||||
This module implements a model-based decision procedure for linear integer arithmetic,
|
||||
inspired by Section 4 of "Cutting to the Chase: Solving Linear Integer Arithmetic".
|
||||
Our implementation includes several enhancements and modifications:
|
||||
Key Features:
|
||||
- Extended constraint support (equality and disequality)
|
||||
- Optimized encoding of `Cooper-Left` rule using "big"-disjunction instead of fresh variables
|
||||
- Decision variable tracking for case splits (disequalities, `Cooper-Left`, `Cooper-Right`)
|
||||
|
||||
Constraint Types:
|
||||
We handle four categories of linear polynomial constraints (where p is a linear polynomial):
|
||||
1. Equality: `p = 0`
|
||||
2. Divisibility: `d ∣ p`
|
||||
3. Inequality: `p ≤ 0`
|
||||
4. Disequality: `p ≠ 0`
|
||||
|
||||
Implementation Details:
|
||||
- Polynomials use `Int.Linear.Poly` with sorted linear monomials (leading monomial contains max variable)
|
||||
- Equalities are eliminated eagerly
|
||||
- Divisibility constraints are maintained in solved form (one constraint per variable) using `Div-Solve`
|
||||
|
||||
Model Construction:
|
||||
The procedure builds a model incrementally, resolving conflicts through constraint generation.
|
||||
For example:
|
||||
Given a partial model `{x := 1}` and constraint `3 ∣ 3*y + x + 1`:
|
||||
- Cannot extend to `y` because `3 ∣ 3*y + 2` is unsatisfiable
|
||||
- Generate implied constraint `3 ∣ x + 1`
|
||||
- Force model update for `x`
|
||||
|
||||
Variable Assignment:
|
||||
When assigning a variable `y`, we consider:
|
||||
- Best upper and lower bounds (inequalities)
|
||||
- Divisibility constraint
|
||||
- Disequality constraints
|
||||
`Cooper-Left` and `Cooper-Right` rules handle the combination of inequalities and divisibility.
|
||||
For unsatisfiable disequalities p ≠ 0, we generate case split: `p + 1 ≤ 0 ∨ -p + 1 ≤ 0`
|
||||
|
||||
Contradiction Handling:
|
||||
- Check dependency on decision variables
|
||||
- If independent, use contradiction to close current grind goal
|
||||
- Otherwise, trigger backtracking
|
||||
|
||||
Optimization:
|
||||
We employ rational approximation for model construction:
|
||||
- Continue with rational solutions when integer solutions aren't immediately found
|
||||
- Helps identify simpler unsatisfiability proofs before full integer model construction
|
||||
-/
|
||||
|
||||
/-
|
||||
Remark: we will not define a parent structure `Cnstr` with the common
|
||||
@@ -19,6 +71,20 @@ fields until the compiler provides support for avoiding the performance overhead
|
||||
-/
|
||||
|
||||
mutual
|
||||
/-- A equality constraint and its justification/proof. -/
|
||||
structure EqCnstr where
|
||||
p : Poly
|
||||
h : EqCnstrProof
|
||||
id : Nat
|
||||
|
||||
inductive EqCnstrProof where
|
||||
| expr (h : Expr)
|
||||
| core (p₁ p₂ : Poly) (h : Expr)
|
||||
| norm (c : EqCnstr)
|
||||
| divCoeffs (c : EqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
|
||||
| ofLeGe (c₁ : LeCnstr) (c₂ : LeCnstr)
|
||||
|
||||
/-- A divisibility constraint and its justification/proof. -/
|
||||
structure DvdCnstr where
|
||||
d : Int
|
||||
@@ -37,6 +103,7 @@ inductive DvdCnstrProof where
|
||||
| ofEq (x : Var) (c : EqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : DvdCnstr)
|
||||
|
||||
/-- An inequality constraint and its justification/proof. -/
|
||||
structure LeCnstr where
|
||||
p : Poly
|
||||
h : LeCnstrProof
|
||||
@@ -49,20 +116,23 @@ inductive LeCnstrProof where
|
||||
| divCoeffs (c : LeCnstr)
|
||||
| combine (c₁ c₂ : LeCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : LeCnstr)
|
||||
| ofLeDiseq (c₁ : LeCnstr) (c₂ : DiseqCnstr)
|
||||
| ofDiseqSplit (c₁ : DiseqCnstr) (decVar : FVarId) (h : UnsatProof) (decVars : Array FVarId)
|
||||
-- TODO: missing constructors
|
||||
|
||||
structure EqCnstr where
|
||||
/-- A disequality constraint and its justification/proof. -/
|
||||
structure DiseqCnstr where
|
||||
p : Poly
|
||||
h : EqCnstrProof
|
||||
h : DiseqCnstrProof
|
||||
id : Nat
|
||||
|
||||
inductive EqCnstrProof where
|
||||
inductive DiseqCnstrProof where
|
||||
| expr (h : Expr)
|
||||
| core (p₁ p₂ : Poly) (h : Expr)
|
||||
| norm (c : EqCnstr)
|
||||
| divCoeffs (c : EqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
|
||||
end
|
||||
| norm (c : DiseqCnstr)
|
||||
| divCoeffs (c : DiseqCnstr)
|
||||
| neg (c : DiseqCnstr)
|
||||
| subst (x : Var) (c₁ : EqCnstr) (c₂ : DiseqCnstr)
|
||||
|
||||
/--
|
||||
A proof of `False`.
|
||||
@@ -72,6 +142,12 @@ inductive UnsatProof where
|
||||
| dvd (c : DvdCnstr)
|
||||
| le (c : LeCnstr)
|
||||
| eq (c : EqCnstr)
|
||||
| diseq (c : DiseqCnstr)
|
||||
|
||||
end
|
||||
|
||||
instance : Inhabited DvdCnstr where
|
||||
default := { d := 0, p := .num 0, h := .expr default, id := 0 }
|
||||
|
||||
abbrev VarSet := RBTree Var compare
|
||||
|
||||
@@ -84,18 +160,23 @@ structure State where
|
||||
/--
|
||||
Mapping from variables to divisibility constraints. Recall that we keep the divisibility constraint in solved form.
|
||||
Thus, we have at most one divisibility per variable. -/
|
||||
dvdCnstrs : PArray (Option DvdCnstr) := {}
|
||||
dvds : PArray (Option DvdCnstr) := {}
|
||||
/--
|
||||
Mapping from variables to their "lower" bounds. We say a relational constraint `c` is a lower bound for a variable `x`
|
||||
if `x` is the maximal variable in `c`, `c.isLe`, and `x` coefficient in `c` is negative.
|
||||
if `x` is the maximal variable in `c`, and `x` coefficient in `c` is negative.
|
||||
-/
|
||||
lowers : PArray (PArray LeCnstr) := {}
|
||||
/--
|
||||
Mapping from variables to their "upper" bounds. We say a relational constraint `c` is a upper bound for a variable `x`
|
||||
if `x` is the maximal variable in `c`, `c.isLe`, and `x` coefficient in `c` is positive.
|
||||
if `x` is the maximal variable in `c`, and `x` coefficient in `c` is positive.
|
||||
-/
|
||||
uppers : PArray (PArray LeCnstr) := {}
|
||||
/--
|
||||
Mapping from variables to their disequalities. We say a disequality constraint `c` is a disequality for a variable `x`
|
||||
if `x` is the maximal variable in `c`.
|
||||
-/
|
||||
diseqs : PArray (PArray DiseqCnstr) := {}
|
||||
/--
|
||||
Mapping from variable to equation constraint used to eliminate it. `solved` variables should not occur in
|
||||
`dvdCnstrs`, `lowers`, or `uppers`.
|
||||
-/
|
||||
@@ -117,14 +198,29 @@ structure State where
|
||||
-/
|
||||
occurs : PArray VarSet := {}
|
||||
/-- Partial assignment being constructed by cutsat. -/
|
||||
assignment : PArray Int := {}
|
||||
assignment : PArray Rat := {}
|
||||
/-- Next unique id for a constraint. -/
|
||||
nextCnstrId : Nat := 0
|
||||
/--
|
||||
`caseSplits` is `true` if cutsat is searching for model and already performed case splits.
|
||||
This information is used to decide whether a conflict should immediately close the
|
||||
current `grind` goal or not.
|
||||
-/
|
||||
caseSplits : Bool := false
|
||||
/--
|
||||
`conflict?` is `some ..` if a contradictory constraint was derived.
|
||||
This field is only set when `caseSplits` is `true`. Otherwise, we
|
||||
can convert `UnsatProof` into a Lean term and close the current `grind` goal.
|
||||
-/
|
||||
conflict? : Option UnsatProof := none
|
||||
/--
|
||||
Cache decision variables used when splitting on disequalities.
|
||||
This is necessary because the same disequality may be in different conflicts.
|
||||
-/
|
||||
diseqSplits : PHashMap Poly FVarId := {}
|
||||
|
||||
/-
|
||||
TODO: support for storing
|
||||
- Disjuctions: they come from conflict resolution, and disequalities.
|
||||
- Disequalities.
|
||||
- Linear integer terms appearing in the main module, and model-based equality propagation.
|
||||
TODO: Model-based theory combination.
|
||||
-/
|
||||
deriving Inhabited
|
||||
|
||||
|
||||
@@ -46,9 +46,8 @@ def get' : GoalM State := do
|
||||
|
||||
/-- Returns `true` if the cutsat state is inconsistent. -/
|
||||
def inconsistent : GoalM Bool := do
|
||||
-- TODO: we will have a nested backtracking search in cutsat
|
||||
-- and this function will have to be refined.
|
||||
isInconsistent
|
||||
if (← isInconsistent) then return true
|
||||
return (← get').conflict?.isSome
|
||||
|
||||
def getVars : GoalM (PArray Expr) :=
|
||||
return (← get').vars
|
||||
@@ -65,11 +64,22 @@ def mkCnstrId : GoalM Nat := do
|
||||
modify' fun s => { s with nextCnstrId := id + 1 }
|
||||
return id
|
||||
|
||||
private partial def shrink (a : PArray Int) (sz : Nat) : PArray Int :=
|
||||
if a.size > sz then
|
||||
shrink a.pop sz
|
||||
else
|
||||
a
|
||||
def mkEqCnstr (p : Poly) (h : EqCnstrProof) : GoalM EqCnstr := do
|
||||
return { p, h, id := (← mkCnstrId) }
|
||||
|
||||
@[extern "lean_grind_cutsat_assert_eq"] -- forward definition
|
||||
opaque EqCnstr.assert (c : EqCnstr) : GoalM Unit
|
||||
|
||||
-- TODO: PArray.shrink and PArray.resize
|
||||
|
||||
partial def shrink (a : PArray Rat) (sz : Nat) : PArray Rat :=
|
||||
if a.size > sz then shrink a.pop sz else a
|
||||
|
||||
partial def resize (a : PArray Rat) (sz : Nat) : PArray Rat :=
|
||||
if a.size > sz then shrink a sz else go a
|
||||
where
|
||||
go (a : PArray Rat) : PArray Rat :=
|
||||
if a.size < sz then go (a.push 0) else a
|
||||
|
||||
/-- Resets the assingment of any variable bigger or equal to `x`. -/
|
||||
def resetAssignmentFrom (x : Var) : GoalM Unit := do
|
||||
@@ -106,6 +116,20 @@ def DvdCnstr.denoteExpr (c : DvdCnstr) : GoalM Expr := do
|
||||
def DvdCnstr.throwUnexpected (c : DvdCnstr) : GoalM α := do
|
||||
throwError "`grind` internal error, unexpected{indentD (← c.pp)} "
|
||||
|
||||
def DiseqCnstr.isTrivial (c : DiseqCnstr) : Bool :=
|
||||
match c.p with
|
||||
| .num k => k != 0
|
||||
| _ => c.p.getConst % c.p.gcdCoeffs' != 0
|
||||
|
||||
def DiseqCnstr.pp (c : DiseqCnstr) : GoalM MessageData := do
|
||||
return m!"{← c.p.pp} ≠ 0"
|
||||
|
||||
def DiseqCnstr.throwUnexpected (c : DiseqCnstr) : GoalM α := do
|
||||
throwError "`grind` internal error, unexpected{indentD (← c.pp)}"
|
||||
|
||||
def DiseqCnstr.denoteExpr (c : DiseqCnstr) : GoalM Expr := do
|
||||
return mkNot (mkIntEq (← c.p.denoteExpr') (mkIntLit 0))
|
||||
|
||||
def LeCnstr.isTrivial (c : LeCnstr) : Bool :=
|
||||
match c.p with
|
||||
| .num k => k ≤ 0
|
||||
@@ -185,6 +209,7 @@ abbrev caching (id : Nat) (k : ProofM Expr) : ProofM Expr := do
|
||||
abbrev DvdCnstr.caching (c : DvdCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
|
||||
abbrev LeCnstr.caching (c : LeCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
|
||||
abbrev EqCnstr.caching (c : EqCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
|
||||
abbrev DiseqCnstr.caching (c : DiseqCnstr) (k : ProofM Expr) : ProofM Expr := Cutsat.caching c.id k
|
||||
|
||||
abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
|
||||
withLetDecl `ctx (mkApp (mkConst ``RArray) (mkConst ``Int)) (← toContextExpr) fun ctx => do
|
||||
@@ -195,9 +220,9 @@ abbrev withProofContext (x : ProofM Expr) : GoalM Expr := do
|
||||
Tries to evaluate the polynomial `p` using the partial model/assignment built so far.
|
||||
The result is `none` if the polynomial contains variables that have not been assigned.
|
||||
-/
|
||||
def _root_.Int.Linear.Poly.eval? (p : Poly) : GoalM (Option Int) := do
|
||||
def _root_.Int.Linear.Poly.eval? (p : Poly) : GoalM (Option Rat) := do
|
||||
let a := (← get').assignment
|
||||
let rec go (v : Int) : Poly → Option Int
|
||||
let rec go (v : Rat) : Poly → Option Rat
|
||||
| .num k => some (v + k)
|
||||
| .add k x p =>
|
||||
if _ : x < a.size then
|
||||
@@ -218,7 +243,8 @@ Returns `.true` if `c` is satisfied by the current partial model,
|
||||
-/
|
||||
def DvdCnstr.satisfied (c : DvdCnstr) : GoalM LBool := do
|
||||
let some v ← c.p.eval? | return .undef
|
||||
return decide (c.d ∣ v) |>.toLBool
|
||||
if v.den != 1 then return .false
|
||||
return decide (c.d ∣ v.num) |>.toLBool
|
||||
|
||||
def _root_.Int.Linear.Poly.satisfiedLe (p : Poly) : GoalM LBool := do
|
||||
let some v ← p.eval? | return .undef
|
||||
@@ -231,6 +257,14 @@ Returns `.true` if `c` is satisfied by the current partial model,
|
||||
def LeCnstr.satisfied (c : LeCnstr) : GoalM LBool := do
|
||||
c.p.satisfiedLe
|
||||
|
||||
/--
|
||||
Returns `.true` if `c` is satisfied by the current partial model,
|
||||
`.undef` if `c` contains unassigned variables, and `.false` otherwise.
|
||||
-/
|
||||
def DiseqCnstr.satisfied (c : DiseqCnstr) : GoalM LBool := do
|
||||
let some v ← c.p.eval? | return .undef
|
||||
return v != 0 |>.toLBool
|
||||
|
||||
/--
|
||||
Given a polynomial `p`, returns `some (x, k, c)` if `p` contains the monomial `k*x`,
|
||||
and `x` has been eliminated using the equality `c`.
|
||||
|
||||
@@ -18,9 +18,10 @@ def mkVar (expr : Expr) : GoalM Var := do
|
||||
modify' fun s => { s with
|
||||
vars := s.vars.push expr
|
||||
varMap := s.varMap.insert { expr } var
|
||||
dvdCnstrs := s.dvdCnstrs.push none
|
||||
dvds := s.dvds.push none
|
||||
lowers := s.lowers.push {}
|
||||
uppers := s.uppers.push {}
|
||||
diseqs := s.diseqs.push {}
|
||||
occurs := s.occurs.push {}
|
||||
elimEqs := s.elimEqs.push none
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Grind.Arith.Offset.Model
|
||||
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
|
||||
|
||||
@@ -112,22 +112,31 @@ private def propagateOffsetEq (rhsRoot lhsRoot : ENode) : GoalM Unit := do
|
||||
/--
|
||||
Helper function for combining `ENode.cutsat?` fields and propagating equalities
|
||||
to the offset constraint module.
|
||||
It returns a set of parents that should be traversed for disequality propagation.
|
||||
-/
|
||||
private def propagateCutsatEq (rhsRoot lhsRoot : ENode) : GoalM Unit := do
|
||||
private def propagateCutsatEq (rhsRoot lhsRoot : ENode) : GoalM ParentSet := do
|
||||
match lhsRoot.cutsat? with
|
||||
| some lhsCutsat =>
|
||||
if let some rhsCutsat := rhsRoot.cutsat? then
|
||||
Arith.Cutsat.processNewEq lhsCutsat rhsCutsat
|
||||
return {}
|
||||
else if isIntNum rhsRoot.self then
|
||||
Arith.Cutsat.processNewEqLit lhsCutsat rhsRoot.self
|
||||
return {}
|
||||
else
|
||||
-- We have to retrieve the node because other fields have been updated
|
||||
let rhsRoot ← getENode rhsRoot.self
|
||||
setENode rhsRoot.self { rhsRoot with cutsat? := lhsCutsat }
|
||||
getParents rhsRoot.self
|
||||
| none =>
|
||||
if isIntNum lhsRoot.self then
|
||||
if let some rhsCutsat := rhsRoot.cutsat? then
|
||||
Arith.Cutsat.processNewEqLit rhsCutsat lhsRoot.self
|
||||
if isIntNum lhsRoot.self then
|
||||
Arith.Cutsat.processNewEqLit rhsCutsat lhsRoot.self
|
||||
return {}
|
||||
else
|
||||
getParents lhsRoot.self
|
||||
else
|
||||
return {}
|
||||
|
||||
/--
|
||||
Tries to apply beta-reductiong using the parent applications of the functions in `fns` with
|
||||
@@ -225,15 +234,16 @@ where
|
||||
}
|
||||
propagateBeta lams₁ fns₁
|
||||
propagateBeta lams₂ fns₂
|
||||
propagateOffsetEq rhsRoot lhsRoot
|
||||
let parentsToPropagateDiseqs ← propagateCutsatEq rhsRoot lhsRoot
|
||||
resetParentsOf lhsRoot.self
|
||||
copyParentsTo parents rhsNode.root
|
||||
unless (← isInconsistent) do
|
||||
updateMT rhsRoot.self
|
||||
propagateOffsetEq rhsRoot lhsRoot
|
||||
propagateCutsatEq rhsRoot lhsRoot
|
||||
unless (← isInconsistent) do
|
||||
for parent in parents do
|
||||
propagateUp parent
|
||||
propagateCutsatDiseqs parentsToPropagateDiseqs
|
||||
|
||||
updateRoots (lhs : Expr) (rootNew : Expr) : GoalM Unit := do
|
||||
traverseEqc lhs fun n =>
|
||||
|
||||
81
src/Lean/Meta/Tactic/Grind/Diseq.lean
Normal file
81
src/Lean/Meta/Tactic/Grind/Diseq.lean
Normal file
@@ -0,0 +1,81 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Grind.Lemmas
|
||||
import Lean.Meta.Tactic.Grind.Types
|
||||
|
||||
namespace Lean.Meta.Grind
|
||||
|
||||
/--
|
||||
Returns `some (c = d)` if
|
||||
- `c = d` and `False` are in the same equivalence class, and
|
||||
- `a` (`b`) and `c` are in the same equivalence class, and
|
||||
- `b` (`a`) and `d` are in the same equivalence class.
|
||||
Otherwise return `none`.
|
||||
|
||||
Remark `a` and `b` are assumed to have the same type.
|
||||
-/
|
||||
private def getDiseqFor? (a b : Expr) : GoalM (Option Expr) := do
|
||||
/-
|
||||
In Z3, we use the congruence table to find equalities more efficiently,
|
||||
but this optimization would be more complicated here because equalities have
|
||||
the type as an implicit argument, and `grind`s congruence table assumes it is
|
||||
hash-consed and canonicalized. So, we use the "slower" approach of visiting
|
||||
parents.
|
||||
-/
|
||||
let aRoot ← getRoot a
|
||||
let bRoot ← getRoot b
|
||||
let aParents ← getParents aRoot
|
||||
let bParents ← getParents bRoot
|
||||
if aParents.size ≤ bParents.size then
|
||||
go aParents
|
||||
else
|
||||
go bParents
|
||||
where
|
||||
go (parents : ParentSet) : GoalM (Option Expr) := do
|
||||
for parent in parents do
|
||||
let_expr Eq α c d := parent | continue
|
||||
if (← isEqFalse parent) then
|
||||
-- Remark: we expect `hasType` test to seldom fail, but it can happen because of
|
||||
-- heterogeneous equalities
|
||||
if (← isEqv a c <&&> isEqv b d <&&> hasType a α) then
|
||||
return some parent
|
||||
if (← isEqv a d <&&> isEqv b c <&&> hasType a α) then
|
||||
return some parent
|
||||
return none
|
||||
|
||||
/--
|
||||
Returns `true` if `a` and `b` are known to be disequal.
|
||||
See `getDiseqFor?`
|
||||
-/
|
||||
def isDiseq (a b : Expr) : GoalM Bool := do
|
||||
return (← getDiseqFor? a b).isSome
|
||||
|
||||
/--
|
||||
Returns a proof for `true` if `a` and `b` are known to be disequal.
|
||||
See `getDiseqFor?`
|
||||
-/
|
||||
def mkDiseqProof? (a b : Expr) : GoalM (Option Expr) := do
|
||||
let some eq ← getDiseqFor? a b | return none
|
||||
let_expr f@Eq α c d := eq | unreachable!
|
||||
let u := f.constLevels!
|
||||
let h ← mkOfEqFalse (← mkEqFalseProof eq)
|
||||
let (c, d, h) ← if (← isEqv a c <&&> isEqv b d) then
|
||||
pure (c, d, h)
|
||||
else
|
||||
pure (d, c, mkApp4 (mkConst ``Ne.symm u) α c d h)
|
||||
-- We have `a = c` and `b = d`
|
||||
let h ← if isSameExpr a c then
|
||||
pure h
|
||||
else
|
||||
pure <| mkApp6 (mkConst ``Grind.ne_of_ne_of_eq_left u) α a c d (← mkEqProof a c) h
|
||||
-- `h : a ≠ d
|
||||
if isSameExpr b d then
|
||||
return h
|
||||
else
|
||||
return mkApp6 (mkConst ``Grind.ne_of_ne_of_eq_right u) α b a d (← mkEqProof b d) h
|
||||
|
||||
end Lean.Meta.Grind
|
||||
@@ -127,6 +127,18 @@ private def ppOffset : M Unit := do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `offset } "Assignment satisfying offset contraints" ms
|
||||
|
||||
private def ppCutsat : M Unit := do
|
||||
let goal ← read
|
||||
let s := goal.arith.cutsat
|
||||
let nodes := s.varMap
|
||||
if nodes.isEmpty then return ()
|
||||
let model ← Arith.Cutsat.mkModel goal
|
||||
if model.isEmpty then return ()
|
||||
let mut ms := #[]
|
||||
for (e, val) in model do
|
||||
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
|
||||
pushMsg <| .trace { cls := `cutsat } "Assignment satisfying integer contraints" ms
|
||||
|
||||
private def ppThresholds (c : Grind.Config) : M Unit := do
|
||||
let goal ← read
|
||||
let maxGen := goal.enodes.foldl (init := 0) fun g _ n => Nat.max g n.generation
|
||||
@@ -165,6 +177,7 @@ where
|
||||
ppCasesTrace
|
||||
ppActiveTheoremPatterns
|
||||
ppOffset
|
||||
ppCutsat
|
||||
ppThresholds config
|
||||
|
||||
end Lean.Meta.Grind
|
||||
|
||||
@@ -146,6 +146,7 @@ builtin_grind_propagator propagateEqDown ↓Eq := fun e => do
|
||||
pushEq a b <| mkOfEqTrueCore e (← mkEqTrueProof e)
|
||||
else if (← isEqFalse e) then
|
||||
let_expr Eq α lhs rhs := e | return ()
|
||||
propagateCutsatDiseq lhs rhs
|
||||
let thms ← getExtTheorems α
|
||||
if !thms.isEmpty then
|
||||
/-
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Grind.Tactics
|
||||
import Init.Data.Queue
|
||||
import Std.Data.TreeSet
|
||||
import Lean.Util.ShareCommon
|
||||
import Lean.HeadIndex
|
||||
import Lean.Meta.Basic
|
||||
@@ -396,7 +397,7 @@ instance : BEq (CongrKey enodes) where
|
||||
abbrev CongrTable (enodes : ENodeMap) := PHashSet (CongrKey enodes)
|
||||
|
||||
-- Remark: we cannot use pointer addresses here because we have to traverse the tree.
|
||||
abbrev ParentSet := RBTree Expr Expr.quickComp
|
||||
abbrev ParentSet := Std.TreeSet Expr Expr.quickComp
|
||||
abbrev ParentMap := PHashMap ENodeKey ParentSet
|
||||
|
||||
/--
|
||||
@@ -865,9 +866,16 @@ opaque Arith.Cutsat.processNewEq (a b : Expr) : GoalM Unit
|
||||
Notifies the cutsat module that `a = k` where
|
||||
`a` is term that has been internalized by this module, and `k` is a numeral.
|
||||
-/
|
||||
@[extern "lean_process_new_cutsat_lit"] -- forward definition
|
||||
@[extern "lean_process_cutsat_eq_lit"] -- forward definition
|
||||
opaque Arith.Cutsat.processNewEqLit (a k : Expr) : GoalM Unit
|
||||
|
||||
/--
|
||||
Notifies the cutsat module that `a ≠ b` where
|
||||
`a` and `b` are terms that have been internalized by this module.
|
||||
-/
|
||||
@[extern "lean_process_cutsat_diseq"] -- forward definition
|
||||
opaque Arith.Cutsat.processNewDiseq (a b : Expr) : GoalM Unit
|
||||
|
||||
/-- Returns `true` if `e` is a nonegative numeral and has type `Int`. -/
|
||||
def isNonnegIntNum (e : Expr) : Bool := Id.run do
|
||||
let_expr OfNat.ofNat _ _ inst := e | false
|
||||
@@ -882,6 +890,47 @@ def isIntNum (e : Expr) : Bool :=
|
||||
isNonnegIntNum e
|
||||
| _ => isNonnegIntNum e
|
||||
|
||||
/--
|
||||
Returns `true` if type of `t` is definitionally equal to `α`
|
||||
-/
|
||||
def hasType (t α : Expr) : MetaM Bool :=
|
||||
withDefault do isDefEq (← inferType t) α
|
||||
|
||||
/--
|
||||
For each equality `b = c` in `parents`, executes `k b c` IF
|
||||
- `b = c` is equal to `False`, and
|
||||
-/
|
||||
@[inline] def forEachDiseq (parents : ParentSet) (k : (lhs : Expr) → (rhs : Expr) → GoalM Unit) : GoalM Unit := do
|
||||
for parent in parents do
|
||||
let_expr Eq _ b c := parent | continue
|
||||
if (← isEqFalse parent) then
|
||||
k b c
|
||||
|
||||
/--
|
||||
Given `lhs` and `rhs` that are known to be disequal, checks whether
|
||||
`lhs` and `rhs` have cutsat terms `e₁` and `e₂` attached to them,
|
||||
and invokes process `Arith.Cutsat.processNewDiseq e₁ e₂`
|
||||
-/
|
||||
def propagateCutsatDiseq (lhs rhs : Expr) : GoalM Unit := do
|
||||
let some lhs ← get? lhs | return ()
|
||||
let some rhs ← get? rhs | return ()
|
||||
-- Recall that core can take care of disequalities of the form `1≠2`.
|
||||
unless isIntNum lhs && isIntNum rhs do
|
||||
Arith.Cutsat.processNewDiseq lhs rhs
|
||||
where
|
||||
get? (a : Expr) : GoalM (Option Expr) := do
|
||||
let root ← getRootENode a
|
||||
if isIntNum root.self then
|
||||
return some root.self
|
||||
return root.cutsat?
|
||||
|
||||
/--
|
||||
Traverses disequalities in `parents`, and propagate the ones relevant to the
|
||||
cutsat module.
|
||||
-/
|
||||
def propagateCutsatDiseqs (parents : ParentSet) : GoalM Unit := do
|
||||
forEachDiseq parents propagateCutsatDiseq
|
||||
|
||||
/--
|
||||
Marks `e` as a term of interest to the cutsat module.
|
||||
If the root of `e`s equivalence class has already a term of interest,
|
||||
@@ -895,6 +944,7 @@ def markAsCutsatTerm (e : Expr) : GoalM Unit := do
|
||||
Arith.Cutsat.processNewEqLit e root.self
|
||||
else
|
||||
setENode root.self { root with cutsat? := some e }
|
||||
propagateCutsatDiseqs (← getParents root.self)
|
||||
|
||||
/-- Returns `true` is `e` is the root of its congruence class. -/
|
||||
def isCongrRoot (e : Expr) : GoalM Bool := do
|
||||
|
||||
@@ -8,6 +8,7 @@ import Lean.Server.CodeActions
|
||||
import Lean.Widget.UserWidget
|
||||
import Lean.Data.Json.Elab
|
||||
import Lean.Data.Lsp.Utf16
|
||||
import Lean.Meta.Tactic.ExposeNames
|
||||
|
||||
/-!
|
||||
# "Try this" support
|
||||
@@ -426,17 +427,27 @@ def addSuggestions (ref : Syntax) (suggestions : Array Suggestion)
|
||||
(codeActionPrefix? : Option String := none) : MetaM Unit := do
|
||||
if suggestions.isEmpty then throwErrorAt ref "no suggestions available"
|
||||
let msgs := suggestions.map toMessageData
|
||||
let msgs := msgs.foldl (init := MessageData.nil) (fun msg m => msg ++ m!"\n• " ++ m)
|
||||
let msgs := msgs.foldl (init := MessageData.nil) (fun msg m => msg ++ m!"\n• " ++ .nest 2 m)
|
||||
logInfoAt ref m!"{header}{msgs}"
|
||||
addSuggestionCore ref suggestions header (isInline := false) origSpan? style? codeActionPrefix?
|
||||
|
||||
private def addExactSuggestionCore (addSubgoalsMsg : Bool) (e : Expr) : MetaM Suggestion :=
|
||||
/--
|
||||
Returns the syntax for an `exact` or `refine` (as indicated by `useRefine`) tactic corresponding to
|
||||
`e`. If `exposeNames` is `true`, prepends the tactic with `expose_names.`
|
||||
-/
|
||||
def mkExactSuggestionSyntax (e : Expr) (useRefine : Bool) (exposeNames : Bool) : MetaM (TSyntax `tactic) :=
|
||||
withOptions (pp.mvars.set · false) do
|
||||
let exprStx ← (if exposeNames then withExposedNames else id) <| delabToRefinableSyntax e
|
||||
let tac ← if useRefine then `(tactic| refine $exprStx) else `(tactic| exact $exprStx)
|
||||
let tacSeq ← if exposeNames then `(tactic| (expose_names; $tac)) else pure tac
|
||||
return tacSeq
|
||||
|
||||
private def addExactSuggestionCore (addSubgoalsMsg : Bool) (exposeNames : Bool) (e : Expr) :
|
||||
MetaM Suggestion :=
|
||||
withOptions (pp.mvars.set · false) do
|
||||
let stx ← delabToRefinableSyntax e
|
||||
let mvars ← getMVars e
|
||||
let suggestion ← if mvars.isEmpty then `(tactic| exact $stx) else `(tactic| refine $stx)
|
||||
let pp ← ppExpr e
|
||||
let messageData? := if mvars.isEmpty then m!"exact {pp}" else m!"refine {pp}"
|
||||
let mut suggestion ← mkExactSuggestionSyntax e (useRefine := !mvars.isEmpty) exposeNames
|
||||
let messageData? ← SuggestionText.prettyExtra suggestion
|
||||
let postInfo? ← if !addSubgoalsMsg || mvars.isEmpty then pure none else
|
||||
let mut str := "\nRemaining subgoals:"
|
||||
for g in mvars do
|
||||
@@ -457,11 +468,12 @@ The parameters are:
|
||||
`Remaining subgoals:`
|
||||
* `codeActionPrefix?`: an optional string to be used as the prefix of the replacement text if the
|
||||
suggestion does not have a custom `toCodeActionTitle?`. If not provided, `"Try this: "` is used.
|
||||
* `exposeNames`: if true (default false), will insert `expose_names` prior to the generated tactic
|
||||
-/
|
||||
def addExactSuggestion (ref : Syntax) (e : Expr)
|
||||
(origSpan? : Option Syntax := none) (addSubgoalsMsg := false)
|
||||
(codeActionPrefix? : Option String := none): MetaM Unit := do
|
||||
addSuggestion ref (← addExactSuggestionCore addSubgoalsMsg e)
|
||||
(codeActionPrefix? : Option String := none) (exposeNames := false) : MetaM Unit := do
|
||||
addSuggestion ref (← addExactSuggestionCore addSubgoalsMsg exposeNames e)
|
||||
(origSpan? := origSpan?) (codeActionPrefix? := codeActionPrefix?)
|
||||
|
||||
/-- Add `exact e` or `refine e` suggestions.
|
||||
@@ -479,8 +491,8 @@ The parameters are:
|
||||
-/
|
||||
def addExactSuggestions (ref : Syntax) (es : Array Expr)
|
||||
(origSpan? : Option Syntax := none) (addSubgoalsMsg := false)
|
||||
(codeActionPrefix? : Option String := none) : MetaM Unit := do
|
||||
let suggestions ← es.mapM <| addExactSuggestionCore addSubgoalsMsg
|
||||
(codeActionPrefix? : Option String := none) (exposeNames := false) : MetaM Unit := do
|
||||
let suggestions ← es.mapM <| addExactSuggestionCore addSubgoalsMsg exposeNames
|
||||
addSuggestions ref suggestions (origSpan? := origSpan?) (codeActionPrefix? := codeActionPrefix?)
|
||||
|
||||
/-- Add a term suggestion.
|
||||
|
||||
@@ -390,7 +390,8 @@ def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Ch
|
||||
let opts := cmdlineOpts.mergeBy (fun _ _ fileOpt => fileOpt) fileSetupResult.fileOptions
|
||||
|
||||
-- default to async elaboration; see also `Elab.async` docs
|
||||
let opts := Elab.async.setIfNotSet opts true
|
||||
-- (temporarily disabled pending #7241)
|
||||
--let opts := Elab.async.setIfNotSet opts true
|
||||
|
||||
return .ok {
|
||||
mainModuleName
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user