mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 02:44:12 +00:00
Compare commits
152 Commits
array_appe
...
grind_spli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f8a66fd30 | ||
|
|
686be911da | ||
|
|
d5e55fb73d | ||
|
|
ca56c5ecc0 | ||
|
|
d10666731c | ||
|
|
6dbb54d221 | ||
|
|
cc260dd231 | ||
|
|
9565334c0e | ||
|
|
2fa38e6ceb | ||
|
|
056d1dbeef | ||
|
|
e8bbba06b7 | ||
|
|
58c7a4f15e | ||
|
|
c8be581bc8 | ||
|
|
c6e244d811 | ||
|
|
044bf85fe9 | ||
|
|
1059e25ca2 | ||
|
|
c70f4064b4 | ||
|
|
757899a7d1 | ||
|
|
a901e34362 | ||
|
|
bab10cc2b5 | ||
|
|
d26dbe73d5 | ||
|
|
214093e6c4 | ||
|
|
ebda2d4d25 | ||
|
|
7e03920bbb | ||
|
|
d033804190 | ||
|
|
56733b953e | ||
|
|
c073da20ce | ||
|
|
d8bcd6a32e | ||
|
|
f35a602070 | ||
|
|
14841ad1ed | ||
|
|
5f3c0daf3d | ||
|
|
6befda831d | ||
|
|
6595ca8f29 | ||
|
|
91e261da38 | ||
|
|
6ebce42142 | ||
|
|
b6db90a316 | ||
|
|
7706b876f6 | ||
|
|
9b74c07767 | ||
|
|
533af01dab | ||
|
|
de31faa470 | ||
|
|
3881f21df1 | ||
|
|
c9a03c7613 | ||
|
|
0c2fb34c82 | ||
|
|
eb30249b11 | ||
|
|
31929c0acd | ||
|
|
3569797377 | ||
|
|
7b813d4f5d | ||
|
|
edeae18f5e | ||
|
|
91bae2e064 | ||
|
|
f9e904af50 | ||
|
|
8375d00d8c | ||
|
|
16bd7ea455 | ||
|
|
c54287fb0d | ||
|
|
e3771e3ad6 | ||
|
|
4935829abe | ||
|
|
778333c667 | ||
|
|
189f5d41fb | ||
|
|
c07f64a621 | ||
|
|
22117f21e3 | ||
|
|
1d03cd6a6b | ||
|
|
ac6a29ee83 | ||
|
|
57f0006c9b | ||
|
|
e40e0892c1 | ||
|
|
1fcdd7ad9a | ||
|
|
9b7bd58c14 | ||
|
|
a062eea204 | ||
|
|
645bdea23c | ||
|
|
35bbb48916 | ||
|
|
b289b660c7 | ||
|
|
75c104ce06 | ||
|
|
74bd40d34d | ||
|
|
4213862b0e | ||
|
|
4d8bc22228 | ||
|
|
7ee938290b | ||
|
|
478d42105f | ||
|
|
5998ba545b | ||
|
|
8a8417f6e1 | ||
|
|
26941793ff | ||
|
|
70050c3798 | ||
|
|
50a0a97b49 | ||
|
|
5fb2e892c8 | ||
|
|
3770808b58 | ||
|
|
5e63dd292f | ||
|
|
98e3d6f663 | ||
|
|
d4070d4bfb | ||
|
|
4d4c0941be | ||
|
|
9b629cc81f | ||
|
|
f374ef154e | ||
|
|
e3fd954318 | ||
|
|
b7815b5684 | ||
|
|
7f0ae22e43 | ||
|
|
35a4da28ac | ||
|
|
60142c967c | ||
|
|
17c0187252 | ||
|
|
e42f7d9fc3 | ||
|
|
906aa1be4b | ||
|
|
f01527142e | ||
|
|
f4c9934171 | ||
|
|
80ddbf45eb | ||
|
|
3a6c5cf4f1 | ||
|
|
af4a7d7e98 | ||
|
|
6259b4742c | ||
|
|
0050e9369c | ||
|
|
64cf5e5e6a | ||
|
|
127b3f9191 | ||
|
|
65175dc7d4 | ||
|
|
54f06ccd64 | ||
|
|
b3f8feffd3 | ||
|
|
6665837232 | ||
|
|
c7fd873333 | ||
|
|
a10ce9492f | ||
|
|
838ad281f2 | ||
|
|
a1ef26bd8b | ||
|
|
a955708b6c | ||
|
|
0f7f80aff5 | ||
|
|
8d69909b18 | ||
|
|
f95d8108f4 | ||
|
|
5d6bf75795 | ||
|
|
563d5e8bcf | ||
|
|
3da7f70014 | ||
|
|
8e5a3e416b | ||
|
|
9dbe5e6f9c | ||
|
|
c12b1d0a55 | ||
|
|
85294b800f | ||
|
|
821c9b7af9 | ||
|
|
e9bd9807ef | ||
|
|
05aa256c99 | ||
|
|
d6f0c324c3 | ||
|
|
f57745e9d4 | ||
|
|
749a82a8ce | ||
|
|
85560da3e4 | ||
|
|
e6a643770f | ||
|
|
30ba383744 | ||
|
|
734fca7b6a | ||
|
|
a6eea4b650 | ||
|
|
8483ac7258 | ||
|
|
5f41cc71ff | ||
|
|
2421f7f799 | ||
|
|
40efbb9b7a | ||
|
|
603108e34c | ||
|
|
aa95a1c03f | ||
|
|
af8f3d1ec1 | ||
|
|
c7939cfb03 | ||
|
|
0da3624ec9 | ||
|
|
349da6cae2 | ||
|
|
541902564b | ||
|
|
8b1aabbb1e | ||
|
|
ce1ff03af0 | ||
|
|
c5c1278315 | ||
|
|
5119528d20 | ||
|
|
4636091571 | ||
|
|
7ea5504af2 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -238,7 +238,7 @@ jobs:
|
||||
"name": "Linux 32bit",
|
||||
"os": "ubuntu-latest",
|
||||
// Use 32bit on stage0 and stage1 to keep oleans compatible
|
||||
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
|
||||
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DPKG_CONFIG_EXECUTABLE=/usr/bin/i386-linux-gnu-pkg-config",
|
||||
"cmultilib": true,
|
||||
"release": true,
|
||||
"check-level": 2,
|
||||
@@ -327,7 +327,7 @@ jobs:
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 pkgconf:i386
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -18,6 +18,9 @@ foreach(var ${vars})
|
||||
if("${var}" MATCHES "LLVM*")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
if("${var}" MATCHES "PKG_CONFIG*")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
|
||||
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"displayName": "Sanitize build config",
|
||||
"cacheVariables": {
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined",
|
||||
"LEANC_EXTRA_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"SMALL_ALLOCATOR": "OFF",
|
||||
"BSYMBOLIC": "OFF"
|
||||
},
|
||||
|
||||
@@ -590,9 +590,9 @@ This table should be read as follows:
|
||||
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
|
||||
|
||||
|
||||
Lean will print the termination argument it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
Lean will print the termination measure it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
|
||||
If Lean does not find the termination argument, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
If Lean does not find the termination measure, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
```
|
||||
termination_by e
|
||||
```
|
||||
@@ -672,7 +672,7 @@ def num_consts_lst : List Term → Nat
|
||||
end
|
||||
```
|
||||
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination argument (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination measure (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
|
||||
```
|
||||
mutual
|
||||
|
||||
@@ -33,6 +33,9 @@ Format of the commit message
|
||||
- chore (maintain, ex: travis-ci)
|
||||
- perf (performance improvement, optimization, ...)
|
||||
|
||||
Every `feat` or `fix` commit must have a `changelog-*` label, and a commit message
|
||||
beginning with "This PR " that will be included in the changelog.
|
||||
|
||||
``<subject>`` has the following constraints:
|
||||
|
||||
- use imperative, present tense: "change" not "changed" nor "changes"
|
||||
@@ -44,6 +47,7 @@ Format of the commit message
|
||||
- just as in ``<subject>``, use imperative, present tense
|
||||
- includes motivation for the change and contrasts with previous
|
||||
behavior
|
||||
- If a `changelog-*` label is present, the body must begin with "This PR ".
|
||||
|
||||
``<footer>`` is optional and may contain two items:
|
||||
|
||||
@@ -60,17 +64,21 @@ Examples
|
||||
|
||||
fix: add declarations for operator<<(std::ostream&, expr const&) and operator<<(std::ostream&, context const&) in the kernel
|
||||
|
||||
This PR adds declarations `operator<<` for raw printing.
|
||||
The actual implementation of these two operators is outside of the
|
||||
kernel. They are implemented in the file 'library/printer.cpp'. We
|
||||
declare them in the kernel to prevent the following problem. Suppose
|
||||
there is a file 'foo.cpp' that does not include 'library/printer.h',
|
||||
but contains
|
||||
kernel. They are implemented in the file 'library/printer.cpp'.
|
||||
|
||||
expr a;
|
||||
...
|
||||
std::cout << a << "\n";
|
||||
...
|
||||
We declare them in the kernel to prevent the following problem.
|
||||
Suppose there is a file 'foo.cpp' that does not include 'library/printer.h',
|
||||
but contains
|
||||
```cpp
|
||||
expr a;
|
||||
...
|
||||
std::cout << a << "\n";
|
||||
...
|
||||
```
|
||||
|
||||
The compiler does not generate an error message. It silently uses the
|
||||
operator bool() to coerce the expression into a Boolean. This produces
|
||||
counter-intuitive behavior, and may confuse developers.
|
||||
|
||||
|
||||
@@ -80,3 +80,10 @@ Unlike most Lean projects, all submodules of the `Lean` module begin with the
|
||||
`prelude` keyword. This disables the automated import of `Init`, meaning that
|
||||
developers need to figure out their own subset of `Init` to import. This is done
|
||||
such that changing files in `Init` doesn't force a full rebuild of `Lean`.
|
||||
|
||||
### Testing against Mathlib/Batteries
|
||||
You can test a Lean PR against Mathlib and Batteries by rebasing your PR
|
||||
on to `nightly-with-mathlib` branch. (It is fine to force push after rebasing.)
|
||||
CI will generate a branch of Mathlib and Batteries called `lean-pr-testing-NNNN`
|
||||
that uses the toolchain for your PR, and will report back to the Lean PR with results from Mathlib CI.
|
||||
See https://leanprover-community.github.io/contribute/tags_and_branches.html for more details.
|
||||
|
||||
@@ -61,7 +61,7 @@ Parts of atomic names can be escaped by enclosing them in pairs of French double
|
||||
letterlike_symbols: [℀-⅏]
|
||||
escaped_ident_part: "«" [^«»\r\n\t]* "»"
|
||||
atomic_ident_rest: atomic_ident_start | [0-9'ⁿ] | subscript
|
||||
subscript: [₀-₉ₐ-ₜᵢ-ᵪ]
|
||||
subscript: [₀-₉ₐ-ₜᵢ-ᵪⱼ]
|
||||
```
|
||||
|
||||
String Literals
|
||||
|
||||
@@ -32,12 +32,13 @@ following to use `g++`.
|
||||
cmake -DCMAKE_CXX_COMPILER=g++ ...
|
||||
```
|
||||
|
||||
## Required Packages: CMake, GMP, libuv
|
||||
## Required Packages: CMake, GMP, libuv, pkgconf
|
||||
|
||||
```bash
|
||||
brew install cmake
|
||||
brew install gmp
|
||||
brew install libuv
|
||||
brew install pkgconf
|
||||
```
|
||||
|
||||
## Recommended Packages: CCache
|
||||
|
||||
@@ -8,5 +8,5 @@ follow the [generic build instructions](index.md).
|
||||
## Basic packages
|
||||
|
||||
```bash
|
||||
sudo apt-get install git libgmp-dev libuv1-dev cmake ccache clang
|
||||
sudo apt-get install git libgmp-dev libuv1-dev cmake ccache clang pkgconf
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
|
||||
} ({
|
||||
buildInputs = with pkgs; [
|
||||
cmake gmp libuv ccache cadical
|
||||
cmake gmp libuv ccache cadical pkg-config
|
||||
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
|
||||
gdb
|
||||
tree # for CI
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
|
||||
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
stdenv, lib, cmake, pkg-config, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
|
||||
... } @ args:
|
||||
with builtins;
|
||||
lib.warn "The Nix-based build is deprecated" rec {
|
||||
inherit stdenv;
|
||||
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
|
||||
buildCMake = args: stdenv.mkDerivation ({
|
||||
nativeBuildInputs = [ cmake ];
|
||||
nativeBuildInputs = [ cmake pkg-config ];
|
||||
buildInputs = [ gmp libuv llvmPackages.llvm ];
|
||||
# https://github.com/NixOS/nixpkgs/issues/60919
|
||||
hardeningDisable = [ "all" ];
|
||||
|
||||
69
script/push_repo_release_tag.py
Executable file
69
script/push_repo_release_tag.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import subprocess
|
||||
import requests
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("Usage: ./push_repo_release_tag.py <repo> <branch> <version_tag>")
|
||||
sys.exit(1)
|
||||
|
||||
repo, branch, version_tag = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
|
||||
if branch not in {"master", "main"}:
|
||||
print(f"Error: Branch '{branch}' is not 'master' or 'main'.")
|
||||
sys.exit(1)
|
||||
|
||||
# Get the `lean-toolchain` file content
|
||||
lean_toolchain_url = f"https://raw.githubusercontent.com/{repo}/{branch}/lean-toolchain"
|
||||
try:
|
||||
response = requests.get(lean_toolchain_url)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching 'lean-toolchain' file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
lean_toolchain_content = response.text.strip()
|
||||
expected_prefix = "leanprover/lean4:"
|
||||
if not lean_toolchain_content.startswith(expected_prefix) or lean_toolchain_content != f"{expected_prefix}{version_tag}":
|
||||
print(f"Error: 'lean-toolchain' content does not match '{expected_prefix}{version_tag}'.")
|
||||
sys.exit(1)
|
||||
|
||||
# Create and push the tag using `gh`
|
||||
try:
|
||||
# Check if the tag already exists
|
||||
list_tags_cmd = ["gh", "api", f"repos/{repo}/git/matching-refs/tags/v4", "--jq", ".[].ref"]
|
||||
list_tags_output = subprocess.run(list_tags_cmd, capture_output=True, text=True)
|
||||
|
||||
if list_tags_output.returncode == 0:
|
||||
existing_tags = list_tags_output.stdout.strip().splitlines()
|
||||
if f"refs/tags/{version_tag}" in existing_tags:
|
||||
print(f"Error: Tag '{version_tag}' already exists.")
|
||||
print("Existing tags starting with 'v4':")
|
||||
for tag in existing_tags:
|
||||
print(tag.replace("refs/tags/", ""))
|
||||
sys.exit(1)
|
||||
|
||||
# Get the SHA of the branch
|
||||
get_sha_cmd = [
|
||||
"gh", "api", f"repos/{repo}/git/ref/heads/{branch}", "--jq", ".object.sha"
|
||||
]
|
||||
sha_result = subprocess.run(get_sha_cmd, capture_output=True, text=True, check=True)
|
||||
sha = sha_result.stdout.strip()
|
||||
|
||||
# Create the tag
|
||||
create_tag_cmd = [
|
||||
"gh", "api", f"repos/{repo}/git/refs",
|
||||
"-X", "POST",
|
||||
"-F", f"ref=refs/tags/{version_tag}",
|
||||
"-F", f"sha={sha}"
|
||||
]
|
||||
subprocess.run(create_tag_cmd, capture_output=True, text=True, check=True)
|
||||
|
||||
print(f"Successfully created and pushed tag '{version_tag}' to {repo}.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error while creating/pushing tag: {e.stderr.strip() if e.stderr else e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -22,6 +22,36 @@ def get_github_token():
|
||||
print("Warning: 'gh' CLI not found. Some API calls may be rate-limited.")
|
||||
return None
|
||||
|
||||
def strip_rc_suffix(toolchain):
|
||||
"""Remove -rcX suffix from the toolchain."""
|
||||
return toolchain.split("-")[0]
|
||||
|
||||
def branch_exists(repo_url, branch, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/branches/{branch}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
|
||||
def tag_exists(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/refs/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
|
||||
def release_page_exists(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
|
||||
def get_release_notes(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json().get("body", "").strip()
|
||||
return None
|
||||
|
||||
def get_branch_content(repo_url, branch, file_path, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
@@ -35,11 +65,20 @@ def get_branch_content(repo_url, branch, file_path, github_token):
|
||||
return None
|
||||
return None
|
||||
|
||||
def tag_exists(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/refs/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
def parse_version(version_str):
|
||||
# Remove 'v' prefix and extract version and release candidate suffix
|
||||
if ':' in version_str:
|
||||
version_str = version_str.split(':')[1]
|
||||
version = version_str.lstrip('v')
|
||||
parts = version.split('-')
|
||||
base_version = tuple(map(int, parts[0].split('.')))
|
||||
rc_part = parts[1] if len(parts) > 1 and parts[1].startswith('rc') else None
|
||||
rc_number = int(rc_part[2:]) if rc_part else float('inf') # Treat non-rc as higher than rc
|
||||
return base_version + (rc_number,)
|
||||
|
||||
def is_version_gte(version1, version2):
|
||||
"""Check if version1 >= version2, including proper handling of release candidates."""
|
||||
return parse_version(version1) >= parse_version(version2)
|
||||
|
||||
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
|
||||
# First get the commit SHA for the tag
|
||||
@@ -64,23 +103,38 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
|
||||
stable_commits = [commit['sha'] for commit in commits_response.json()]
|
||||
return tag_sha in stable_commits
|
||||
|
||||
def parse_version(version_str):
|
||||
# Remove 'v' prefix and split into components
|
||||
# Handle Lean toolchain format (leanprover/lean4:v4.x.y)
|
||||
if ':' in version_str:
|
||||
version_str = version_str.split(':')[1]
|
||||
version = version_str.lstrip('v')
|
||||
# Handle release candidates by removing -rc part for comparison
|
||||
version = version.split('-')[0]
|
||||
return tuple(map(int, version.split('.')))
|
||||
|
||||
def is_version_gte(version1, version2):
|
||||
"""Check if version1 >= version2"""
|
||||
return parse_version(version1) >= parse_version(version2)
|
||||
|
||||
def is_release_candidate(version):
|
||||
return "-rc" in version
|
||||
|
||||
def check_cmake_version(repo_url, branch, version_major, version_minor, github_token):
|
||||
"""Verify the CMake version settings in src/CMakeLists.txt."""
|
||||
cmake_file_path = "src/CMakeLists.txt"
|
||||
content = get_branch_content(repo_url, branch, cmake_file_path, github_token)
|
||||
if content is None:
|
||||
print(f" ❌ Could not retrieve {cmake_file_path} from {branch}")
|
||||
return False
|
||||
|
||||
expected_lines = [
|
||||
f"set(LEAN_VERSION_MAJOR {version_major})",
|
||||
f"set(LEAN_VERSION_MINOR {version_minor})",
|
||||
f"set(LEAN_VERSION_PATCH 0)",
|
||||
f"set(LEAN_VERSION_IS_RELEASE 1)"
|
||||
]
|
||||
|
||||
for line in expected_lines:
|
||||
if not any(l.strip().startswith(line) for l in content.splitlines()):
|
||||
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {line}")
|
||||
return False
|
||||
|
||||
print(f" ✅ CMake version settings are correct in {cmake_file_path}")
|
||||
return True
|
||||
|
||||
def extract_org_repo_from_url(repo_url):
|
||||
"""Extract the 'org/repo' part from a GitHub URL."""
|
||||
if repo_url.startswith("https://github.com/"):
|
||||
return repo_url.replace("https://github.com/", "").rstrip("/")
|
||||
return repo_url
|
||||
|
||||
def main():
|
||||
github_token = get_github_token()
|
||||
|
||||
@@ -89,6 +143,47 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
toolchain = sys.argv[1]
|
||||
stripped_toolchain = strip_rc_suffix(toolchain)
|
||||
lean_repo_url = "https://github.com/leanprover/lean4"
|
||||
|
||||
# Preliminary checks
|
||||
print("\nPerforming preliminary checks...")
|
||||
|
||||
# Check for branch releases/v4.Y.0
|
||||
version_major, version_minor, _ = map(int, stripped_toolchain.lstrip('v').split('.'))
|
||||
branch_name = f"releases/v{version_major}.{version_minor}.0"
|
||||
if branch_exists(lean_repo_url, branch_name, github_token):
|
||||
print(f" ✅ Branch {branch_name} exists")
|
||||
|
||||
# Check CMake version settings
|
||||
check_cmake_version(lean_repo_url, branch_name, version_major, version_minor, github_token)
|
||||
else:
|
||||
print(f" ❌ Branch {branch_name} does not exist")
|
||||
|
||||
# Check for tag v4.X.Y(-rcZ)
|
||||
if tag_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ✅ Tag {toolchain} exists")
|
||||
else:
|
||||
print(f" ❌ Tag {toolchain} does not exist.")
|
||||
|
||||
# Check for release page
|
||||
if release_page_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ✅ Release page for {toolchain} exists")
|
||||
|
||||
# Check the first line of the release notes
|
||||
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
|
||||
if release_notes and release_notes.splitlines()[0].strip() == toolchain:
|
||||
print(f" ✅ Release notes look good.")
|
||||
else:
|
||||
previous_minor_version = version_minor - 1
|
||||
previous_stable_branch = f"releases/v{version_major}.{previous_minor_version}.0"
|
||||
previous_release = f"v{version_major}.{previous_minor_version}.0"
|
||||
print(f" ❌ Release notes not published. Please run `script/release_notes.py {previous_release}` on branch `{previous_stable_branch}`.")
|
||||
else:
|
||||
print(f" ❌ Release page for {toolchain} does not exist")
|
||||
|
||||
# Load repositories and perform further checks
|
||||
print("\nChecking repositories...")
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "release_repos.yml")) as f:
|
||||
repos = yaml.safe_load(f)["repositories"]
|
||||
@@ -117,7 +212,7 @@ def main():
|
||||
# Only check for tag if toolchain-tag is true
|
||||
if check_tag:
|
||||
if not tag_exists(url, toolchain, github_token):
|
||||
print(f" ❌ Tag {toolchain} does not exist")
|
||||
print(f" ❌ Tag {toolchain} does not exist. Run `script/push_repo_release_tag.py {extract_org_repo_from_url(url)} {branch} {toolchain}`.")
|
||||
continue
|
||||
print(f" ✅ Tag {toolchain} exists")
|
||||
|
||||
|
||||
@@ -295,14 +295,15 @@ index 5e8e0166..f3b29134 100644
|
||||
PATCH_COMMAND git reset --hard HEAD && printf "${LIBUV_PATCH}" > patch.diff && git apply patch.diff
|
||||
BUILD_IN_SOURCE ON
|
||||
INSTALL_COMMAND "")
|
||||
set(LIBUV_INCLUDE_DIR "${CMAKE_BINARY_DIR}/libuv/src/libuv/include")
|
||||
set(LIBUV_LIBRARIES "${CMAKE_BINARY_DIR}/libuv/src/libuv/libuv.a")
|
||||
set(LIBUV_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/libuv/src/libuv/include")
|
||||
set(LIBUV_LDFLAGS "${CMAKE_BINARY_DIR}/libuv/src/libuv/libuv.a")
|
||||
else()
|
||||
find_package(LibUV 1.0.0 REQUIRED)
|
||||
endif()
|
||||
include_directories(${LIBUV_INCLUDE_DIR})
|
||||
include_directories(${LIBUV_INCLUDE_DIRS})
|
||||
if(NOT LEAN_STANDALONE)
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${LIBUV_LIBRARIES}")
|
||||
string(JOIN " " LIBUV_LDFLAGS ${LIBUV_LDFLAGS})
|
||||
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${LIBUV_LDFLAGS}")
|
||||
endif()
|
||||
|
||||
# Windows SDK (for ICU)
|
||||
@@ -698,12 +699,12 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
add_custom_target(lake_lib ALL
|
||||
add_custom_target(lake_lib
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS leanshared
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
|
||||
VERBATIM)
|
||||
add_custom_target(lake_shared ALL
|
||||
add_custom_target(lake_shared
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS lake_lib
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
|
||||
|
||||
@@ -516,8 +516,17 @@ The tasks have an overridden representation in the runtime.
|
||||
structure Task (α : Type u) : Type u where
|
||||
/-- `Task.pure (a : α)` constructs a task that is already resolved with value `a`. -/
|
||||
pure ::
|
||||
/-- If `task : Task α` then `task.get : α` blocks the current thread until the
|
||||
value is available, and then returns the result of the task. -/
|
||||
/--
|
||||
Blocks the current thread until the given task has finished execution, and then returns the result
|
||||
of the task. If the current thread is itself executing a (non-dedicated) task, the maximum
|
||||
threadpool size is temporarily increased by one while waiting so as to ensure the process cannot
|
||||
be deadlocked by threadpool starvation. Note that when the current thread is unblocked, more tasks
|
||||
than the configured threadpool size may temporarily be running at the same time until sufficiently
|
||||
many tasks have finished.
|
||||
|
||||
`Task.map` and `Task.bind` should be preferred over `Task.get` for setting up task dependencies
|
||||
where possible as they do not require temporarily growing the threadpool in this way.
|
||||
-/
|
||||
get : α
|
||||
deriving Inhabited, Nonempty
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Joachim Breitner, Mario Carneiro
|
||||
prelude
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Count
|
||||
import Init.Data.List.Attach
|
||||
|
||||
namespace Array
|
||||
@@ -142,10 +143,16 @@ theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
cases l
|
||||
simp [List.pmap_eq_map_attach]
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_attachWith {p q : α → Prop} (f : ∀ a, p a → q a) (l H) :
|
||||
pmap (fun a h => ⟨a, f a h⟩) l H = l.attachWith q (fun x h => f x (H x h)) := by
|
||||
cases l
|
||||
simp [List.pmap_eq_attachWith]
|
||||
|
||||
theorem attach_map_coe (l : Array α) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
cases l
|
||||
simp [List.attach_map_coe]
|
||||
simp
|
||||
|
||||
theorem attach_map_val (l : Array α) (f : α → β) : (l.attach.map fun i => f i.val) = l.map f :=
|
||||
attach_map_coe _ _
|
||||
@@ -172,6 +179,12 @@ theorem mem_attach (l : Array α) : ∀ x, x ∈ l.attach
|
||||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||||
exact m
|
||||
|
||||
@[simp]
|
||||
theorem mem_attachWith (l : Array α) {q : α → Prop} (H) (x : {x // q x}) :
|
||||
x ∈ l.attachWith q H ↔ x.1 ∈ l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
@@ -223,16 +236,16 @@ theorem attachWith_ne_empty_iff {l : Array α} {P : α → Prop} {H : ∀ a ∈
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (mem_of_getElem? H) := by
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) (i : Nat) :
|
||||
(pmap f l h)[i]? = Option.pmap f l[i]? fun x H => h x (mem_of_getElem? H) := by
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).size) :
|
||||
(pmap f l h)[n] =
|
||||
f (l[n]'(@size_pmap _ _ p f l h ▸ hn))
|
||||
(h _ (getElem_mem (@size_pmap _ _ p f l h ▸ hn))) := by
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) {i : Nat}
|
||||
(hi : i < (pmap f l h).size) :
|
||||
(pmap f l h)[i] =
|
||||
f (l[i]'(@size_pmap _ _ p f l h ▸ hi))
|
||||
(h _ (getElem_mem (@size_pmap _ _ p f l h ▸ hi))) := by
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
@@ -256,6 +269,18 @@ theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
@[simp] theorem pmap_attach (l : Array α) {p : {x // x ∈ l} → Prop} (f : ∀ a, p a → β) (H) :
|
||||
pmap f l.attach H =
|
||||
l.pmap (P := fun a => ∃ h : a ∈ l, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem pmap_attachWith (l : Array α) {p : {x // q x} → Prop} (f : ∀ a, p a → β) (H₁ H₂) :
|
||||
pmap f (l.attachWith q H₁) H₂ =
|
||||
l.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
theorem foldl_pmap (l : Array α) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : γ → β → γ) (x : γ) :
|
||||
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
@@ -313,11 +338,7 @@ theorem attachWith_map {l : Array α} (f : α → β) {P : β → Prop} {H : ∀
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem f h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
cases l
|
||||
ext
|
||||
· simp
|
||||
· simp only [List.map_toArray, List.attachWith_toArray, List.getElem_toArray,
|
||||
List.getElem_attachWith, List.getElem_map, Function.comp_apply]
|
||||
erw [List.getElem_attachWith] -- Why is `erw` needed here?
|
||||
simp [List.attachWith_map]
|
||||
|
||||
theorem map_attachWith {l : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ l → P a}
|
||||
(f : { x // P x } → β) :
|
||||
@@ -347,7 +368,23 @@ theorem attach_filter {l : Array α} (p : α → Bool) :
|
||||
simp [List.attach_filter, List.map_filterMap, Function.comp_def]
|
||||
|
||||
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
|
||||
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
|
||||
|
||||
@[simp]
|
||||
theorem filterMap_attachWith {q : α → Prop} {l : Array α} {f : {x // q x} → Option β} (H)
|
||||
(w : stop = (l.attachWith q H).size) :
|
||||
(l.attachWith q H).filterMap f 0 stop = l.attach.filterMap (fun ⟨x, h⟩ => f ⟨x, H _ h⟩) := by
|
||||
subst w
|
||||
cases l
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem filter_attachWith {q : α → Prop} {l : Array α} {p : {x // q x} → Bool} (H)
|
||||
(w : stop = (l.attachWith q H).size) :
|
||||
(l.attachWith q H).filter p 0 stop =
|
||||
(l.attach.filter (fun ⟨x, h⟩ => p ⟨x, H _ h⟩)).map (fun ⟨x, h⟩ => ⟨x, H _ h⟩) := by
|
||||
subst w
|
||||
cases l
|
||||
simp [Function.comp_def, List.filter_map]
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} (g : ∀ a, p a → β) (f : ∀ b, q b → γ) (l H₁ H₂) :
|
||||
pmap f (pmap g l H₁) H₂ =
|
||||
@@ -427,16 +464,48 @@ theorem reverse_attach (xs : Array α) :
|
||||
|
||||
@[simp] theorem back?_attachWith {P : α → Prop} {xs : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back?_eq_some h)⟩) := by
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back? h)⟩) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem back?_attach {xs : Array α} :
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back?_eq_some h⟩ := by
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back? h⟩ := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem countP_attach (l : Array α) (p : α → Bool) :
|
||||
l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
cases l
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem countP_attachWith {p : α → Prop} (l : Array α) (H : ∀ a ∈ l, p a) (q : α → Bool) :
|
||||
(l.attachWith p H).countP (fun a : {x // p x} => q a) = l.countP q := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem count_attach [DecidableEq α] (l : Array α) (a : {x // x ∈ l}) :
|
||||
l.attach.count a = l.count ↑a := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
|
||||
rw [List.map_attach, List.count_eq_countP]
|
||||
simp only [Subtype.beq_iff]
|
||||
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
|
||||
|
||||
@[simp]
|
||||
theorem count_attachWith [DecidableEq α] {p : α → Prop} (l : Array α) (H : ∀ a ∈ l, p a) (a : {x // p x}) :
|
||||
(l.attachWith p H).count a = l.count ↑a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pmap {p : α → Prop} (g : ∀ a, p a → β) (f : β → Bool) (l : Array α) (H₁) :
|
||||
(l.pmap g H₁).countP f =
|
||||
l.attach.countP (fun ⟨a, m⟩ => f (g a (H₁ a m))) := by
|
||||
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
|
||||
@@ -455,7 +524,7 @@ and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) := l.map (·.val)
|
||||
def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) : Array α := l.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {l : Array { x // p x }} :
|
||||
@@ -578,4 +647,16 @@ and simplifies these to the function directly taking the value.
|
||||
cases l₂
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_flatten {p : α → Prop} {l : Array (Array { x // p x })} :
|
||||
l.flatten.unattach = (l.map unattach).flatten := by
|
||||
unfold unattach
|
||||
cases l using array₂_induction
|
||||
simp only [flatten_toArray, List.map_map, Function.comp_def, List.map_id_fun', id_eq,
|
||||
List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
|
||||
simp only [List.unattach]
|
||||
|
||||
@[simp] theorem unattach_mkArray {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(Array.mkArray n x).unattach = Array.mkArray n x.1 := by
|
||||
simp [unattach]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -455,7 +455,7 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
/-- Variant of `mapIdxM` which receives the index as a `Fin as.size`. -/
|
||||
@[inline]
|
||||
def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
(as : Array α) (f : Fin as.size → α → m β) : m (Array β) :=
|
||||
(as : Array α) (f : (i : Nat) → α → (h : i < as.size) → m β) : m (Array β) :=
|
||||
let rec @[specialize] map (i : Nat) (j : Nat) (inv : i + j = as.size) (bs : Array β) : m (Array β) := do
|
||||
match i, inv with
|
||||
| 0, _ => pure bs
|
||||
@@ -464,12 +464,12 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
rw [← inv, Nat.add_assoc, Nat.add_comm 1 j, Nat.add_comm]
|
||||
apply Nat.le_add_right
|
||||
have : i + (j + 1) = as.size := by rw [← inv, Nat.add_comm j 1, Nat.add_assoc]
|
||||
map i (j+1) this (bs.push (← f ⟨j, j_lt⟩ (as.get j j_lt)))
|
||||
map i (j+1) this (bs.push (← f j (as.get j j_lt) j_lt))
|
||||
map as.size 0 rfl (mkEmpty as.size)
|
||||
|
||||
@[inline]
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : Nat → α → m β) (as : Array α) : m (Array β) :=
|
||||
as.mapFinIdxM fun i a => f i a
|
||||
as.mapFinIdxM fun i a _ => f i a
|
||||
|
||||
@[inline]
|
||||
def findSomeM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m (Option β)) (as : Array α) : m (Option β) := do
|
||||
@@ -576,13 +576,28 @@ def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : A
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Array α) (start := as.size) (stop := 0) : β :=
|
||||
Id.run <| as.foldrM f init start stop
|
||||
|
||||
/-- Sum of an array.
|
||||
|
||||
`Array.sum #[a, b, c] = a + (b + (c + 0))` -/
|
||||
@[inline]
|
||||
def sum {α} [Add α] [Zero α] : Array α → α :=
|
||||
foldr (· + ·) 0
|
||||
|
||||
@[inline]
|
||||
def countP {α : Type u} (p : α → Bool) (as : Array α) : Nat :=
|
||||
as.foldr (init := 0) fun a acc => bif p a then acc + 1 else acc
|
||||
|
||||
@[inline]
|
||||
def count {α : Type u} [BEq α] (a : α) (as : Array α) : Nat :=
|
||||
countP (· == a) as
|
||||
|
||||
@[inline]
|
||||
def map {α : Type u} {β : Type v} (f : α → β) (as : Array α) : Array β :=
|
||||
Id.run <| as.mapM f
|
||||
|
||||
/-- Variant of `mapIdx` which receives the index as a `Fin as.size`. -/
|
||||
@[inline]
|
||||
def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : Fin as.size → α → β) : Array β :=
|
||||
def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → β) : Array β :=
|
||||
Id.run <| as.mapFinIdxM f
|
||||
|
||||
@[inline]
|
||||
|
||||
@@ -81,12 +81,18 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α → β → m β) (init
|
||||
|
||||
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
|
||||
|
||||
@[simp] theorem append_nil (as : Array α) : as ++ #[] = as := by
|
||||
@[simp] theorem append_empty (as : Array α) : as ++ #[] = as := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
|
||||
|
||||
@[simp] theorem nil_append (as : Array α) : #[] ++ as = as := by
|
||||
@[deprecated append_empty (since := "2025-01-13")]
|
||||
abbrev append_nil := @append_empty
|
||||
|
||||
@[simp] theorem empty_append (as : Array α) : #[] ++ as = as := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
|
||||
|
||||
@[deprecated empty_append (since := "2025-01-13")]
|
||||
abbrev nil_append := @empty_append
|
||||
|
||||
@[simp] theorem append_assoc (as bs cs : Array α) : as ++ bs ++ cs = as ++ (bs ++ cs) := by
|
||||
apply ext'; simp only [toList_append, List.append_assoc]
|
||||
|
||||
|
||||
270
src/Init/Data/Array/Count.lean
Normal file
270
src/Init/Data/Array/Count.lean
Normal file
@@ -0,0 +1,270 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.Count
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.countP` and `Array.count`.
|
||||
-/
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### countP -/
|
||||
section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos (l) (pa : p a) : countP p (l.push a) = countP p l + 1 := by
|
||||
rcases l with ⟨l⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_push_of_neg (l) (pa : ¬p a) : countP p (l.push a) = countP p l := by
|
||||
rcases l with ⟨l⟩
|
||||
simp_all
|
||||
|
||||
theorem countP_push (a : α) (l) : countP p (l.push a) = countP p l + if p a then 1 else 0 := by
|
||||
rcases l with ⟨l⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_singleton (a : α) : countP p #[a] = if p a then 1 else 0 := by
|
||||
simp [countP_push]
|
||||
|
||||
theorem size_eq_countP_add_countP (l) : l.size = countP p l + countP (fun a => ¬p a) l := by
|
||||
cases l
|
||||
simp [List.length_eq_countP_add_countP (p := p)]
|
||||
|
||||
theorem countP_eq_size_filter (l) : countP p l = (filter p l).size := by
|
||||
cases l
|
||||
simp [List.countP_eq_length_filter]
|
||||
|
||||
theorem countP_eq_size_filter' : countP p = size ∘ filter p := by
|
||||
funext l
|
||||
apply countP_eq_size_filter
|
||||
|
||||
theorem countP_le_size : countP p l ≤ l.size := by
|
||||
simp only [countP_eq_size_filter]
|
||||
apply size_filter_le
|
||||
|
||||
@[simp] theorem countP_append (l₁ l₂) : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pos_iff {p} : 0 < countP p l ↔ ∃ a ∈ l, p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem one_le_countP_iff {p} : 1 ≤ countP p l ↔ ∃ a ∈ l, p a :=
|
||||
countP_pos_iff
|
||||
|
||||
@[simp] theorem countP_eq_zero {p} : countP p l = 0 ↔ ∀ a ∈ l, ¬p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_eq_size {p} : countP p l = l.size ↔ ∀ a ∈ l, p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem countP_mkArray (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (mkArray n a) = if p a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.countP_replicate]
|
||||
|
||||
theorem boole_getElem_le_countP (p : α → Bool) (l : Array α) (i : Nat) (h : i < l.size) :
|
||||
(if p l[i] then 1 else 0) ≤ l.countP p := by
|
||||
cases l
|
||||
simp [List.boole_getElem_le_countP]
|
||||
|
||||
theorem countP_set (p : α → Bool) (l : Array α) (i : Nat) (a : α) (h : i < l.size) :
|
||||
(l.set i a).countP p = l.countP p - (if p l[i] then 1 else 0) + (if p a then 1 else 0) := by
|
||||
cases l
|
||||
simp [List.countP_set, h]
|
||||
|
||||
theorem countP_filter (l : Array α) :
|
||||
countP p (filter q l) = countP (fun a => p a && q a) l := by
|
||||
cases l
|
||||
simp [List.countP_filter]
|
||||
|
||||
@[simp] theorem countP_true : (countP fun (_ : α) => true) = size := by
|
||||
funext l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_false : (countP fun (_ : α) => false) = Function.const _ 0 := by
|
||||
funext l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_map (p : β → Bool) (f : α → β) (l : Array α) :
|
||||
countP p (map f l) = countP (p ∘ f) l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem size_filterMap_eq_countP (f : α → Option β) (l : Array α) :
|
||||
(filterMap f l).size = countP (fun a => (f a).isSome) l := by
|
||||
cases l
|
||||
simp [List.length_filterMap_eq_countP]
|
||||
|
||||
theorem countP_filterMap (p : β → Bool) (f : α → Option β) (l : Array α) :
|
||||
countP p (filterMap f l) = countP (fun a => ((f a).map p).getD false) l := by
|
||||
cases l
|
||||
simp [List.countP_filterMap]
|
||||
|
||||
@[simp] theorem countP_flatten (l : Array (Array α)) :
|
||||
countP p l.flatten = (l.map (countP p)).sum := by
|
||||
cases l using array₂_induction
|
||||
simp [List.countP_flatten, Function.comp_def]
|
||||
|
||||
theorem countP_flatMap (p : β → Bool) (l : Array α) (f : α → Array β) :
|
||||
countP p (l.flatMap f) = sum (map (countP p ∘ f) l) := by
|
||||
cases l
|
||||
simp [List.countP_flatMap, Function.comp_def]
|
||||
|
||||
@[simp] theorem countP_reverse (l : Array α) : countP p l.reverse = countP p l := by
|
||||
cases l
|
||||
simp [List.countP_reverse]
|
||||
|
||||
variable {p q}
|
||||
|
||||
theorem countP_mono_left (h : ∀ x ∈ l, p x → q x) : countP p l ≤ countP q l := by
|
||||
cases l
|
||||
simpa using List.countP_mono_left (by simpa using h)
|
||||
|
||||
theorem countP_congr (h : ∀ x ∈ l, p x ↔ q x) : countP p l = countP q l :=
|
||||
Nat.le_antisymm
|
||||
(countP_mono_left fun x hx => (h x hx).1)
|
||||
(countP_mono_left fun x hx => (h x hx).2)
|
||||
|
||||
end countP
|
||||
|
||||
/-! ### count -/
|
||||
section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
|
||||
|
||||
theorem count_push (a b : α) (l : Array α) :
|
||||
count a (l.push b) = count a l + if b == a then 1 else 0 := by
|
||||
simp [count, countP_push]
|
||||
|
||||
theorem count_eq_countP (a : α) (l : Array α) : count a l = countP (· == a) l := rfl
|
||||
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
|
||||
funext l
|
||||
apply count_eq_countP
|
||||
|
||||
theorem count_le_size (a : α) (l : Array α) : count a l ≤ l.size := countP_le_size _
|
||||
|
||||
theorem count_le_count_push (a b : α) (l : Array α) : count a l ≤ count a (l.push b) := by
|
||||
simp [count_push]
|
||||
|
||||
@[simp] theorem count_singleton (a b : α) : count a #[b] = if b == a then 1 else 0 := by
|
||||
simp [count_eq_countP]
|
||||
|
||||
@[simp] theorem count_append (a : α) : ∀ l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
|
||||
countP_append _
|
||||
|
||||
@[simp] theorem count_flatten (a : α) (l : Array (Array α)) :
|
||||
count a l.flatten = (l.map (count a)).sum := by
|
||||
cases l using array₂_induction
|
||||
simp [List.count_flatten, Function.comp_def]
|
||||
|
||||
@[simp] theorem count_reverse (a : α) (l : Array α) : count a l.reverse = count a l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem boole_getElem_le_count (a : α) (l : Array α) (i : Nat) (h : i < l.size) :
|
||||
(if l[i] == a then 1 else 0) ≤ l.count a := by
|
||||
rw [count_eq_countP]
|
||||
apply boole_getElem_le_countP (· == a)
|
||||
|
||||
theorem count_set (a b : α) (l : Array α) (i : Nat) (h : i < l.size) :
|
||||
(l.set i a).count b = l.count b - (if l[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
|
||||
simp [count_eq_countP, countP_set, h]
|
||||
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem count_push_self (a : α) (l : Array α) : count a (l.push a) = count a l + 1 := by
|
||||
simp [count_push]
|
||||
|
||||
@[simp] theorem count_push_of_ne (h : b ≠ a) (l : Array α) : count a (l.push b) = count a l := by
|
||||
simp_all [count_push, h]
|
||||
|
||||
theorem count_singleton_self (a : α) : count a #[a] = 1 := by simp
|
||||
|
||||
@[simp]
|
||||
theorem count_pos_iff {a : α} {l : Array α} : 0 < count a l ↔ a ∈ l := by
|
||||
simp only [count, countP_pos_iff, beq_iff_eq, exists_eq_right]
|
||||
|
||||
@[simp] theorem one_le_count_iff {a : α} {l : Array α} : 1 ≤ count a l ↔ a ∈ l :=
|
||||
count_pos_iff
|
||||
|
||||
theorem count_eq_zero_of_not_mem {a : α} {l : Array α} (h : a ∉ l) : count a l = 0 :=
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff.1 (Nat.pos_of_ne_zero h')
|
||||
|
||||
theorem not_mem_of_count_eq_zero {a : α} {l : Array α} (h : count a l = 0) : a ∉ l :=
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff.2 h') h.symm
|
||||
|
||||
theorem count_eq_zero {l : Array α} : count a l = 0 ↔ a ∉ l :=
|
||||
⟨not_mem_of_count_eq_zero, count_eq_zero_of_not_mem⟩
|
||||
|
||||
theorem count_eq_size {l : Array α} : count a l = l.size ↔ ∀ b ∈ l, a = b := by
|
||||
rw [count, countP_eq_size]
|
||||
refine ⟨fun h b hb => Eq.symm ?_, fun h b hb => ?_⟩
|
||||
· simpa using h b hb
|
||||
· rw [h b hb, beq_self_eq_true]
|
||||
|
||||
@[simp] theorem count_mkArray_self (a : α) (n : Nat) : count a (mkArray n a) = n := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
theorem count_mkArray (a b : α) (n : Nat) : count a (mkArray n b) = if b == a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.count_replicate]
|
||||
|
||||
theorem filter_beq (l : Array α) (a : α) : l.filter (· == a) = mkArray (count a l) a := by
|
||||
cases l
|
||||
simp [List.filter_beq]
|
||||
|
||||
theorem filter_eq {α} [DecidableEq α] (l : Array α) (a : α) : l.filter (· = a) = mkArray (count a l) a :=
|
||||
filter_beq l a
|
||||
|
||||
theorem mkArray_count_eq_of_count_eq_size {l : Array α} (h : count a l = l.size) :
|
||||
mkArray (count a l) a = l := by
|
||||
cases l
|
||||
rw [← toList_inj]
|
||||
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
|
||||
|
||||
@[simp] theorem count_filter {l : Array α} (h : p a) : count a (filter p l) = count a l := by
|
||||
cases l
|
||||
simp [List.count_filter, h]
|
||||
|
||||
theorem count_le_count_map [DecidableEq β] (l : Array α) (f : α → β) (x : α) :
|
||||
count x l ≤ count (f x) (map f l) := by
|
||||
cases l
|
||||
simp [List.count_le_count_map, countP_map]
|
||||
|
||||
theorem count_filterMap {α} [BEq β] (b : β) (f : α → Option β) (l : Array α) :
|
||||
count b (filterMap f l) = countP (fun a => f a == some b) l := by
|
||||
cases l
|
||||
simp [List.count_filterMap, countP_filterMap]
|
||||
|
||||
theorem count_flatMap {α} [BEq β] (l : Array α) (f : α → Array β) (x : β) :
|
||||
count x (l.flatMap f) = sum (map (count x ∘ f) l) := by
|
||||
simp [count_eq_countP, countP_flatMap, Function.comp_def]
|
||||
|
||||
-- FIXME these theorems can be restored once `List.erase` and `Array.erase` have been related.
|
||||
|
||||
-- theorem count_erase (a b : α) (l : Array α) : count a (l.erase b) = count a l - if b == a then 1 else 0 := by
|
||||
-- sorry
|
||||
|
||||
-- @[simp] theorem count_erase_self (a : α) (l : Array α) :
|
||||
-- count a (l.erase a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
|
||||
|
||||
-- @[simp] theorem count_erase_of_ne (ab : a ≠ b) (l : Array α) : count a (l.erase b) = count a l := by
|
||||
-- rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
|
||||
|
||||
end count
|
||||
@@ -74,12 +74,12 @@ theorem findSome?_append {l₁ l₂ : Array α} : (l₁ ++ l₂).findSome? f = (
|
||||
|
||||
theorem getElem?_zero_flatten (L : Array (Array α)) :
|
||||
(flatten L)[0]? = L.findSome? fun l => l[0]? := by
|
||||
cases L using array_array_induction
|
||||
cases L using array₂_induction
|
||||
simp [← List.head?_eq_getElem?, List.head?_flatten, List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem getElem_zero_flatten.proof {L : Array (Array α)} (h : 0 < L.flatten.size) :
|
||||
(L.findSome? fun l => l[0]?).isSome := by
|
||||
cases L using array_array_induction
|
||||
cases L using array₂_induction
|
||||
simp only [List.findSome?_toArray, List.findSome?_map, Function.comp_def, List.getElem?_toArray,
|
||||
List.findSome?_isSome_iff, isSome_getElem?]
|
||||
simp only [flatten_toArray_map_toArray, size_toArray, List.length_flatten,
|
||||
@@ -95,7 +95,7 @@ theorem getElem_zero_flatten {L : Array (Array α)} (h) :
|
||||
|
||||
theorem back?_flatten {L : Array (Array α)} :
|
||||
(flatten L).back? = (L.findSomeRev? fun l => l.back?) := by
|
||||
cases L using array_array_induction
|
||||
cases L using array₂_induction
|
||||
simp [List.getLast?_flatten, ← List.map_reverse, List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
|
||||
@@ -203,7 +203,7 @@ theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h ∈ xs := by
|
||||
|
||||
@[simp] theorem find?_flatten (xs : Array (Array α)) (p : α → Bool) :
|
||||
xs.flatten.find? p = xs.findSome? (·.find? p) := by
|
||||
cases xs using array_array_induction
|
||||
cases xs using array₂_induction
|
||||
simp [List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem find?_flatten_eq_none {xs : Array (Array α)} {p : α → Bool} :
|
||||
@@ -220,7 +220,7 @@ theorem find?_flatten_eq_some {xs : Array (Array α)} {p : α → Bool} {a : α}
|
||||
p a ∧ ∃ (as : Array (Array α)) (ys zs : Array α) (bs : Array (Array α)),
|
||||
xs = as.push (ys.push a ++ zs) ++ bs ∧
|
||||
(∀ a ∈ as, ∀ x ∈ a, !p x) ∧ (∀ x ∈ ys, !p x) := by
|
||||
cases xs using array_array_induction
|
||||
cases xs using array₂_induction
|
||||
simp only [flatten_toArray_map_toArray, List.find?_toArray, List.find?_flatten_eq_some]
|
||||
simp only [Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, and_congr_right_iff]
|
||||
intro w
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
namespace Array
|
||||
@@ -12,81 +13,82 @@ namespace Array
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
-- This could also be proved from `SatisfiesM_mapIdxM` in Batteries.
|
||||
theorem mapFinIdx_induction (as : Array α) (f : Fin as.size → α → β)
|
||||
theorem mapFinIdx_induction (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → β)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
(p : (i : Nat) → β → (h : i < as.size) → Prop)
|
||||
(hs : ∀ i h, motive i → p i (f i as[i] h) h ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (Array.mapFinIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapFinIdx as f)[i]) := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p ⟨i, h⟩ bs[i]) (hm : motive j) :
|
||||
∀ i h, p i ((Array.mapFinIdx as f)[i]) h := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p i bs[i] h) (hm : motive j) :
|
||||
let arr : Array β := Array.mapFinIdxM.map (m := Id) as f i j h bs
|
||||
motive as.size ∧ ∃ eq : arr.size = as.size, ∀ i h, p ⟨i, h⟩ arr[i] := by
|
||||
motive as.size ∧ ∃ eq : arr.size = as.size, ∀ i h, p i arr[i] h := by
|
||||
induction i generalizing j bs with simp [mapFinIdxM.map]
|
||||
| zero =>
|
||||
have := (Nat.zero_add _).symm.trans h
|
||||
exact ⟨this ▸ hm, h₁ ▸ this, fun _ _ => h₂ ..⟩
|
||||
| succ i ih =>
|
||||
apply @ih (bs.push (f ⟨j, by omega⟩ as[j])) (j + 1) (by omega) (by simp; omega)
|
||||
apply @ih (bs.push (f j as[j] (by omega))) (j + 1) (by omega) (by simp; omega)
|
||||
· intro i i_lt h'
|
||||
rw [getElem_push]
|
||||
split
|
||||
· apply h₂
|
||||
· simp only [size_push] at h'
|
||||
obtain rfl : i = j := by omega
|
||||
apply (hs ⟨i, by omega⟩ hm).1
|
||||
· exact (hs ⟨j, by omega⟩ hm).2
|
||||
apply (hs i (by omega) hm).1
|
||||
· exact (hs j (by omega) hm).2
|
||||
simp [mapFinIdx, mapFinIdxM]; exact go rfl nofun h0
|
||||
|
||||
theorem mapFinIdx_spec (as : Array α) (f : Fin as.size → α → β)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
theorem mapFinIdx_spec (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → β)
|
||||
(p : (i : Nat) → β → (h : i < as.size) → Prop) (hs : ∀ i h, p i (f i as[i] h) h) :
|
||||
∃ eq : (Array.mapFinIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapFinIdx as f)[i]) :=
|
||||
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
∀ i h, p i ((Array.mapFinIdx as f)[i]) h :=
|
||||
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapFinIdx (a : Array α) (f : Fin a.size → α → β) : (a.mapFinIdx f).size = a.size :=
|
||||
(mapFinIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
@[simp] theorem size_mapFinIdx (a : Array α) (f : (i : Nat) → α → (h : i < a.size) → β) :
|
||||
(a.mapFinIdx f).size = a.size :=
|
||||
(mapFinIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
|
||||
|
||||
@[simp] theorem size_zipWithIndex (as : Array α) : as.zipWithIndex.size = as.size :=
|
||||
Array.size_mapFinIdx _ _
|
||||
|
||||
@[simp] theorem getElem_mapFinIdx (a : Array α) (f : Fin a.size → α → β) (i : Nat)
|
||||
@[simp] theorem getElem_mapFinIdx (a : Array α) (f : (i : Nat) → α → (h : i < a.size) → β) (i : Nat)
|
||||
(h : i < (mapFinIdx a f).size) :
|
||||
(a.mapFinIdx f)[i] = f ⟨i, by simp_all⟩ (a[i]'(by simp_all)) :=
|
||||
(mapFinIdx_spec _ _ (fun i b => b = f i a[i]) fun _ => rfl).2 i _
|
||||
(a.mapFinIdx f)[i] = f i (a[i]'(by simp_all)) (by simp_all) :=
|
||||
(mapFinIdx_spec _ _ (fun i b h => b = f i a[i] h) fun _ _ => rfl).2 i _
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx (a : Array α) (f : Fin a.size → α → β) (i : Nat) :
|
||||
@[simp] theorem getElem?_mapFinIdx (a : Array α) (f : (i : Nat) → α → (h : i < a.size) → β) (i : Nat) :
|
||||
(a.mapFinIdx f)[i]? =
|
||||
a[i]?.pbind fun b h => f ⟨i, (getElem?_eq_some_iff.1 h).1⟩ b := by
|
||||
a[i]?.pbind fun b h => f i b (getElem?_eq_some_iff.1 h).1 := by
|
||||
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem toList_mapFinIdx (a : Array α) (f : Fin a.size → α → β) :
|
||||
(a.mapFinIdx f).toList = a.toList.mapFinIdx (fun i a => f ⟨i, by simp⟩ a) := by
|
||||
@[simp] theorem toList_mapFinIdx (a : Array α) (f : (i : Nat) → α → (h : i < a.size) → β) :
|
||||
(a.mapFinIdx f).toList = a.toList.mapFinIdx (fun i a h => f i a (by simpa)) := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
theorem mapIdx_induction (f : Nat → α → β) (as : Array α)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
(p : (i : Nat) → β → (h : i < as.size) → Prop)
|
||||
(hs : ∀ i h, motive i → p i (f i as[i]) h ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
mapFinIdx_induction as (fun i a => f i a) motive h0 p hs
|
||||
∀ i h, p i ((as.mapIdx f)[i]) h :=
|
||||
mapFinIdx_induction as (fun i a _ => f i a) motive h0 p hs
|
||||
|
||||
theorem mapIdx_spec (f : Nat → α → β) (as : Array α)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
(p : (i : Nat) → β → (h : i < as.size) → Prop) (hs : ∀ i h, p i (f i as[i]) h) :
|
||||
∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
∀ i h, p i ((as.mapIdx f)[i]) h :=
|
||||
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapIdx (f : Nat → α → β) (as : Array α) : (as.mapIdx f).size = as.size :=
|
||||
(mapIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
(mapIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
|
||||
|
||||
@[simp] theorem getElem_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat)
|
||||
(h : i < (as.mapIdx f).size) :
|
||||
(as.mapIdx f)[i] = f i (as[i]'(by simp_all)) :=
|
||||
(mapIdx_spec _ _ (fun i b => b = f i as[i]) fun _ => rfl).2 i (by simp_all)
|
||||
(mapIdx_spec _ _ (fun i b h => b = f i as[i]) fun _ _ => rfl).2 i (by simp_all)
|
||||
|
||||
@[simp] theorem getElem?_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat) :
|
||||
(as.mapIdx f)[i]? =
|
||||
@@ -101,7 +103,7 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem mapFinIdx_toArray (l : List α) (f : Fin l.length → α → β) :
|
||||
@[simp] theorem mapFinIdx_toArray (l : List α) (f : (i : Nat) → α → (h : i < l.length) → β) :
|
||||
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
@@ -110,3 +112,293 @@ namespace List
|
||||
ext <;> simp
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### zipWithIndex -/
|
||||
|
||||
@[simp] theorem getElem_zipWithIndex (a : Array α) (i : Nat) (h : i < a.zipWithIndex.size) :
|
||||
(a.zipWithIndex)[i] = (a[i]'(by simp_all), i) := by
|
||||
simp [zipWithIndex]
|
||||
|
||||
@[simp] theorem zipWithIndex_toArray {l : List α} :
|
||||
l.toArray.zipWithIndex = (l.enum.map fun (i, x) => (x, i)).toArray := by
|
||||
ext i hi₁ hi₂ <;> simp
|
||||
|
||||
@[simp] theorem toList_zipWithIndex (a : Array α) :
|
||||
a.zipWithIndex.toList = a.toList.enum.map (fun (i, a) => (a, i)) := by
|
||||
rcases a with ⟨a⟩
|
||||
simp
|
||||
|
||||
theorem mk_mem_zipWithIndex_iff_getElem? {x : α} {i : Nat} {l : Array α} :
|
||||
(x, i) ∈ l.zipWithIndex ↔ l[i]? = x := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [zipWithIndex_toArray, mem_toArray, List.mem_map, Prod.mk.injEq, Prod.exists,
|
||||
List.mk_mem_enum_iff_getElem?, List.getElem?_toArray]
|
||||
constructor
|
||||
· rintro ⟨a, b, h, rfl, rfl⟩
|
||||
exact h
|
||||
· intro h
|
||||
exact ⟨i, x, by simp [h]⟩
|
||||
|
||||
theorem mem_enum_iff_getElem? {x : α × Nat} {l : Array α} : x ∈ l.zipWithIndex ↔ l[x.2]? = some x.1 :=
|
||||
mk_mem_zipWithIndex_iff_getElem?
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
@[congr] theorem mapFinIdx_congr {xs ys : Array α} (w : xs = ys)
|
||||
(f : (i : Nat) → α → (h : i < xs.size) → β) :
|
||||
mapFinIdx xs f = mapFinIdx ys (fun i a h => f i a (by simp [w]; omega)) := by
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_empty {f : (i : Nat) → α → (h : i < 0) → β} : mapFinIdx #[] f = #[] :=
|
||||
rfl
|
||||
|
||||
theorem mapFinIdx_eq_ofFn {as : Array α} {f : (i : Nat) → α → (h : i < as.size) → β} :
|
||||
as.mapFinIdx f = Array.ofFn fun i : Fin as.size => f i as[i] i.2 := by
|
||||
cases as
|
||||
simp [List.mapFinIdx_eq_ofFn]
|
||||
|
||||
theorem mapFinIdx_append {K L : Array α} {f : (i : Nat) → α → (h : i < (K ++ L).size) → β} :
|
||||
(K ++ L).mapFinIdx f =
|
||||
K.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
|
||||
L.mapFinIdx (fun i a h => f (i + K.size) a (by simp; omega)) := by
|
||||
cases K
|
||||
cases L
|
||||
simp [List.mapFinIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_push {l : Array α} {a : α} {f : (i : Nat) → α → (h : i < (l.push a).size) → β} :
|
||||
mapFinIdx (l.push a) f =
|
||||
(mapFinIdx l (fun i a h => f i a (by simp; omega))).push (f l.size a (by simp)) := by
|
||||
simp [← append_singleton, mapFinIdx_append]
|
||||
|
||||
theorem mapFinIdx_singleton {a : α} {f : (i : Nat) → α → (h : i < 1) → β} :
|
||||
#[a].mapFinIdx f = #[f 0 a (by simp)] := by
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_zipWithIndex_map {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f = l.zipWithIndex.attach.map
|
||||
fun ⟨⟨x, i⟩, m⟩ =>
|
||||
f i x (by simp [mk_mem_zipWithIndex_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_eq_empty_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f = #[] ↔ l = #[] := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_ne_empty_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f ≠ #[] ↔ l ≠ #[] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapFinIdx {b : β} {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β}
|
||||
(h : b ∈ l.mapFinIdx f) : ∃ (i : Nat) (h : i < l.size), f i l[i] h = b := by
|
||||
rcases l with ⟨l⟩
|
||||
exact List.exists_of_mem_mapFinIdx (by simpa using h)
|
||||
|
||||
@[simp] theorem mem_mapFinIdx {b : β} {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
b ∈ l.mapFinIdx f ↔ ∃ (i : Nat) (h : i < l.size), f i l[i] h = b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f = l' ↔ ∃ h : l'.size = l.size, ∀ (i : Nat) (h : i < l.size), l'[i] = f i l[i] h := by
|
||||
rcases l with ⟨l⟩
|
||||
rcases l' with ⟨l'⟩
|
||||
simpa using List.mapFinIdx_eq_iff
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_singleton_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} {b : β} :
|
||||
l.mapFinIdx f = #[b] ↔ ∃ (a : α) (w : l = #[a]), f 0 a (by simp [w]) = b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_append_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} {l₁ l₂ : Array β} :
|
||||
l.mapFinIdx f = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : Array α) (l₂' : Array α) (w : l = l₁' ++ l₂'),
|
||||
l₁'.mapFinIdx (fun i a h => f i a (by simp [w]; omega)) = l₁ ∧
|
||||
l₂'.mapFinIdx (fun i a h => f (i + l₁'.size) a (by simp [w]; omega)) = l₂ := by
|
||||
rcases l with ⟨l⟩
|
||||
rcases l₁ with ⟨l₁⟩
|
||||
rcases l₂ with ⟨l₂⟩
|
||||
simp only [List.mapFinIdx_toArray, List.append_toArray, mk.injEq, List.mapFinIdx_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, rfl⟩
|
||||
refine ⟨l₁.toArray, l₂.toArray, by simp_all⟩
|
||||
· rintro ⟨⟨l₁⟩, ⟨l₂⟩, rfl, h₁, h₂⟩
|
||||
simp [← toList_inj] at h₁ h₂
|
||||
obtain rfl := h₁
|
||||
obtain rfl := h₂
|
||||
refine ⟨l₁, l₂, by simp_all⟩
|
||||
|
||||
theorem mapFinIdx_eq_push_iff {l : Array α} {b : β} {f : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f = l₂.push b ↔
|
||||
∃ (l₁ : Array α) (a : α) (w : l = l₁.push a),
|
||||
l₁.mapFinIdx (fun i a h => f i a (by simp [w]; omega)) = l₂ ∧ b = f (l.size - 1) a (by simp [w]) := by
|
||||
rw [push_eq_append, mapFinIdx_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, h₂⟩
|
||||
simp only [mapFinIdx_eq_singleton_iff, Nat.zero_add] at h₂
|
||||
obtain ⟨a, rfl, rfl⟩ := h₂
|
||||
exact ⟨l₁, a, by simp⟩
|
||||
· rintro ⟨l₁, a, rfl, rfl, rfl⟩
|
||||
exact ⟨l₁, #[a], by simp⟩
|
||||
|
||||
theorem mapFinIdx_eq_mapFinIdx_iff {l : Array α} {f g : (i : Nat) → α → (h : i < l.size) → β} :
|
||||
l.mapFinIdx f = l.mapFinIdx g ↔ ∀ (i : Nat) (h : i < l.size), f i l[i] h = g i l[i] h := by
|
||||
rw [eq_comm, mapFinIdx_eq_iff]
|
||||
simp
|
||||
|
||||
@[simp] theorem mapFinIdx_mapFinIdx {l : Array α}
|
||||
{f : (i : Nat) → α → (h : i < l.size) → β}
|
||||
{g : (i : Nat) → β → (h : i < (l.mapFinIdx f).size) → γ} :
|
||||
(l.mapFinIdx f).mapFinIdx g = l.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapFinIdx_eq_mkArray_iff {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} {b : β} :
|
||||
l.mapFinIdx f = mkArray l.size b ↔ ∀ (i : Nat) (h : i < l.size), f i l[i] h = b := by
|
||||
rcases l with ⟨l⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapFinIdx_eq_replicate_iff]
|
||||
|
||||
@[simp] theorem mapFinIdx_reverse {l : Array α} {f : (i : Nat) → α → (h : i < l.reverse.size) → β} :
|
||||
l.reverse.mapFinIdx f = (l.mapFinIdx (fun i a h => f (l.size - 1 - i) a (by simp; omega))).reverse := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapFinIdx_reverse]
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_empty {f : Nat → α → β} : mapIdx f #[] = #[] :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_mapIdx {l : Array α} {f : (i : Nat) → α → (h : i < l.size) → β} {g : Nat → α → β}
|
||||
(h : ∀ (i : Nat) (h : i < l.size), f i l[i] h = g i l[i]) :
|
||||
l.mapFinIdx f = l.mapIdx g := by
|
||||
simp_all [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapFinIdx {l : Array α} {f : Nat → α → β} :
|
||||
l.mapIdx f = l.mapFinIdx (fun i a _ => f i a) := by
|
||||
simp [mapFinIdx_eq_mapIdx]
|
||||
|
||||
theorem mapIdx_eq_zipWithIndex_map {l : Array α} {f : Nat → α → β} :
|
||||
l.mapIdx f = l.zipWithIndex.map fun ⟨a, i⟩ => f i a := by
|
||||
ext <;> simp
|
||||
|
||||
theorem mapIdx_append {K L : Array α} :
|
||||
(K ++ L).mapIdx f = K.mapIdx f ++ L.mapIdx fun i => f (i + K.size) := by
|
||||
rcases K with ⟨K⟩
|
||||
rcases L with ⟨L⟩
|
||||
simp [List.mapIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_push {l : Array α} {a : α} :
|
||||
mapIdx f (l.push a) = (mapIdx f l).push (f l.size a) := by
|
||||
simp [← append_singleton, mapIdx_append]
|
||||
|
||||
theorem mapIdx_singleton {a : α} : mapIdx f #[a] = #[f 0 a] := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_eq_empty_iff {l : Array α} : mapIdx f l = #[] ↔ l = #[] := by
|
||||
rcases l with ⟨l⟩
|
||||
simp
|
||||
|
||||
theorem mapIdx_ne_empty_iff {l : Array α} :
|
||||
mapIdx f l ≠ #[] ↔ l ≠ #[] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapIdx {b : β} {l : Array α}
|
||||
(h : b ∈ mapIdx f l) : ∃ (i : Nat) (h : i < l.size), f i l[i] = b := by
|
||||
rw [mapIdx_eq_mapFinIdx] at h
|
||||
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
|
||||
|
||||
@[simp] theorem mem_mapIdx {b : β} {l : Array α} :
|
||||
b ∈ mapIdx f l ↔ ∃ (i : Nat) (h : i < l.size), f i l[i] = b := by
|
||||
constructor
|
||||
· intro h
|
||||
exact exists_of_mem_mapIdx h
|
||||
· rintro ⟨i, h, rfl⟩
|
||||
rw [mem_iff_getElem]
|
||||
exact ⟨i, by simpa using h, by simp⟩
|
||||
|
||||
theorem mapIdx_eq_push_iff {l : Array α} {b : β} :
|
||||
mapIdx f l = l₂.push b ↔
|
||||
∃ (a : α) (l₁ : Array α), l = l₁.push a ∧ mapIdx f l₁ = l₂ ∧ f l₁.size a = b := by
|
||||
rw [mapIdx_eq_mapFinIdx, mapFinIdx_eq_push_iff]
|
||||
simp only [mapFinIdx_eq_mapIdx, exists_and_left, exists_prop]
|
||||
constructor
|
||||
· rintro ⟨l₁, rfl, a, rfl, rfl⟩
|
||||
exact ⟨a, l₁, by simp⟩
|
||||
· rintro ⟨a, l₁, rfl, rfl, rfl⟩
|
||||
exact ⟨l₁, rfl, a, by simp⟩
|
||||
|
||||
@[simp] theorem mapIdx_eq_singleton_iff {l : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f l = #[b] ↔ ∃ (a : α), l = #[a] ∧ f 0 a = b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapIdx_eq_singleton_iff]
|
||||
|
||||
theorem mapIdx_eq_append_iff {l : Array α} {f : Nat → α → β} {l₁ l₂ : Array β} :
|
||||
mapIdx f l = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : Array α) (l₂' : Array α), l = l₁' ++ l₂' ∧
|
||||
l₁'.mapIdx f = l₁ ∧
|
||||
l₂'.mapIdx (fun i => f (i + l₁'.size)) = l₂ := by
|
||||
rcases l with ⟨l⟩
|
||||
rcases l₁ with ⟨l₁⟩
|
||||
rcases l₂ with ⟨l₂⟩
|
||||
simp only [List.mapIdx_toArray, List.append_toArray, mk.injEq, List.mapIdx_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, rfl⟩
|
||||
exact ⟨l₁.toArray, l₂.toArray, by simp⟩
|
||||
· rintro ⟨⟨l₁⟩, ⟨l₂⟩, rfl, h₁, h₂⟩
|
||||
simp only [List.mapIdx_toArray, mk.injEq, size_toArray] at h₁ h₂
|
||||
obtain rfl := h₁
|
||||
obtain rfl := h₂
|
||||
exact ⟨l₁, l₂, by simp⟩
|
||||
|
||||
theorem mapIdx_eq_iff {l : Array α} : mapIdx f l = l' ↔ ∀ i : Nat, l'[i]? = l[i]?.map (f i) := by
|
||||
rcases l with ⟨l⟩
|
||||
rcases l' with ⟨l'⟩
|
||||
simp [List.mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapIdx_iff {l : Array α} :
|
||||
mapIdx f l = mapIdx g l ↔ ∀ i : Nat, (h : i < l.size) → f i l[i] = g i l[i] := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapIdx_eq_mapIdx_iff]
|
||||
|
||||
@[simp] theorem mapIdx_set {l : Array α} {i : Nat} {h : i < l.size} {a : α} :
|
||||
(l.set i a).mapIdx f = (l.mapIdx f).set i (f i a) (by simpa) := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapIdx_set]
|
||||
|
||||
@[simp] theorem mapIdx_setIfInBounds {l : Array α} {i : Nat} {a : α} :
|
||||
(l.setIfInBounds i a).mapIdx f = (l.mapIdx f).setIfInBounds i (f i a) := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapIdx_set]
|
||||
|
||||
@[simp] theorem back?_mapIdx {l : Array α} {f : Nat → α → β} :
|
||||
(mapIdx f l).back? = (l.back?).map (f (l.size - 1)) := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.getLast?_mapIdx]
|
||||
|
||||
@[simp] theorem mapIdx_mapIdx {l : Array α} {f : Nat → α → β} {g : Nat → β → γ} :
|
||||
(l.mapIdx f).mapIdx g = l.mapIdx (fun i => g i ∘ f i) := by
|
||||
simp [mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mkArray_iff {l : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f l = mkArray l.size b ↔ ∀ (i : Nat) (h : i < l.size), f i l[i] = b := by
|
||||
rcases l with ⟨l⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapIdx_eq_replicate_iff]
|
||||
|
||||
@[simp] theorem mapIdx_reverse {l : Array α} {f : Nat → α → β} :
|
||||
l.reverse.mapIdx f = (mapIdx (fun i => f (l.size - 1 - i)) l).reverse := by
|
||||
rcases l with ⟨l⟩
|
||||
simp [List.mapIdx_reverse]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -669,4 +669,11 @@ def ofBoolListLE : (bs : List Bool) → BitVec bs.length
|
||||
| [] => 0#0
|
||||
| b :: bs => concat (ofBoolListLE bs) b
|
||||
|
||||
/- ### reverse -/
|
||||
|
||||
/-- Reverse the bits in a bitvector. -/
|
||||
def reverse : {w : Nat} → BitVec w → BitVec w
|
||||
| 0, x => x
|
||||
| w + 1, x => concat (reverse (x.truncate w)) (x.msb)
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -631,6 +631,13 @@ theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
· simp
|
||||
· omega
|
||||
|
||||
theorem getMsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
(x * y).getMsbD i = (mulRec x y w).getMsbD i := by
|
||||
simp only [mulRec_eq_mul_signExtend_setWidth]
|
||||
rw [setWidth_setWidth_of_le]
|
||||
· simp
|
||||
· omega
|
||||
|
||||
theorem getElem_mul {x y : BitVec w} {i : Nat} (h : i < w) :
|
||||
(x * y)[i] = (mulRec x y w)[i] := by
|
||||
simp [mulRec_eq_mul_signExtend_setWidth]
|
||||
@@ -1084,6 +1091,21 @@ theorem divRec_succ' (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec m args input := by
|
||||
simp [divRec_succ, divSubtractShift]
|
||||
|
||||
theorem getElem_udiv (n d : BitVec w) (hy : 0#w < d) (i : Nat) (hi : i < w) :
|
||||
(n / d)[i] = (divRec w {n, d} (DivModState.init w)).q[i] := by
|
||||
rw [udiv_eq_divRec (by assumption)]
|
||||
|
||||
theorem getLsbD_udiv (n d : BitVec w) (hy : 0#w < d) (i : Nat) :
|
||||
(n / d).getLsbD i = (decide (i < w) && (divRec w {n, d} (DivModState.init w)).q.getLsbD i) := by
|
||||
by_cases hi : i < w
|
||||
· simp [udiv_eq_divRec (by assumption)]
|
||||
omega
|
||||
· simp_all
|
||||
|
||||
theorem getMsbD_udiv (n d : BitVec w) (hd : 0#w < d) (i : Nat) :
|
||||
(n / d).getMsbD i = (decide (i < w) && (divRec w {n, d} (DivModState.init w)).q.getMsbD i) := by
|
||||
simp [getMsbD_eq_getLsbD, getLsbD_udiv, udiv_eq_divRec (by assumption)]
|
||||
|
||||
/- ### Arithmetic shift right (sshiftRight) recurrence -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -905,6 +905,16 @@ instance : Std.LawfulCommIdentity (α := BitVec n) (· ||| · ) (0#n) where
|
||||
ext i h
|
||||
simp [h]
|
||||
|
||||
theorem extractLsb'_or {x y : BitVec w} {start len : Nat} :
|
||||
(x ||| y).extractLsb' start len = (x.extractLsb' start len) ||| (y.extractLsb' start len) := by
|
||||
ext i hi
|
||||
simp [hi]
|
||||
|
||||
theorem extractLsb_or {x : BitVec w} {hi lo : Nat} :
|
||||
(x ||| y).extractLsb lo hi = (x.extractLsb lo hi) ||| (y.extractLsb lo hi) := by
|
||||
ext k hk
|
||||
simp [hk, show k ≤ lo - hi by omega]
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem toNat_and (x y : BitVec v) :
|
||||
@@ -978,6 +988,16 @@ instance : Std.LawfulCommIdentity (α := BitVec n) (· &&& · ) (allOnes n) wher
|
||||
ext i h
|
||||
simp [h]
|
||||
|
||||
theorem extractLsb'_and {x y : BitVec w} {start len : Nat} :
|
||||
(x &&& y).extractLsb' start len = (x.extractLsb' start len) &&& (y.extractLsb' start len) := by
|
||||
ext i hi
|
||||
simp [hi]
|
||||
|
||||
theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
|
||||
(x &&& y).extractLsb lo hi = (x.extractLsb lo hi) &&& (y.extractLsb lo hi) := by
|
||||
ext k hk
|
||||
simp [hk, show k ≤ lo - hi by omega]
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp] theorem toNat_xor (x y : BitVec v) :
|
||||
@@ -1043,6 +1063,16 @@ instance : Std.LawfulCommIdentity (α := BitVec n) (· ^^^ · ) (0#n) where
|
||||
ext i
|
||||
simp
|
||||
|
||||
theorem extractLsb'_xor {x y : BitVec w} {start len : Nat} :
|
||||
(x ^^^ y).extractLsb' start len = (x.extractLsb' start len) ^^^ (y.extractLsb' start len) := by
|
||||
ext i hi
|
||||
simp [hi]
|
||||
|
||||
theorem extractLsb_xor {x : BitVec w} {hi lo : Nat} :
|
||||
(x ^^^ y).extractLsb lo hi = (x.extractLsb lo hi) ^^^ (y.extractLsb lo hi) := by
|
||||
ext k hk
|
||||
simp [hk, show k ≤ lo - hi by omega]
|
||||
|
||||
/-! ### not -/
|
||||
|
||||
theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
|
||||
@@ -1149,6 +1179,31 @@ theorem getMsb_not {x : BitVec w} :
|
||||
@[simp] theorem msb_not {x : BitVec w} : (~~~x).msb = (decide (0 < w) && !x.msb) := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
/--
|
||||
Negating `x` and then extracting [start..start+len) is the same as extracting and then negating,
|
||||
as long as the range [start..start+len) is in bounds.
|
||||
See that if the index is out-of-bounds, then `extractLsb` will return `false`,
|
||||
which makes the operation not commute.
|
||||
-/
|
||||
theorem extractLsb'_not_of_lt {x : BitVec w} {start len : Nat} (h : start + len < w) :
|
||||
(~~~ x).extractLsb' start len = ~~~ (x.extractLsb' start len) := by
|
||||
ext i hi
|
||||
simp [hi]
|
||||
omega
|
||||
|
||||
/--
|
||||
Negating `x` and then extracting [lo:hi] is the same as extracting and then negating.
|
||||
For the extraction to be well-behaved,
|
||||
we need the range [lo:hi] to be a valid closed interval inside the bitvector:
|
||||
1. `lo ≤ hi` for the interval to be a well-formed closed interval.
|
||||
2. `hi < w`, for the interval to be contained inside the bitvector.
|
||||
-/
|
||||
theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi : hi < w) :
|
||||
(~~~ x).extractLsb hi lo = ~~~ (x.extractLsb hi lo) := by
|
||||
ext k hk
|
||||
simp [hk, show k ≤ hi - lo by omega]
|
||||
omega
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
|
||||
@@ -1294,11 +1349,6 @@ theorem allOnes_shiftLeft_or_shiftLeft {x : BitVec w} {n : Nat} :
|
||||
BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by
|
||||
simp [← shiftLeft_or_distrib]
|
||||
|
||||
@[deprecated shiftLeft_add (since := "2024-06-02")]
|
||||
theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
|
||||
(x <<< n) <<< m = x <<< (n + m) := by
|
||||
rw [shiftLeft_add]
|
||||
|
||||
/-! ### shiftLeft reductions from BitVec to Nat -/
|
||||
|
||||
@[simp]
|
||||
@@ -1318,6 +1368,13 @@ theorem getElem_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} (h : i
|
||||
(x <<< y)[i] = (!decide (i < y.toNat) && x.getLsbD (i - y.toNat)) := by
|
||||
simp
|
||||
|
||||
@[simp] theorem shiftLeft_eq_zero {x : BitVec w} {n : Nat} (hn : w ≤ n) : x <<< n = 0#w := by
|
||||
ext i hi
|
||||
simp [hn, hi]
|
||||
omega
|
||||
|
||||
theorem shiftLeft_ofNat_eq {x : BitVec w} {k : Nat} : x <<< (BitVec.ofNat w k) = x <<< (k % 2^w) := rfl
|
||||
|
||||
/-! ### ushiftRight -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
|
||||
@@ -1449,6 +1506,8 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
theorem ushiftRight_eq' (x : BitVec w₁) (y : BitVec w₂) :
|
||||
x >>> y = x >>> y.toNat := by rfl
|
||||
|
||||
theorem ushiftRight_ofNat_eq {x : BitVec w} {k : Nat} : x >>> (BitVec.ofNat w k) = x >>> (k % 2^w) := rfl
|
||||
|
||||
/-! ### sshiftRight -/
|
||||
|
||||
theorem sshiftRight_eq {x : BitVec n} {i : Nat} :
|
||||
@@ -1546,6 +1605,9 @@ theorem sshiftRight_or_distrib (x y : BitVec w) (n : Nat) :
|
||||
<;> by_cases w ≤ i
|
||||
<;> simp [*]
|
||||
|
||||
|
||||
theorem sshiftRight'_ofNat_eq_sshiftRight {x : BitVec w} {k : Nat} : x.sshiftRight' (BitVec.ofNat w k) = x.sshiftRight (k % 2^w) := rfl
|
||||
|
||||
/-- The msb after arithmetic shifting right equals the original msb. -/
|
||||
@[simp]
|
||||
theorem msb_sshiftRight {n : Nat} {x : BitVec w} :
|
||||
@@ -1946,10 +2008,24 @@ theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
|
||||
(x <<< n).msb = x.getMsbD n := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
@[deprecated shiftRight_add (since := "2024-06-02")]
|
||||
theorem shiftRight_shiftRight {w : Nat} (x : BitVec w) (n m : Nat) :
|
||||
(x >>> n) >>> m = x >>> (n + m) := by
|
||||
rw [shiftRight_add]
|
||||
theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||||
x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by
|
||||
ext i hi
|
||||
simp only [getLsbD_ushiftRight, getLsbD_cast, getLsbD_append, getLsbD_extractLsb', getLsbD_zero,
|
||||
Bool.if_false_right, Bool.and_self_left, Bool.iff_and_self, decide_eq_true_eq]
|
||||
intros h
|
||||
have := lt_of_getLsbD h
|
||||
omega
|
||||
|
||||
theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||||
x <<< n = (x.extractLsb' 0 (w - n) ++ 0#n).cast (by omega) := by
|
||||
ext i hi
|
||||
simp only [getLsbD_shiftLeft, hi, decide_true, Bool.true_and, getLsbD_cast, getLsbD_append,
|
||||
getLsbD_zero, getLsbD_extractLsb', Nat.zero_add, Bool.if_false_left]
|
||||
by_cases hi' : i < n
|
||||
· simp [hi']
|
||||
· simp [hi']
|
||||
omega
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
@@ -2063,6 +2139,32 @@ theorem eq_msb_cons_setWidth (x : BitVec (w+1)) : x = (cons x.msb (x.setWidth w)
|
||||
ext i
|
||||
simp [cons]
|
||||
|
||||
|
||||
theorem cons_append (x : BitVec w₁) (y : BitVec w₂) (a : Bool) :
|
||||
(cons a x) ++ y = (cons a (x ++ y)).cast (by omega) := by
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_append, toNat_cons, toNat_cast]
|
||||
rw [Nat.shiftLeft_add, Nat.shiftLeft_or_distrib, Nat.or_assoc]
|
||||
|
||||
theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃) (a : Bool) :
|
||||
(cons a x) ++ y ++ z = (cons a (x ++ y ++ z)).cast (by omega) := by
|
||||
ext i h
|
||||
simp only [cons, getLsbD_append, getLsbD_cast, getLsbD_ofBool, cast_cast]
|
||||
by_cases h₀ : i < w₁ + w₂ + w₃
|
||||
· simp only [h₀, ↓reduceIte]
|
||||
by_cases h₁ : i < w₃
|
||||
· simp [h₁]
|
||||
· simp only [h₁, ↓reduceIte]
|
||||
by_cases h₂ : i - w₃ < w₂
|
||||
· simp [h₂]
|
||||
· simp [h₂]
|
||||
omega
|
||||
· simp only [show ¬i - w₃ - w₂ < w₁ by omega, ↓reduceIte, show i - w₃ - w₂ - w₁ = 0 by omega,
|
||||
decide_true, Bool.true_and, h₀, show i - (w₁ + w₂ + w₃) = 0 by omega]
|
||||
by_cases h₂ : i < w₃
|
||||
· simp [h₂]; omega
|
||||
· simp [h₂]; omega
|
||||
|
||||
/-! ### concat -/
|
||||
|
||||
@[simp] theorem toNat_concat (x : BitVec w) (b : Bool) :
|
||||
@@ -3063,7 +3165,7 @@ theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
|
||||
· simp only [h₁, decide_true, Bool.true_and]
|
||||
have h₂ : (r + n) < 2 * (w + 1) := by omega
|
||||
congr 1
|
||||
rw [← Nat.sub_mul_eq_mod_of_lt_of_le (n := 1) (by omega) (by omega), Nat.mul_one]
|
||||
rw [← Nat.sub_mul_eq_mod_of_lt_of_le (n := 1) (by omega) (by omega)]
|
||||
omega
|
||||
· simp [h₁]
|
||||
|
||||
@@ -3312,6 +3414,11 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||||
apply Nat.pow_dvd_pow 2 (by omega)
|
||||
simp [Nat.mul_mod, hpow]
|
||||
|
||||
theorem twoPow_mul_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||||
(twoPow w i) * x = x <<< i := by
|
||||
rw [BitVec.mul_comm, mul_twoPow_eq_shiftLeft]
|
||||
|
||||
|
||||
theorem twoPow_zero {w : Nat} : twoPow w 0 = 1#w := by
|
||||
apply eq_of_toNat_eq
|
||||
simp
|
||||
@@ -3321,6 +3428,12 @@ theorem shiftLeft_eq_mul_twoPow (x : BitVec w) (n : Nat) :
|
||||
ext i
|
||||
simp [getLsbD_shiftLeft, Fin.is_lt, decide_true, Bool.true_and, mul_twoPow_eq_shiftLeft]
|
||||
|
||||
/-- 2^i * 2^j = 2^(i + j) with bitvectors as well -/
|
||||
theorem twoPow_mul_twoPow_eq {w : Nat} (i j : Nat) : twoPow w i * twoPow w j = twoPow w (i + j) := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
simp only [toNat_mul, toNat_twoPow]
|
||||
rw [← Nat.mul_mod, Nat.pow_add]
|
||||
|
||||
/--
|
||||
The unsigned division of `x` by `2^k` equals shifting `x` right by `k`,
|
||||
when `k` is less than the bitwidth `w`.
|
||||
@@ -3383,11 +3496,11 @@ theorem and_one_eq_setWidth_ofBool_getLsbD {x : BitVec w} :
|
||||
ext (_ | i) h <;> simp [Bool.and_comm]
|
||||
|
||||
@[simp]
|
||||
theorem replicate_zero_eq {x : BitVec w} : x.replicate 0 = 0#0 := by
|
||||
theorem replicate_zero {x : BitVec w} : x.replicate 0 = 0#0 := by
|
||||
simp [replicate]
|
||||
|
||||
@[simp]
|
||||
theorem replicate_succ_eq {x : BitVec w} :
|
||||
theorem replicate_succ {x : BitVec w} :
|
||||
x.replicate (n + 1) =
|
||||
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega) := by
|
||||
simp [replicate]
|
||||
@@ -3399,7 +3512,7 @@ theorem getLsbD_replicate {n w : Nat} (x : BitVec w) :
|
||||
induction n generalizing x
|
||||
case zero => simp
|
||||
case succ n ih =>
|
||||
simp only [replicate_succ_eq, getLsbD_cast, getLsbD_append]
|
||||
simp only [replicate_succ, getLsbD_cast, getLsbD_append]
|
||||
by_cases hi : i < w * (n + 1)
|
||||
· simp only [hi, decide_true, Bool.true_and]
|
||||
by_cases hi' : i < w * n
|
||||
@@ -3416,6 +3529,33 @@ theorem getElem_replicate {n w : Nat} (x : BitVec w) (h : i < w * n) :
|
||||
simp only [← getLsbD_eq_getElem, getLsbD_replicate]
|
||||
by_cases h' : w = 0 <;> simp [h'] <;> omega
|
||||
|
||||
theorem append_assoc {x₁ : BitVec w₁} {x₂ : BitVec w₂} {x₃ : BitVec w₃} :
|
||||
(x₁ ++ x₂) ++ x₃ = (x₁ ++ (x₂ ++ x₃)).cast (by omega) := by
|
||||
induction w₁ generalizing x₂ x₃
|
||||
case zero => simp
|
||||
case succ n ih =>
|
||||
specialize @ih (setWidth n x₁)
|
||||
rw [← cons_msb_setWidth x₁, cons_append_append, ih, cons_append]
|
||||
ext j h
|
||||
simp [getLsbD_cons, show n + w₂ + w₃ = n + (w₂ + w₃) by omega]
|
||||
|
||||
theorem replicate_append_self {x : BitVec w} :
|
||||
x ++ x.replicate n = (x.replicate n ++ x).cast (by omega) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
rw [replicate_succ]
|
||||
conv => lhs; rw [ih]
|
||||
simp only [cast_cast, cast_eq]
|
||||
rw [← cast_append_left]
|
||||
· rw [append_assoc]; congr
|
||||
· rw [Nat.add_comm, Nat.mul_add, Nat.mul_one]; omega
|
||||
|
||||
theorem replicate_succ' {x : BitVec w} :
|
||||
x.replicate (n + 1) =
|
||||
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
|
||||
simp [replicate_append_self]
|
||||
|
||||
/-! ### intMin -/
|
||||
|
||||
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
|
||||
@@ -3539,7 +3679,7 @@ theorem getLsbD_intMax (w : Nat) : (intMax w).getLsbD i = decide (i + 1 < w) :=
|
||||
|
||||
/-! ### Non-overflow theorems -/
|
||||
|
||||
/-- If `x.toNat * y.toNat < 2^w`, then the multiplication `(x * y)` does not overflow. -/
|
||||
/-- If `x.toNat + y.toNat < 2^w`, then the addition `(x + y)` does not overflow. -/
|
||||
theorem toNat_add_of_lt {w} {x y : BitVec w} (h : x.toNat + y.toNat < 2^w) :
|
||||
(x + y).toNat = x.toNat + y.toNat := by
|
||||
rw [BitVec.toNat_add, Nat.mod_eq_of_lt h]
|
||||
@@ -3701,6 +3841,57 @@ theorem toInt_abs_eq_natAbs_of_ne_intMin {x : BitVec w} (hx : x ≠ intMin w) :
|
||||
x.abs.toInt = x.toInt.natAbs := by
|
||||
simp [toInt_abs_eq_natAbs, hx]
|
||||
|
||||
/-! ### Reverse -/
|
||||
|
||||
theorem getLsbD_reverse {i : Nat} {x : BitVec w} :
|
||||
(x.reverse).getLsbD i = x.getMsbD i := by
|
||||
induction w generalizing i
|
||||
case zero => simp
|
||||
case succ n ih =>
|
||||
simp only [reverse, truncate_eq_setWidth, getLsbD_concat]
|
||||
rcases i with rfl | i
|
||||
· rfl
|
||||
· simp only [Nat.add_one_ne_zero, ↓reduceIte, Nat.add_one_sub_one, ih]
|
||||
rw [getMsbD_setWidth]
|
||||
simp only [show n - (n + 1) = 0 by omega, Nat.zero_le, decide_true, Bool.true_and]
|
||||
congr; omega
|
||||
|
||||
theorem getMsbD_reverse {i : Nat} {x : BitVec w} :
|
||||
(x.reverse).getMsbD i = x.getLsbD i := by
|
||||
simp only [getMsbD_eq_getLsbD, getLsbD_reverse]
|
||||
by_cases hi : i < w
|
||||
· simp only [hi, decide_true, show w - 1 - i < w by omega, Bool.true_and]
|
||||
congr; omega
|
||||
· simp [hi, show i ≥ w by omega]
|
||||
|
||||
theorem msb_reverse {x : BitVec w} :
|
||||
(x.reverse).msb = x.getLsbD 0 :=
|
||||
by rw [BitVec.msb, getMsbD_reverse]
|
||||
|
||||
theorem reverse_append {x : BitVec w} {y : BitVec v} :
|
||||
(x ++ y).reverse = (y.reverse ++ x.reverse).cast (by omega) := by
|
||||
ext i h
|
||||
simp only [getLsbD_append, getLsbD_reverse]
|
||||
by_cases hi : i < v
|
||||
· by_cases hw : w ≤ i
|
||||
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, getLsbD_reverse, hw]
|
||||
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, getLsbD_reverse, hw, show i < w by omega]
|
||||
· by_cases hw : w ≤ i
|
||||
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, hw, show ¬ i < w by omega, getLsbD_reverse]
|
||||
· simp [getMsbD_append, getLsbD_cast, getLsbD_append, hw, show i < w by omega, getLsbD_reverse]
|
||||
|
||||
@[simp]
|
||||
theorem reverse_cast {w v : Nat} (h : w = v) (x : BitVec w) :
|
||||
(x.cast h).reverse = x.reverse.cast h := by
|
||||
subst h; simp
|
||||
|
||||
theorem reverse_replicate {n : Nat} {x : BitVec w} :
|
||||
(x.replicate n).reverse = (x.reverse).replicate n := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih =>
|
||||
conv => lhs; simp only [replicate_succ']
|
||||
simp [reverse_append, ih]
|
||||
|
||||
/-! ### Decidable quantifiers -/
|
||||
|
||||
@@ -3916,4 +4107,10 @@ abbrev shiftLeft_zero_eq := @shiftLeft_zero
|
||||
@[deprecated ushiftRight_zero (since := "2024-10-27")]
|
||||
abbrev ushiftRight_zero_eq := @ushiftRight_zero
|
||||
|
||||
@[deprecated replicate_zero (since := "2025-01-08")]
|
||||
abbrev replicate_zero_eq := @replicate_zero
|
||||
|
||||
@[deprecated replicate_succ (since := "2025-01-08")]
|
||||
abbrev replicate_succ_eq := @replicate_succ
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -620,3 +620,12 @@ but may be used locally.
|
||||
-/
|
||||
def boolRelToRel : Coe (α → α → Bool) (α → α → Prop) where
|
||||
coe r := fun a b => Eq (r a b) true
|
||||
|
||||
/-! ### subtypes -/
|
||||
|
||||
@[simp] theorem Subtype.beq_iff {α : Type u} [DecidableEq α] {p : α → Prop} {x y : {a : α // p a}} :
|
||||
(x == y) = (x.1 == y.1) := by
|
||||
cases x
|
||||
cases y
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [beq_iff_eq]
|
||||
|
||||
@@ -70,5 +70,3 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 ∨ c.utf8Size = 2 ∨ c.utf8Siz
|
||||
rfl
|
||||
|
||||
end Char
|
||||
|
||||
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size
|
||||
|
||||
@@ -257,7 +257,7 @@ theorem ofNat_fdiv : ∀ m n : Nat, ↑(m / n) = fdiv ↑m ↑n
|
||||
# `bmod` ("balanced" mod)
|
||||
|
||||
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 <
|
||||
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.
|
||||
@@ -266,10 +266,26 @@ This is used in Omega as well as signed bitvectors.
|
||||
/--
|
||||
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
|
||||
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
|
||||
If `m = 0`, then `bmod x m = x`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).bdiv 0 -- 0
|
||||
#eval (0 : Int).bdiv 7 -- 0
|
||||
|
||||
#eval (12 : Int).bdiv 6 -- 2
|
||||
#eval (12 : Int).bdiv 7 -- 2
|
||||
#eval (12 : Int).bdiv 8 -- 2
|
||||
#eval (12 : Int).bdiv 9 -- 1
|
||||
|
||||
#eval (-12 : Int).bdiv 6 -- -2
|
||||
#eval (-12 : Int).bdiv 7 -- -2
|
||||
#eval (-12 : Int).bdiv 8 -- -1
|
||||
#eval (-12 : Int).bdiv 9 -- -1
|
||||
```
|
||||
-/
|
||||
def bmod (x : Int) (m : Nat) : Int :=
|
||||
let r := x % m
|
||||
@@ -281,6 +297,22 @@ def bmod (x : Int) (m : Nat) : Int :=
|
||||
/--
|
||||
Balanced division. This returns the unique integer so that
|
||||
`b * (Int.bdiv a b) + Int.bmod a b = a`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
#eval (7 : Int).bmod 0 -- 7
|
||||
#eval (0 : Int).bmod 7 -- 0
|
||||
|
||||
#eval (12 : Int).bmod 6 -- 0
|
||||
#eval (12 : Int).bmod 7 -- -2
|
||||
#eval (12 : Int).bmod 8 -- -4
|
||||
#eval (12 : Int).bmod 9 -- 3
|
||||
|
||||
#eval (-12 : Int).bmod 6 -- 0
|
||||
#eval (-12 : Int).bmod 7 -- 2
|
||||
#eval (-12 : Int).bmod 8 -- -4
|
||||
#eval (-12 : Int).bmod 9 -- -3
|
||||
```
|
||||
-/
|
||||
def bdiv (x : Int) (m : Nat) : Int :=
|
||||
if m = 0 then
|
||||
|
||||
@@ -111,6 +111,14 @@ theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_congr_left l fun _ _ _ _ => rfl
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_attachWith {p q : α → Prop} (f : ∀ a, p a → q a) (l H) :
|
||||
pmap (fun a h => ⟨a, f a h⟩) l H = l.attachWith q (fun x h => f x (H x h)) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons a l ih =>
|
||||
simp [pmap, attachWith, ih]
|
||||
|
||||
theorem attach_map_coe (l : List α) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
|
||||
@@ -136,10 +144,23 @@ theorem attachWith_map_subtype_val {p : α → Prop} (l : List α) (H : ∀ a
|
||||
@[simp]
|
||||
theorem mem_attach (l : List α) : ∀ x, x ∈ l.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val]; exact h)
|
||||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||||
exact m
|
||||
|
||||
@[simp]
|
||||
theorem mem_attachWith (l : List α) {q : α → Prop} (H) (x : {x // q x}) :
|
||||
x ∈ l.attachWith q H ↔ x.1 ∈ l := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp [ih]
|
||||
constructor
|
||||
· rintro (_ | _) <;> simp_all
|
||||
· rintro (h | h)
|
||||
· simp [← h]
|
||||
· simp_all
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
@@ -266,6 +287,18 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
@[simp] theorem pmap_attach (l : List α) {p : {x // x ∈ l} → Prop} (f : ∀ a, p a → β) (H) :
|
||||
pmap f l.attach H =
|
||||
l.pmap (P := fun a => ∃ h : a ∈ l, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp] theorem pmap_attachWith (l : List α) {p : {x // q x} → Prop} (f : ∀ a, p a → β) (H₁ H₂) :
|
||||
pmap f (l.attachWith q H₁) H₂ =
|
||||
l.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp] theorem head?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : List α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).head? = xs.attach.head?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
@@ -431,7 +464,25 @@ theorem attach_filter {l : List α} (p : α → Bool) :
|
||||
split <;> simp
|
||||
|
||||
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
|
||||
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
|
||||
|
||||
@[simp]
|
||||
theorem filterMap_attachWith {q : α → Prop} {l : List α} {f : {x // q x} → Option β} (H) :
|
||||
(l.attachWith q H).filterMap f = l.attach.filterMap (fun ⟨x, h⟩ => f ⟨x, H _ h⟩) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [attachWith_cons, filterMap_cons]
|
||||
split <;> simp_all [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem filter_attachWith {q : α → Prop} {l : List α} {p : {x // q x} → Bool} (H) :
|
||||
(l.attachWith q H).filter p =
|
||||
(l.attach.filter (fun ⟨x, h⟩ => p ⟨x, H _ h⟩)).map (fun ⟨x, h⟩ => ⟨x, H _ h⟩) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [attachWith_cons, filter_cons]
|
||||
split <;> simp_all [Function.comp_def, filter_map]
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} (g : ∀ a, p a → β) (f : ∀ b, q b → γ) (l H₁ H₂) :
|
||||
pmap f (pmap g l H₁) H₂ =
|
||||
@@ -520,7 +571,7 @@ theorem reverse_attach (xs : List α) :
|
||||
|
||||
@[simp] theorem getLast?_attachWith {P : α → Prop} {xs : List α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).getLast? = xs.getLast?.pbind (fun a h => some ⟨a, H _ (mem_of_getLast?_eq_some h)⟩) := by
|
||||
(xs.attachWith P H).getLast? = xs.getLast?.pbind (fun a h => some ⟨a, H _ (mem_of_getLast? h)⟩) := by
|
||||
rw [getLast?_eq_head?_reverse, reverse_attachWith, head?_attachWith]
|
||||
simp
|
||||
|
||||
@@ -531,7 +582,7 @@ theorem reverse_attach (xs : List α) :
|
||||
|
||||
@[simp]
|
||||
theorem getLast?_attach {xs : List α} :
|
||||
xs.attach.getLast? = xs.getLast?.pbind fun a h => some ⟨a, mem_of_getLast?_eq_some h⟩ := by
|
||||
xs.attach.getLast? = xs.getLast?.pbind fun a h => some ⟨a, mem_of_getLast? h⟩ := by
|
||||
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map, head?_attach]
|
||||
simp
|
||||
|
||||
@@ -560,6 +611,11 @@ theorem count_attachWith [DecidableEq α] {p : α → Prop} (l : List α) (H :
|
||||
(l.attachWith p H).count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _ _ _
|
||||
|
||||
@[simp] theorem countP_pmap {p : α → Prop} (g : ∀ a, p a → β) (f : β → Bool) (l : List α) (H₁) :
|
||||
(l.pmap g H₁).countP f =
|
||||
l.attach.countP (fun ⟨a, m⟩ => f (g a (H₁ a m))) := by
|
||||
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`List.unattach` is the (one-sided) inverse of `List.attach`. It is a synonym for `List.map Subtype.val`.
|
||||
@@ -578,7 +634,7 @@ and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) := l.map (·.val)
|
||||
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α := l.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : ([] : List { x // p x }).unattach = [] := rfl
|
||||
@[simp] theorem unattach_cons {p : α → Prop} {a : { x // p x }} {l : List { x // p x }} :
|
||||
|
||||
@@ -258,9 +258,6 @@ theorem ext_get? : ∀ {l₁ l₂ : List α}, (∀ n, l₁.get? n = l₂.get? n)
|
||||
have h0 : some a = some a' := h 0
|
||||
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
|
||||
|
||||
/-- Deprecated alias for `ext_get?`. The preferred extensionality theorem is now `ext_getElem?`. -/
|
||||
@[deprecated ext_get? (since := "2024-06-07")] abbrev ext := @ext_get?
|
||||
|
||||
/-! ### getD -/
|
||||
|
||||
/--
|
||||
@@ -606,11 +603,11 @@ set_option linter.missingDocs false in
|
||||
to get a list of lists, and then concatenates them all together.
|
||||
* `[2, 3, 2].bind range = [0, 1, 0, 1, 2, 0, 1]`
|
||||
-/
|
||||
@[inline] def flatMap {α : Type u} {β : Type v} (a : List α) (b : α → List β) : List β := flatten (map b a)
|
||||
@[inline] def flatMap {α : Type u} {β : Type v} (b : α → List β) (a : List α) : List β := flatten (map b a)
|
||||
|
||||
@[simp] theorem flatMap_nil (f : α → List β) : List.flatMap [] f = [] := by simp [flatten, List.flatMap]
|
||||
@[simp] theorem flatMap_nil (f : α → List β) : List.flatMap f [] = [] := by simp [flatten, List.flatMap]
|
||||
@[simp] theorem flatMap_cons x xs (f : α → List β) :
|
||||
List.flatMap (x :: xs) f = f x ++ List.flatMap xs f := by simp [flatten, List.flatMap]
|
||||
List.flatMap f (x :: xs) = f x ++ List.flatMap f xs := by simp [flatten, List.flatMap]
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated flatMap (since := "2024-10-16")] abbrev bind := @flatMap
|
||||
@@ -619,11 +616,6 @@ set_option linter.missingDocs false in
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated flatMap_cons (since := "2024-10-16")] abbrev cons_flatMap := @flatMap_cons
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated flatMap_nil (since := "2024-06-15")] abbrev nil_bind := @flatMap_nil
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated flatMap_cons (since := "2024-06-15")] abbrev cons_bind := @flatMap_cons
|
||||
|
||||
/-! ### replicate -/
|
||||
|
||||
/--
|
||||
@@ -713,11 +705,6 @@ def elem [BEq α] (a : α) : List α → Bool
|
||||
theorem elem_cons [BEq α] {a : α} :
|
||||
(b::bs).elem a = match a == b with | true => true | false => bs.elem a := rfl
|
||||
|
||||
/-- `notElem a l` is `!(elem a l)`. -/
|
||||
@[deprecated "Use `!(elem a l)` instead."(since := "2024-06-15")]
|
||||
def notElem [BEq α] (a : α) (as : List α) : Bool :=
|
||||
!(as.elem a)
|
||||
|
||||
/-! ### contains -/
|
||||
|
||||
@[inherit_doc elem] abbrev contains [BEq α] (as : List α) (a : α) : Bool :=
|
||||
@@ -1533,11 +1520,14 @@ def range' : (start len : Nat) → (step : Nat := 1) → List Nat
|
||||
`O(n)`. `iota n` is the numbers from `1` to `n` inclusive, in decreasing order.
|
||||
* `iota 5 = [5, 4, 3, 2, 1]`
|
||||
-/
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
def iota : Nat → List Nat
|
||||
| 0 => []
|
||||
| m@(n+1) => m :: iota n
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[simp] theorem iota_zero : iota 0 = [] := rfl
|
||||
set_option linter.deprecated false in
|
||||
@[simp] theorem iota_succ : iota (i+1) = (i+1) :: iota i := rfl
|
||||
|
||||
/-! ### enumFrom -/
|
||||
@@ -1861,12 +1851,14 @@ def unzipTR (l : List (α × β)) : List α × List β :=
|
||||
/-! ### iota -/
|
||||
|
||||
/-- Tail-recursive version of `List.iota`. -/
|
||||
@[deprecated "Use `List.range' 1 n` instead of `iota n`." (since := "2025-01-20")]
|
||||
def iotaTR (n : Nat) : List Nat :=
|
||||
let rec go : Nat → List Nat → List Nat
|
||||
| 0, r => r.reverse
|
||||
| m@(n+1), r => go n (m::r)
|
||||
go n []
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[csimp]
|
||||
theorem iota_eq_iotaTR : @iota = @iotaTR :=
|
||||
have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
|
||||
|
||||
@@ -254,6 +254,7 @@ theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] (p : α → m Bool) (as :
|
||||
| [], b, _ => pure b
|
||||
| a::as', b, h => do
|
||||
have : a ∈ as := by
|
||||
clear f
|
||||
have ⟨bs, h⟩ := h
|
||||
subst h
|
||||
exact mem_append_right _ (Mem.head ..)
|
||||
|
||||
@@ -40,7 +40,7 @@ protected theorem countP_go_eq_add (l) : countP.go p l n = n + countP.go p l 0 :
|
||||
theorem countP_cons (a : α) (l) : countP p (a :: l) = countP p l + if p a then 1 else 0 := by
|
||||
by_cases h : p a <;> simp [h]
|
||||
|
||||
theorem countP_singleton (a : α) : countP p [a] = if p a then 1 else 0 := by
|
||||
@[simp] theorem countP_singleton (a : α) : countP p [a] = if p a then 1 else 0 := by
|
||||
simp [countP_cons]
|
||||
|
||||
theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a => ¬p a) l := by
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Find
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.eraseP` and `List.erase`.
|
||||
@@ -572,4 +573,19 @@ protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
|
||||
-- See also `mem_eraseIdx_iff_getElem` and `mem_eraseIdx_iff_getElem?` in
|
||||
-- `Init/Data/List/Nat/Basic.lean`.
|
||||
|
||||
theorem erase_eq_eraseIdx [BEq α] [LawfulBEq α] (l : List α) (a : α) (i : Nat) (w : l.indexOf a = i) :
|
||||
l.erase a = l.eraseIdx i := by
|
||||
subst w
|
||||
rw [erase_eq_iff]
|
||||
by_cases h : a ∈ l
|
||||
· right
|
||||
obtain ⟨as, bs, rfl, h'⟩ := eq_append_cons_of_mem h
|
||||
refine ⟨as, bs, h', by simp, ?_⟩
|
||||
rw [indexOf_append, if_neg h', indexOf_cons_self, eraseIdx_append_of_length_le] <;>
|
||||
simp
|
||||
· left
|
||||
refine ⟨h, ?_⟩
|
||||
rw [eq_comm, eraseIdx_eq_self]
|
||||
exact Nat.le_of_eq (indexOf_eq_length h).symm
|
||||
|
||||
end List
|
||||
|
||||
@@ -884,14 +884,68 @@ theorem IsInfix.findIdx?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l
|
||||
List.findIdx? p l₂ = none → List.findIdx? p l₁ = none :=
|
||||
h.sublist.findIdx?_eq_none
|
||||
|
||||
/-! ### indexOf -/
|
||||
/-! ### indexOf
|
||||
|
||||
The verification API for `indexOf` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
|
||||
-/
|
||||
|
||||
theorem indexOf_cons [BEq α] :
|
||||
(x :: xs : List α).indexOf y = bif x == y then 0 else xs.indexOf y + 1 := by
|
||||
dsimp [indexOf]
|
||||
simp [findIdx_cons]
|
||||
|
||||
@[simp] theorem indexOf_cons_self [BEq α] [ReflBEq α] {l : List α} : (a :: l).indexOf a = 0 := by
|
||||
simp [indexOf_cons]
|
||||
|
||||
theorem indexOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
|
||||
(l₁ ++ l₂).indexOf a = if a ∈ l₁ then l₁.indexOf a else l₂.indexOf a + l₁.length := by
|
||||
rw [indexOf, findIdx_append]
|
||||
split <;> rename_i h
|
||||
· rw [if_pos]
|
||||
simpa using h
|
||||
· rw [if_neg]
|
||||
simpa using h
|
||||
|
||||
theorem indexOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.indexOf a = l.length := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [mem_cons, not_or] at h
|
||||
simp only [indexOf_cons, cond_eq_if, beq_iff_eq]
|
||||
split <;> simp_all
|
||||
|
||||
theorem indexOf_lt_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∈ l) : l.indexOf a < l.length := by
|
||||
induction l with
|
||||
| nil => simp at h
|
||||
| cons x xs ih =>
|
||||
simp only [mem_cons] at h
|
||||
obtain rfl | h := h
|
||||
· simp
|
||||
· simp only [indexOf_cons, cond_eq_if, beq_iff_eq, length_cons]
|
||||
specialize ih h
|
||||
split
|
||||
· exact zero_lt_succ xs.length
|
||||
· exact Nat.add_lt_add_right ih 1
|
||||
|
||||
/-! ### indexOf?
|
||||
|
||||
The verification API for `indexOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
@[simp] theorem indexOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||||
l.indexOf? a = none ↔ a ∉ l := by
|
||||
simp only [indexOf?, findIdx?_eq_none_iff, beq_eq_false_iff_ne, ne_eq]
|
||||
constructor
|
||||
· intro w h
|
||||
specialize w _ h
|
||||
simp at w
|
||||
· rintro w x h rfl
|
||||
contradiction
|
||||
|
||||
/-! ### lookup -/
|
||||
|
||||
section lookup
|
||||
variable [BEq α] [LawfulBEq α]
|
||||
|
||||
|
||||
@@ -96,14 +96,14 @@ The following operations are given `@[csimp]` replacements below:
|
||||
/-! ### flatMap -/
|
||||
|
||||
/-- Tail recursive version of `List.flatMap`. -/
|
||||
@[inline] def flatMapTR (as : List α) (f : α → List β) : List β := go as #[] where
|
||||
@[inline] def flatMapTR (f : α → List β) (as : List α) : List β := go as #[] where
|
||||
/-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/
|
||||
@[specialize] go : List α → Array β → List β
|
||||
| [], acc => acc.toList
|
||||
| x::xs, acc => go xs (acc ++ f x)
|
||||
|
||||
@[csimp] theorem flatMap_eq_flatMapTR : @List.flatMap = @flatMapTR := by
|
||||
funext α β as f
|
||||
funext α β f as
|
||||
let rec go : ∀ as acc, flatMapTR.go f as acc = acc.toList ++ as.flatMap f
|
||||
| [], acc => by simp [flatMapTR.go, flatMap]
|
||||
| x::xs, acc => by simp [flatMapTR.go, flatMap, go xs]
|
||||
@@ -112,7 +112,7 @@ The following operations are given `@[csimp]` replacements below:
|
||||
/-! ### flatten -/
|
||||
|
||||
/-- Tail recursive version of `List.flatten`. -/
|
||||
@[inline] def flattenTR (l : List (List α)) : List α := flatMapTR l id
|
||||
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
|
||||
|
||||
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
|
||||
funext α l; rw [← List.flatMap_id, List.flatMap_eq_flatMapTR]; rfl
|
||||
|
||||
@@ -813,11 +813,6 @@ theorem getElem_cons_length (x : α) (xs : List α) (i : Nat) (h : i = xs.length
|
||||
(x :: xs)[i]'(by simp [h]) = (x :: xs).getLast (cons_ne_nil x xs) := by
|
||||
rw [getLast_eq_getElem]; cases h; rfl
|
||||
|
||||
@[deprecated getElem_cons_length (since := "2024-06-12")]
|
||||
theorem get_cons_length (x : α) (xs : List α) (n : Nat) (h : n = xs.length) :
|
||||
(x :: xs).get ⟨n, by simp [h]⟩ = (x :: xs).getLast (cons_ne_nil x xs) := by
|
||||
simp [getElem_cons_length, h]
|
||||
|
||||
/-! ### getLast? -/
|
||||
|
||||
@[simp] theorem getLast?_singleton (a : α) : getLast? [a] = a := rfl
|
||||
@@ -1026,21 +1021,10 @@ theorem getLast?_tail (l : List α) : (tail l).getLast? = if l.length = 1 then n
|
||||
| _ :: _, 0 => by simp
|
||||
| _ :: l, i+1 => by simp [getElem?_map f l i]
|
||||
|
||||
@[deprecated getElem?_map (since := "2024-06-12")]
|
||||
theorem get?_map (f : α → β) : ∀ l i, (map f l).get? i = (l.get? i).map f
|
||||
| [], _ => rfl
|
||||
| _ :: _, 0 => rfl
|
||||
| _ :: l, i+1 => get?_map f l i
|
||||
|
||||
@[simp] theorem getElem_map (f : α → β) {l} {i : Nat} {h : i < (map f l).length} :
|
||||
(map f l)[i] = f (l[i]'(length_map l f ▸ h)) :=
|
||||
Option.some.inj <| by rw [← getElem?_eq_getElem, getElem?_map, getElem?_eq_getElem]; rfl
|
||||
|
||||
@[deprecated getElem_map (since := "2024-06-12")]
|
||||
theorem get_map (f : α → β) {l i} :
|
||||
get (map f l) i = f (get l ⟨i, length_map l f ▸ i.2⟩) := by
|
||||
simp
|
||||
|
||||
@[simp] theorem map_id_fun : map (id : α → α) = id := by
|
||||
funext l
|
||||
induction l <;> simp_all
|
||||
@@ -1076,9 +1060,31 @@ theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
|
||||
|
||||
@[deprecated forall_mem_map (since := "2024-07-25")] abbrev forall_mem_map_iff := @forall_mem_map
|
||||
|
||||
@[simp] theorem map_eq_nil_iff {f : α → β} {l : List α} : map f l = [] ↔ l = [] := by
|
||||
constructor <;> exact fun _ => match l with | [] => rfl
|
||||
|
||||
@[deprecated map_eq_nil_iff (since := "2024-09-05")] abbrev map_eq_nil := @map_eq_nil_iff
|
||||
|
||||
theorem eq_nil_of_map_eq_nil {f : α → β} {l : List α} (h : map f l = []) : l = [] :=
|
||||
map_eq_nil_iff.mp h
|
||||
|
||||
@[simp] theorem map_inj_left {f g : α → β} : map f l = map g l ↔ ∀ a ∈ l, f a = g a := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem map_inj_right {f : α → β} (w : ∀ x y, f x = f y → x = y) : map f l = map f l' ↔ l = l' := by
|
||||
induction l generalizing l' with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp only [map_cons]
|
||||
cases l' with
|
||||
| nil => simp
|
||||
| cons a' l' =>
|
||||
simp only [map_cons, cons.injEq, ih, and_congr_left_iff]
|
||||
intro h
|
||||
constructor
|
||||
· apply w
|
||||
· simp +contextual
|
||||
|
||||
theorem map_congr_left (h : ∀ a ∈ l, f a = g a) : map f l = map g l :=
|
||||
map_inj_left.2 h
|
||||
|
||||
@@ -1087,14 +1093,6 @@ theorem map_inj : map f = map g ↔ f = g := by
|
||||
· intro h; ext a; replace h := congrFun h [a]; simpa using h
|
||||
· intro h; subst h; rfl
|
||||
|
||||
@[simp] theorem map_eq_nil_iff {f : α → β} {l : List α} : map f l = [] ↔ l = [] := by
|
||||
constructor <;> exact fun _ => match l with | [] => rfl
|
||||
|
||||
@[deprecated map_eq_nil_iff (since := "2024-09-05")] abbrev map_eq_nil := @map_eq_nil_iff
|
||||
|
||||
theorem eq_nil_of_map_eq_nil {f : α → β} {l : List α} (h : map f l = []) : l = [] :=
|
||||
map_eq_nil_iff.mp h
|
||||
|
||||
theorem map_eq_cons_iff {f : α → β} {l : List α} :
|
||||
map f l = b :: l₂ ↔ ∃ a l₁, l = a :: l₁ ∧ f a = b ∧ map f l₁ = l₂ := by
|
||||
cases l
|
||||
@@ -1272,8 +1270,6 @@ theorem filter_map (f : β → α) (l : List β) : filter p (map f l) = map f (f
|
||||
| nil => rfl
|
||||
| cons a l IH => by_cases h : p (f a) <;> simp [*]
|
||||
|
||||
@[deprecated filter_map (since := "2024-06-15")] abbrev map_filter := @filter_map
|
||||
|
||||
theorem map_filter_eq_foldr (f : α → β) (p : α → Bool) (as : List α) :
|
||||
map f (filter p as) = foldr (fun a bs => bif p a then f a :: bs else bs) [] as := by
|
||||
induction as with
|
||||
@@ -1318,8 +1314,6 @@ theorem filter_congr {p q : α → Bool} :
|
||||
· simp [pa, h.1 ▸ pa, filter_congr h.2]
|
||||
· simp [pa, h.1 ▸ pa, filter_congr h.2]
|
||||
|
||||
@[deprecated filter_congr (since := "2024-06-20")] abbrev filter_congr' := @filter_congr
|
||||
|
||||
theorem head_filter_of_pos {p : α → Bool} {l : List α} (w : l ≠ []) (h : p (l.head w)) :
|
||||
(filter p l).head ((ne_nil_of_mem (mem_filter.2 ⟨head_mem w, h⟩))) = l.head w := by
|
||||
cases l with
|
||||
@@ -1494,6 +1488,34 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
|
||||
@[simp] theorem cons_append_fun (a : α) (as : List α) :
|
||||
(fun bs => ((a :: as) ++ bs)) = fun bs => a :: (as ++ bs) := rfl
|
||||
|
||||
@[simp] theorem mem_append {a : α} {s t : List α} : a ∈ s ++ t ↔ a ∈ s ∨ a ∈ t := by
|
||||
induction s <;> simp_all [or_assoc]
|
||||
|
||||
theorem not_mem_append {a : α} {s t : List α} (h₁ : a ∉ s) (h₂ : a ∉ t) : a ∉ s ++ t :=
|
||||
mt mem_append.1 $ not_or.mpr ⟨h₁, h₂⟩
|
||||
|
||||
@[deprecated mem_append (since := "2025-01-13")]
|
||||
theorem mem_append_eq (a : α) (s t : List α) : (a ∈ s ++ t) = (a ∈ s ∨ a ∈ t) :=
|
||||
propext mem_append
|
||||
|
||||
@[deprecated mem_append_left (since := "2024-11-20")] abbrev mem_append_of_mem_left := @mem_append_left
|
||||
@[deprecated mem_append_right (since := "2024-11-20")] abbrev mem_append_of_mem_right := @mem_append_right
|
||||
|
||||
/--
|
||||
See also `eq_append_cons_of_mem`, which proves a stronger version
|
||||
in which the initial list must not contain the element.
|
||||
-/
|
||||
theorem append_of_mem {a : α} {l : List α} : a ∈ l → ∃ s t : List α, l = s ++ a :: t
|
||||
| .head l => ⟨[], l, rfl⟩
|
||||
| .tail b h => let ⟨s, t, h'⟩ := append_of_mem h; ⟨b::s, t, by rw [h', cons_append]⟩
|
||||
|
||||
theorem mem_iff_append {a : α} {l : List α} : a ∈ l ↔ ∃ s t : List α, l = s ++ a :: t :=
|
||||
⟨append_of_mem, fun ⟨s, t, e⟩ => e ▸ by simp⟩
|
||||
|
||||
theorem forall_mem_append {p : α → Prop} {l₁ l₂ : List α} :
|
||||
(∀ (x) (_ : x ∈ l₁ ++ l₂), p x) ↔ (∀ (x) (_ : x ∈ l₁), p x) ∧ (∀ (x) (_ : x ∈ l₂), p x) := by
|
||||
simp only [mem_append, or_imp, forall_and]
|
||||
|
||||
theorem getElem_append {l₁ l₂ : List α} (i : Nat) (h : i < (l₁ ++ l₂).length) :
|
||||
(l₁ ++ l₂)[i] = if h' : i < l₁.length then l₁[i] else l₂[i - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
|
||||
split <;> rename_i h'
|
||||
@@ -1519,11 +1541,6 @@ theorem getElem?_append {l₁ l₂ : List α} {i : Nat} :
|
||||
· exact getElem?_append_left h
|
||||
· exact getElem?_append_right (by simpa using h)
|
||||
|
||||
@[deprecated getElem?_append_right (since := "2024-06-12")]
|
||||
theorem get?_append_right {l₁ l₂ : List α} {i : Nat} (h : l₁.length ≤ i) :
|
||||
(l₁ ++ l₂).get? i = l₂.get? (i - l₁.length) := by
|
||||
simp [getElem?_append_right, h]
|
||||
|
||||
/-- Variant of `getElem_append_left` useful for rewriting from the small list to the big list. -/
|
||||
theorem getElem_append_left' (l₂ : List α) {l₁ : List α} {i : Nat} (hi : i < l₁.length) :
|
||||
l₁[i] = (l₁ ++ l₂)[i]'(by simpa using Nat.lt_add_right l₂.length hi) := by
|
||||
@@ -1534,41 +1551,11 @@ theorem getElem_append_right' (l₁ : List α) {l₂ : List α} {i : Nat} (hi :
|
||||
l₂[i] = (l₁ ++ l₂)[i + l₁.length]'(by simpa [Nat.add_comm] using Nat.add_lt_add_left hi _) := by
|
||||
rw [getElem_append_right] <;> simp [*, le_add_left]
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-06-12")]
|
||||
theorem get_append_right_aux {l₁ l₂ : List α} {i : Nat}
|
||||
(h₁ : l₁.length ≤ i) (h₂ : i < (l₁ ++ l₂).length) : i - l₁.length < l₂.length := by
|
||||
rw [length_append] at h₂
|
||||
exact Nat.sub_lt_left_of_lt_add h₁ h₂
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem_append_right (since := "2024-06-12")]
|
||||
theorem get_append_right' {l₁ l₂ : List α} {i : Nat} (h₁ : l₁.length ≤ i) (h₂) :
|
||||
(l₁ ++ l₂).get ⟨i, h₂⟩ = l₂.get ⟨i - l₁.length, get_append_right_aux h₁ h₂⟩ :=
|
||||
Option.some.inj <| by rw [← get?_eq_get, ← get?_eq_get, get?_append_right h₁]
|
||||
|
||||
theorem getElem_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = i) :
|
||||
l[i]'(eq ▸ h ▸ by simp_arith) = a := Option.some.inj <| by
|
||||
rw [← getElem?_eq_getElem, eq, getElem?_append_right (h ▸ Nat.le_refl _), h]
|
||||
simp
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-06-12")]
|
||||
theorem get_of_append_proof {l : List α}
|
||||
(eq : l = l₁ ++ a :: l₂) (h : l₁.length = i) : i < length l := eq ▸ h ▸ by simp_arith
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem_of_append (since := "2024-06-12")]
|
||||
theorem get_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = i) :
|
||||
l.get ⟨i, get_of_append_proof eq h⟩ = a := Option.some.inj <| by
|
||||
rw [← get?_eq_get, eq, get?_append_right (h ▸ Nat.le_refl _), h, Nat.sub_self]; rfl
|
||||
|
||||
/--
|
||||
See also `eq_append_cons_of_mem`, which proves a stronger version
|
||||
in which the initial list must not contain the element.
|
||||
-/
|
||||
theorem append_of_mem {a : α} {l : List α} : a ∈ l → ∃ s t : List α, l = s ++ a :: t
|
||||
| .head l => ⟨[], l, rfl⟩
|
||||
| .tail b h => let ⟨s, t, h'⟩ := append_of_mem h; ⟨b::s, t, by rw [h', cons_append]⟩
|
||||
|
||||
@[simp 1100] theorem singleton_append : [x] ++ l = x :: l := rfl
|
||||
|
||||
theorem append_inj :
|
||||
@@ -1585,8 +1572,8 @@ theorem append_inj_left (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = le
|
||||
|
||||
/-- Variant of `append_inj` instead requiring equality of the lengths of the second lists. -/
|
||||
theorem append_inj' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ ∧ t₁ = t₂ :=
|
||||
append_inj h <| @Nat.add_right_cancel _ (length t₁) _ <| by
|
||||
let hap := congrArg length h; simp only [length_append, ← hl] at hap; exact hap
|
||||
append_inj h <| @Nat.add_right_cancel _ t₁.length _ <| by
|
||||
let hap := congrArg length h; simp only [length_append, ← hl] at hap; exact hap
|
||||
|
||||
/-- Variant of `append_inj_right` instead requiring equality of the lengths of the second lists. -/
|
||||
theorem append_inj_right' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : t₁ = t₂ :=
|
||||
@@ -1614,33 +1601,58 @@ theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s
|
||||
@[simp] theorem self_eq_append_right {x y : List α} : x = x ++ y ↔ y = [] := by
|
||||
rw [eq_comm, append_right_eq_self]
|
||||
|
||||
@[simp] theorem append_eq_nil : p ++ q = [] ↔ p = [] ∧ q = [] := by
|
||||
cases p <;> simp
|
||||
|
||||
theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp) = a
|
||||
| [] => rfl
|
||||
| a::t => by
|
||||
simp [getLast_cons _, getLast_concat t]
|
||||
|
||||
@[deprecated getElem_append (since := "2024-06-12")]
|
||||
theorem get_append {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length) :
|
||||
(l₁ ++ l₂).get ⟨n, length_append .. ▸ Nat.lt_add_right _ h⟩ = l₁.get ⟨n, h⟩ := by
|
||||
simp [getElem_append, h]
|
||||
@[simp] theorem append_eq_nil_iff : p ++ q = [] ↔ p = [] ∧ q = [] := by
|
||||
cases p <;> simp
|
||||
|
||||
@[deprecated getElem_append_left (since := "2024-06-12")]
|
||||
theorem get_append_left (as bs : List α) (h : i < as.length) {h'} :
|
||||
(as ++ bs).get ⟨i, h'⟩ = as.get ⟨i, h⟩ := by
|
||||
simp [getElem_append_left, h, h']
|
||||
@[deprecated append_eq_nil_iff (since := "2025-01-13")] abbrev append_eq_nil := @append_eq_nil_iff
|
||||
|
||||
@[deprecated getElem_append_right (since := "2024-06-12")]
|
||||
theorem get_append_right (as bs : List α) (h : as.length ≤ i) {h' h''} :
|
||||
(as ++ bs).get ⟨i, h'⟩ = bs.get ⟨i - as.length, h''⟩ := by
|
||||
simp [getElem_append_right, h, h', h'']
|
||||
@[simp] theorem nil_eq_append_iff : [] = a ++ b ↔ a = [] ∧ b = [] := by
|
||||
rw [eq_comm, append_eq_nil_iff]
|
||||
|
||||
@[deprecated getElem?_append_left (since := "2024-06-12")]
|
||||
theorem get?_append {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
|
||||
(l₁ ++ l₂).get? n = l₁.get? n := by
|
||||
simp [getElem?_append_left hn]
|
||||
@[deprecated nil_eq_append_iff (since := "2024-07-24")] abbrev nil_eq_append := @nil_eq_append_iff
|
||||
|
||||
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
theorem append_ne_nil_of_right_ne_nil (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
@[deprecated append_ne_nil_of_left_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_right (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
theorem append_eq_cons_iff :
|
||||
a ++ b = x :: c ↔ (a = [] ∧ b = x :: c) ∨ (∃ a', a = x :: a' ∧ c = a' ++ b) := by
|
||||
cases a with simp | cons a as => ?_
|
||||
exact ⟨fun h => ⟨as, by simp [h]⟩, fun ⟨a', ⟨aeq, aseq⟩, h⟩ => ⟨aeq, by rw [aseq, h]⟩⟩
|
||||
|
||||
@[deprecated append_eq_cons_iff (since := "2024-07-24")] abbrev append_eq_cons := @append_eq_cons_iff
|
||||
|
||||
theorem cons_eq_append_iff :
|
||||
x :: c = a ++ b ↔ (a = [] ∧ b = x :: c) ∨ (∃ a', a = x :: a' ∧ c = a' ++ b) := by
|
||||
rw [eq_comm, append_eq_cons_iff]
|
||||
|
||||
@[deprecated cons_eq_append_iff (since := "2024-07-24")] abbrev cons_eq_append := @cons_eq_append_iff
|
||||
|
||||
theorem append_eq_singleton_iff :
|
||||
a ++ b = [x] ↔ (a = [] ∧ b = [x]) ∨ (a = [x] ∧ b = []) := by
|
||||
cases a <;> cases b <;> simp
|
||||
|
||||
theorem singleton_eq_append_iff :
|
||||
[x] = a ++ b ↔ (a = [] ∧ b = [x]) ∨ (a = [x] ∧ b = []) := by
|
||||
cases a <;> cases b <;> simp [eq_comm]
|
||||
|
||||
theorem append_eq_append_iff {a b c d : List α} :
|
||||
a ++ b = c ++ d ↔ (∃ a', c = a ++ a' ∧ b = a' ++ d) ∨ ∃ c', a = c ++ c' ∧ d = c' ++ b := by
|
||||
induction a generalizing c with
|
||||
| nil => simp_all
|
||||
| cons a as ih => cases c <;> simp [eq_comm, and_assoc, ih, and_or_left]
|
||||
|
||||
@[deprecated append_inj (since := "2024-07-24")] abbrev append_inj_of_length_left := @append_inj
|
||||
@[deprecated append_inj' (since := "2024-07-24")] abbrev append_inj_of_length_right := @append_inj'
|
||||
|
||||
@[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
|
||||
head (l ++ l') w₁ = head l w₂ := by
|
||||
@@ -1691,60 +1703,6 @@ theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tai
|
||||
|
||||
@[deprecated tail_append_of_ne_nil (since := "2024-07-24")] abbrev tail_append_left := @tail_append_of_ne_nil
|
||||
|
||||
theorem nil_eq_append_iff : [] = a ++ b ↔ a = [] ∧ b = [] := by
|
||||
rw [eq_comm, append_eq_nil]
|
||||
|
||||
@[deprecated nil_eq_append_iff (since := "2024-07-24")] abbrev nil_eq_append := @nil_eq_append_iff
|
||||
|
||||
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
theorem append_ne_nil_of_right_ne_nil (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
@[deprecated append_ne_nil_of_left_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s ≠ []) (t : List α) : s ++ t ≠ [] := by simp_all
|
||||
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
|
||||
theorem append_ne_nil_of_ne_nil_right (s : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
|
||||
|
||||
theorem append_eq_cons_iff :
|
||||
a ++ b = x :: c ↔ (a = [] ∧ b = x :: c) ∨ (∃ a', a = x :: a' ∧ c = a' ++ b) := by
|
||||
cases a with simp | cons a as => ?_
|
||||
exact ⟨fun h => ⟨as, by simp [h]⟩, fun ⟨a', ⟨aeq, aseq⟩, h⟩ => ⟨aeq, by rw [aseq, h]⟩⟩
|
||||
|
||||
@[deprecated append_eq_cons_iff (since := "2024-07-24")] abbrev append_eq_cons := @append_eq_cons_iff
|
||||
|
||||
theorem cons_eq_append_iff :
|
||||
x :: c = a ++ b ↔ (a = [] ∧ b = x :: c) ∨ (∃ a', a = x :: a' ∧ c = a' ++ b) := by
|
||||
rw [eq_comm, append_eq_cons_iff]
|
||||
|
||||
@[deprecated cons_eq_append_iff (since := "2024-07-24")] abbrev cons_eq_append := @cons_eq_append_iff
|
||||
|
||||
theorem append_eq_append_iff {a b c d : List α} :
|
||||
a ++ b = c ++ d ↔ (∃ a', c = a ++ a' ∧ b = a' ++ d) ∨ ∃ c', a = c ++ c' ∧ d = c' ++ b := by
|
||||
induction a generalizing c with
|
||||
| nil => simp_all
|
||||
| cons a as ih => cases c <;> simp [eq_comm, and_assoc, ih, and_or_left]
|
||||
|
||||
@[deprecated append_inj (since := "2024-07-24")] abbrev append_inj_of_length_left := @append_inj
|
||||
@[deprecated append_inj' (since := "2024-07-24")] abbrev append_inj_of_length_right := @append_inj'
|
||||
|
||||
@[simp] theorem mem_append {a : α} {s t : List α} : a ∈ s ++ t ↔ a ∈ s ∨ a ∈ t := by
|
||||
induction s <;> simp_all [or_assoc]
|
||||
|
||||
theorem not_mem_append {a : α} {s t : List α} (h₁ : a ∉ s) (h₂ : a ∉ t) : a ∉ s ++ t :=
|
||||
mt mem_append.1 $ not_or.mpr ⟨h₁, h₂⟩
|
||||
|
||||
theorem mem_append_eq (a : α) (s t : List α) : (a ∈ s ++ t) = (a ∈ s ∨ a ∈ t) :=
|
||||
propext mem_append
|
||||
|
||||
@[deprecated mem_append_left (since := "2024-11-20")] abbrev mem_append_of_mem_left := @mem_append_left
|
||||
@[deprecated mem_append_right (since := "2024-11-20")] abbrev mem_append_of_mem_right := @mem_append_right
|
||||
|
||||
theorem mem_iff_append {a : α} {l : List α} : a ∈ l ↔ ∃ s t : List α, l = s ++ a :: t :=
|
||||
⟨append_of_mem, fun ⟨s, t, e⟩ => e ▸ by simp⟩
|
||||
|
||||
theorem forall_mem_append {p : α → Prop} {l₁ l₂ : List α} :
|
||||
(∀ (x) (_ : x ∈ l₁ ++ l₂), p x) ↔ (∀ (x) (_ : x ∈ l₁), p x) ∧ (∀ (x) (_ : x ∈ l₂), p x) := by
|
||||
simp only [mem_append, or_imp, forall_and]
|
||||
|
||||
theorem set_append {s t : List α} :
|
||||
(s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by
|
||||
induction s generalizing i with
|
||||
@@ -1873,7 +1831,7 @@ theorem eq_nil_or_concat : ∀ l : List α, l = [] ∨ ∃ L b, l = concat L b
|
||||
|
||||
/-! ### flatten -/
|
||||
|
||||
@[simp] theorem length_flatten (L : List (List α)) : (flatten L).length = (L.map length).sum := by
|
||||
@[simp] theorem length_flatten (L : List (List α)) : L.flatten.length = (L.map length).sum := by
|
||||
induction L with
|
||||
| nil => rfl
|
||||
| cons =>
|
||||
@@ -1888,6 +1846,9 @@ theorem flatten_singleton (l : List α) : [l].flatten = l := by simp
|
||||
@[simp] theorem flatten_eq_nil_iff {L : List (List α)} : L.flatten = [] ↔ ∀ l ∈ L, l = [] := by
|
||||
induction L <;> simp_all
|
||||
|
||||
@[simp] theorem nil_eq_flatten_iff {L : List (List α)} : [] = L.flatten ↔ ∀ l ∈ L, l = [] := by
|
||||
rw [eq_comm, flatten_eq_nil_iff]
|
||||
|
||||
theorem flatten_ne_nil_iff {xs : List (List α)} : xs.flatten ≠ [] ↔ ∃ x, x ∈ xs ∧ x ≠ [] := by
|
||||
simp
|
||||
|
||||
@@ -1913,7 +1874,8 @@ theorem head?_flatten {L : List (List α)} : (flatten L).head? = L.findSome? fun
|
||||
-- `getLast?_flatten` is proved later, after the `reverse` section.
|
||||
-- `head_flatten` and `getLast_flatten` are proved in `Init.Data.List.Find`.
|
||||
|
||||
@[simp] theorem map_flatten (f : α → β) (L : List (List α)) : map f (flatten L) = flatten (map (map f) L) := by
|
||||
@[simp] theorem map_flatten (f : α → β) (L : List (List α)) :
|
||||
(flatten L).map f = (map (map f) L).flatten := by
|
||||
induction L <;> simp_all
|
||||
|
||||
@[simp] theorem filterMap_flatten (f : α → Option β) (L : List (List α)) :
|
||||
@@ -1966,6 +1928,26 @@ theorem flatten_eq_cons_iff {xs : List (List α)} {y : α} {ys : List α} :
|
||||
· rintro ⟨as, bs, cs, rfl, h₁, rfl⟩
|
||||
simp [flatten_eq_nil_iff.mpr h₁]
|
||||
|
||||
theorem cons_eq_flatten_iff {xs : List (List α)} {y : α} {ys : List α} :
|
||||
y :: ys = xs.flatten ↔
|
||||
∃ as bs cs, xs = as ++ (y :: bs) :: cs ∧ (∀ l, l ∈ as → l = []) ∧ ys = bs ++ cs.flatten := by
|
||||
rw [eq_comm, flatten_eq_cons_iff]
|
||||
|
||||
theorem flatten_eq_singleton_iff {xs : List (List α)} {y : α} :
|
||||
xs.flatten = [y] ↔ ∃ as bs, xs = as ++ [y] :: bs ∧ (∀ l, l ∈ as → l = []) ∧ (∀ l, l ∈ bs → l = []) := by
|
||||
rw [flatten_eq_cons_iff]
|
||||
constructor
|
||||
· rintro ⟨as, bs, cs, rfl, h₁, h₂⟩
|
||||
simp at h₂
|
||||
obtain ⟨rfl, h₂⟩ := h₂
|
||||
exact ⟨as, cs, by simp, h₁, h₂⟩
|
||||
· rintro ⟨as, bs, rfl, h₁, h₂⟩
|
||||
exact ⟨as, [], bs, rfl, h₁, by simpa⟩
|
||||
|
||||
theorem singleton_eq_flatten_iff {xs : List (List α)} {y : α} :
|
||||
[y] = xs.flatten ↔ ∃ as bs, xs = as ++ [y] :: bs ∧ (∀ l, l ∈ as → l = []) ∧ (∀ l, l ∈ bs → l = []) := by
|
||||
rw [eq_comm, flatten_eq_singleton_iff]
|
||||
|
||||
theorem flatten_eq_append_iff {xs : List (List α)} {ys zs : List α} :
|
||||
xs.flatten = ys ++ zs ↔
|
||||
(∃ as bs, xs = as ++ bs ∧ ys = as.flatten ∧ zs = bs.flatten) ∨
|
||||
@@ -1974,8 +1956,8 @@ theorem flatten_eq_append_iff {xs : List (List α)} {ys zs : List α} :
|
||||
constructor
|
||||
· induction xs generalizing ys with
|
||||
| nil =>
|
||||
simp only [flatten_nil, nil_eq, append_eq_nil, and_false, cons_append, false_and, exists_const,
|
||||
exists_false, or_false, and_imp, List.cons_ne_nil]
|
||||
simp only [flatten_nil, nil_eq, append_eq_nil_iff, and_false, cons_append, false_and,
|
||||
exists_const, exists_false, or_false, and_imp, List.cons_ne_nil]
|
||||
rintro rfl rfl
|
||||
exact ⟨[], [], by simp⟩
|
||||
| cons x xs ih =>
|
||||
@@ -1994,6 +1976,13 @@ theorem flatten_eq_append_iff {xs : List (List α)} {ys zs : List α} :
|
||||
· simp
|
||||
· simp
|
||||
|
||||
theorem append_eq_flatten_iff {xs : List (List α)} {ys zs : List α} :
|
||||
ys ++ zs = xs.flatten ↔
|
||||
(∃ as bs, xs = as ++ bs ∧ ys = as.flatten ∧ zs = bs.flatten) ∨
|
||||
∃ as bs c cs ds, xs = as ++ (bs ++ c :: cs) :: ds ∧ ys = as.flatten ++ bs ∧
|
||||
zs = c :: cs ++ ds.flatten := by
|
||||
rw [eq_comm, flatten_eq_append_iff]
|
||||
|
||||
/-- Two lists of sublists are equal iff their flattens coincide, as well as the lengths of the
|
||||
sublists. -/
|
||||
theorem eq_iff_flatten_eq : ∀ {L L' : List (List α)},
|
||||
@@ -2014,12 +2003,14 @@ theorem eq_iff_flatten_eq : ∀ {L L' : List (List α)},
|
||||
|
||||
theorem flatMap_def (l : List α) (f : α → List β) : l.flatMap f = flatten (map f l) := by rfl
|
||||
|
||||
@[simp] theorem flatMap_id (l : List (List α)) : List.flatMap l id = l.flatten := by simp [flatMap_def]
|
||||
@[simp] theorem flatMap_id (l : List (List α)) : l.flatMap id = l.flatten := by simp [flatMap_def]
|
||||
|
||||
@[simp] theorem flatMap_id' (l : List (List α)) : l.flatMap (fun a => a) = l.flatten := by simp [flatMap_def]
|
||||
|
||||
@[simp]
|
||||
theorem length_flatMap (l : List α) (f : α → List β) :
|
||||
length (l.flatMap f) = sum (map (length ∘ f) l) := by
|
||||
rw [List.flatMap, length_flatten, map_map]
|
||||
length (l.flatMap f) = sum (map (fun a => (f a).length) l) := by
|
||||
rw [List.flatMap, length_flatten, map_map, Function.comp_def]
|
||||
|
||||
@[simp] theorem mem_flatMap {f : α → List β} {b} {l : List α} : b ∈ l.flatMap f ↔ ∃ a, a ∈ l ∧ b ∈ f a := by
|
||||
simp [flatMap_def, mem_flatten]
|
||||
@@ -2032,7 +2023,7 @@ theorem mem_flatMap_of_mem {b : β} {l : List α} {f : α → List β} {a} (al :
|
||||
b ∈ l.flatMap f := mem_flatMap.2 ⟨a, al, h⟩
|
||||
|
||||
@[simp]
|
||||
theorem flatMap_eq_nil_iff {l : List α} {f : α → List β} : List.flatMap l f = [] ↔ ∀ x ∈ l, f x = [] :=
|
||||
theorem flatMap_eq_nil_iff {l : List α} {f : α → List β} : l.flatMap f = [] ↔ ∀ x ∈ l, f x = [] :=
|
||||
flatten_eq_nil_iff.trans <| by
|
||||
simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
|
||||
|
||||
@@ -2141,10 +2132,6 @@ theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
|
||||
(replicate n a)[m] = a :=
|
||||
eq_of_mem_replicate (getElem_mem _)
|
||||
|
||||
@[deprecated getElem_replicate (since := "2024-06-12")]
|
||||
theorem get_replicate (a : α) {n : Nat} (m : Fin _) : (replicate n a).get m = a := by
|
||||
simp
|
||||
|
||||
theorem getElem?_replicate : (replicate n a)[m]? = if m < n then some a else none := by
|
||||
by_cases h : m < n
|
||||
· rw [getElem?_eq_getElem (by simpa), getElem_replicate, if_pos h]
|
||||
@@ -2216,7 +2203,7 @@ theorem map_const' (l : List α) (b : β) : map (fun _ => b) l = replicate l.len
|
||||
· intro i h₁ h₂
|
||||
simp [getElem_set]
|
||||
|
||||
@[simp] theorem append_replicate_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
|
||||
@[simp] theorem replicate_append_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
|
||||
rw [eq_replicate_iff]
|
||||
constructor
|
||||
· simp
|
||||
@@ -2224,6 +2211,9 @@ theorem map_const' (l : List α) (b : β) : map (fun _ => b) l = replicate l.len
|
||||
simp only [mem_append, mem_replicate, ne_eq]
|
||||
rintro (⟨-, rfl⟩ | ⟨_, rfl⟩) <;> rfl
|
||||
|
||||
@[deprecated replicate_append_replicate (since := "2025-01-16")]
|
||||
abbrev append_replicate_replicate := @replicate_append_replicate
|
||||
|
||||
theorem append_eq_replicate_iff {l₁ l₂ : List α} {a : α} :
|
||||
l₁ ++ l₂ = replicate n a ↔
|
||||
l₁.length + l₂.length = n ∧ l₁ = replicate l₁.length a ∧ l₂ = replicate l₂.length a := by
|
||||
@@ -2234,6 +2224,11 @@ theorem append_eq_replicate_iff {l₁ l₂ : List α} {a : α} :
|
||||
|
||||
@[deprecated append_eq_replicate_iff (since := "2024-09-05")] abbrev append_eq_replicate := @append_eq_replicate_iff
|
||||
|
||||
theorem replicate_eq_append_iff {l₁ l₂ : List α} {a : α} :
|
||||
replicate n a = l₁ ++ l₂ ↔
|
||||
l₁.length + l₂.length = n ∧ l₁ = replicate l₁.length a ∧ l₂ = replicate l₂.length a := by
|
||||
rw [eq_comm, append_eq_replicate_iff]
|
||||
|
||||
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
|
||||
ext1 n
|
||||
simp only [getElem?_map, getElem?_replicate]
|
||||
@@ -2285,7 +2280,7 @@ theorem filterMap_replicate_of_some {f : α → Option β} (h : f a = some b) :
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, flatten_cons, ih, append_replicate_replicate, replicate_inj, or_true,
|
||||
simp only [replicate_succ, flatten_cons, ih, replicate_append_replicate, replicate_inj, or_true,
|
||||
and_true, add_one_mul, Nat.add_comm]
|
||||
|
||||
theorem flatMap_replicate {β} (f : α → List β) : (replicate n a).flatMap f = (replicate n (f a)).flatten := by
|
||||
@@ -2337,6 +2332,9 @@ theorem replicateRecOn {α : Type _} {p : List α → Prop} (m : List α)
|
||||
exact hi _ _ _ _ h hn (replicateRecOn (b :: l') h0 hr hi)
|
||||
termination_by m.length
|
||||
|
||||
@[simp] theorem sum_replicate_nat (n : Nat) (a : Nat) : (replicate n a).sum = n * a := by
|
||||
induction n <;> simp_all [replicate_succ, Nat.add_mul, Nat.add_comm]
|
||||
|
||||
/-! ### reverse -/
|
||||
|
||||
@[simp] theorem length_reverse (as : List α) : (as.reverse).length = as.length := by
|
||||
@@ -2369,10 +2367,6 @@ theorem getElem?_reverse' : ∀ {l : List α} (i j), i + j + 1 = length l →
|
||||
rw [getElem?_append_left, getElem?_reverse' _ _ this]
|
||||
rw [length_reverse, ← this]; apply Nat.lt_add_of_pos_right (Nat.succ_pos _)
|
||||
|
||||
@[deprecated getElem?_reverse' (since := "2024-06-12")]
|
||||
theorem get?_reverse' {l : List α} (i j) (h : i + j + 1 = length l) : get? l.reverse i = get? l j := by
|
||||
simp [getElem?_reverse' _ _ h]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_reverse {l : List α} {i} (h : i < length l) :
|
||||
l.reverse[i]? = l[l.length - 1 - i]? :=
|
||||
@@ -2387,11 +2381,6 @@ theorem getElem_reverse {l : List α} {i} (h : i < l.reverse.length) :
|
||||
rw [← getElem?_eq_getElem, ← getElem?_eq_getElem]
|
||||
rw [getElem?_reverse (by simpa using h)]
|
||||
|
||||
@[deprecated getElem?_reverse (since := "2024-06-12")]
|
||||
theorem get?_reverse {l : List α} {i} (h : i < length l) :
|
||||
get? l.reverse i = get? l (l.length - 1 - i) := by
|
||||
simp [getElem?_reverse h]
|
||||
|
||||
theorem reverseAux_reverseAux_nil (as bs : List α) : reverseAux (reverseAux as bs) [] = reverseAux bs as := by
|
||||
induction as generalizing bs with
|
||||
| nil => rfl
|
||||
@@ -2432,10 +2421,6 @@ theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a ∈ getLast? l) : a
|
||||
@[simp] theorem map_reverse (f : α → β) (l : List α) : l.reverse.map f = (l.map f).reverse := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[deprecated map_reverse (since := "2024-06-20")]
|
||||
theorem reverse_map (f : α → β) (l : List α) : (l.map f).reverse = l.reverse.map f := by
|
||||
simp
|
||||
|
||||
@[simp] theorem filter_reverse (p : α → Bool) (l : List α) : (l.reverse.filter p) = (l.filter p).reverse := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
@@ -2561,20 +2546,24 @@ theorem foldr_filterMap (f : α → Option β) (g : β → γ → γ) (l : List
|
||||
simp only [filterMap_cons, foldr_cons]
|
||||
cases f a <;> simp [ih]
|
||||
|
||||
theorem foldl_map' (g : α → β) (f : α → α → α) (f' : β → β → β) (a : α) (l : List α)
|
||||
theorem foldl_map_hom (g : α → β) (f : α → α → α) (f' : β → β → β) (a : α) (l : List α)
|
||||
(h : ∀ x y, f' (g x) (g y) = g (f x y)) :
|
||||
(l.map g).foldl f' (g a) = g (l.foldl f a) := by
|
||||
induction l generalizing a
|
||||
· simp
|
||||
· simp [*, h]
|
||||
|
||||
theorem foldr_map' (g : α → β) (f : α → α → α) (f' : β → β → β) (a : α) (l : List α)
|
||||
@[deprecated foldl_map_hom (since := "2025-01-20")] abbrev foldl_map' := @foldl_map_hom
|
||||
|
||||
theorem foldr_map_hom (g : α → β) (f : α → α → α) (f' : β → β → β) (a : α) (l : List α)
|
||||
(h : ∀ x y, f' (g x) (g y) = g (f x y)) :
|
||||
(l.map g).foldr f' (g a) = g (l.foldr f a) := by
|
||||
induction l generalizing a
|
||||
· simp
|
||||
· simp [*, h]
|
||||
|
||||
@[deprecated foldr_map_hom (since := "2025-01-20")] abbrev foldr_map' := @foldr_map_hom
|
||||
|
||||
@[simp] theorem foldrM_append [Monad m] [LawfulMonad m] (f : α → β → m β) (b) (l l' : List α) :
|
||||
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
|
||||
induction l <;> simp [*]
|
||||
@@ -2761,10 +2750,12 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
|
||||
rw [getLast?_eq_head?_reverse, head?_isSome]
|
||||
simp
|
||||
|
||||
theorem mem_of_getLast?_eq_some {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||||
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||||
obtain ⟨ys, rfl⟩ := getLast?_eq_some_iff.1 h
|
||||
exact mem_concat_self ys a
|
||||
|
||||
@[deprecated mem_of_getLast? (since := "2024-10-21")] abbrev mem_of_getLast?_eq_some := @mem_of_getLast?
|
||||
|
||||
@[simp] theorem getLast_reverse {l : List α} (h : l.reverse ≠ []) :
|
||||
l.reverse.getLast h = l.head (by simp_all) := by
|
||||
simp [getLast_eq_head_reverse]
|
||||
@@ -2899,11 +2890,6 @@ are often used for theorems about `Array.pop`.
|
||||
| _::_::_, 0, _ => rfl
|
||||
| _::_::_, i+1, h => getElem_dropLast _ i (Nat.add_one_lt_add_one_iff.mp h)
|
||||
|
||||
@[deprecated getElem_dropLast (since := "2024-06-12")]
|
||||
theorem get_dropLast (xs : List α) (i : Fin xs.dropLast.length) :
|
||||
xs.dropLast.get i = xs.get ⟨i, Nat.lt_of_lt_of_le i.isLt (length_dropLast .. ▸ Nat.pred_le _)⟩ := by
|
||||
simp
|
||||
|
||||
theorem getElem?_dropLast (xs : List α) (i : Nat) :
|
||||
xs.dropLast[i]? = if i < xs.length - 1 then xs[i]? else none := by
|
||||
split
|
||||
@@ -3441,29 +3427,6 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
|
||||
|
||||
/-! ### Deprecations -/
|
||||
|
||||
@[deprecated getD_eq_getElem?_getD (since := "2024-06-12")]
|
||||
theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a := by simp
|
||||
@[deprecated getElem_singleton (since := "2024-06-12")]
|
||||
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
|
||||
@[deprecated getElem?_concat_length (since := "2024-06-12")]
|
||||
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
|
||||
@[deprecated getElem_set_self (since := "2024-06-12")]
|
||||
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
|
||||
(l.set i a).get ⟨i, h⟩ = a := by
|
||||
simp
|
||||
@[deprecated getElem_set_ne (since := "2024-06-12")]
|
||||
theorem get_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α}
|
||||
(hj : j < (l.set i a).length) :
|
||||
(l.set i a).get ⟨j, hj⟩ = l.get ⟨j, by simp at hj; exact hj⟩ := by
|
||||
simp [h]
|
||||
@[deprecated getElem_set (since := "2024-06-12")]
|
||||
theorem get_set {l : List α} {m n} {a : α} (h) :
|
||||
(set l m a).get ⟨n, h⟩ = if m = n then a else l.get ⟨n, length_set .. ▸ h⟩ := by
|
||||
simp [getElem_set]
|
||||
@[deprecated cons_inj_right (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
|
||||
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
|
||||
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
|
||||
|
||||
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
|
||||
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
|
||||
|
||||
|
||||
@@ -17,18 +17,19 @@ namespace List
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
|
||||
/--
|
||||
Given a list `as = [a₀, a₁, ...]` function `f : Fin as.length → α → β`, returns the list
|
||||
`[f 0 a₀, f 1 a₁, ...]`.
|
||||
-/
|
||||
@[inline] def mapFinIdx (as : List α) (f : Fin as.length → α → β) : List β := go as #[] (by simp) where
|
||||
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) → α → (h : i < as.length) → β) : List β :=
|
||||
go as #[] (by simp)
|
||||
where
|
||||
/-- Auxiliary for `mapFinIdx`:
|
||||
`mapFinIdx.go [a₀, a₁, ...] acc = acc.toList ++ [f 0 a₀, f 1 a₁, ...]` -/
|
||||
@[specialize] go : (bs : List α) → (acc : Array β) → bs.length + acc.size = as.length → List β
|
||||
| [], acc, h => acc.toList
|
||||
| a :: as, acc, h =>
|
||||
go as (acc.push (f ⟨acc.size, by simp at h; omega⟩ a)) (by simp at h ⊢; omega)
|
||||
go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
|
||||
|
||||
/--
|
||||
Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁, ...]`, returns the list
|
||||
@@ -43,8 +44,14 @@ Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁,
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
@[congr] theorem mapFinIdx_congr {xs ys : List α} (w : xs = ys)
|
||||
(f : (i : Nat) → α → (h : i < xs.length) → β) :
|
||||
mapFinIdx xs f = mapFinIdx ys (fun i a h => f i a (by simp [w]; omega)) := by
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_nil {f : Fin 0 → α → β} : mapFinIdx [] f = [] :=
|
||||
theorem mapFinIdx_nil {f : (i : Nat) → α → (h : i < 0) → β} : mapFinIdx [] f = [] :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem length_mapFinIdx_go :
|
||||
@@ -53,13 +60,16 @@ theorem mapFinIdx_nil {f : Fin 0 → α → β} : mapFinIdx [] f = [] :=
|
||||
| nil => simpa using h
|
||||
| cons _ _ ih => simp [mapFinIdx.go, ih]
|
||||
|
||||
@[simp] theorem length_mapFinIdx {as : List α} {f : Fin as.length → α → β} :
|
||||
@[simp] theorem length_mapFinIdx {as : List α} {f : (i : Nat) → α → (h : i < as.length) → β} :
|
||||
(as.mapFinIdx f).length = as.length := by
|
||||
simp [mapFinIdx, length_mapFinIdx_go]
|
||||
|
||||
theorem getElem_mapFinIdx_go {as : List α} {f : Fin as.length → α → β} {i : Nat} {h} {w} :
|
||||
theorem getElem_mapFinIdx_go {as : List α} {f : (i : Nat) → α → (h : i < as.length) → β} {i : Nat} {h} {w} :
|
||||
(mapFinIdx.go as f bs acc h)[i] =
|
||||
if w' : i < acc.size then acc[i] else f ⟨i, by simp at w; omega⟩ (bs[i - acc.size]'(by simp at w; omega)) := by
|
||||
if w' : i < acc.size then
|
||||
acc[i]
|
||||
else
|
||||
f i (bs[i - acc.size]'(by simp at w; omega)) (by simp at w; omega) := by
|
||||
induction bs generalizing acc with
|
||||
| nil =>
|
||||
simp only [length_mapFinIdx_go, length_nil, Nat.zero_add] at w h
|
||||
@@ -78,29 +88,30 @@ theorem getElem_mapFinIdx_go {as : List α} {f : Fin as.length → α → β} {i
|
||||
· have h₃ : i - acc.size = (i - (acc.size + 1)) + 1 := by omega
|
||||
simp [h₃]
|
||||
|
||||
@[simp] theorem getElem_mapFinIdx {as : List α} {f : Fin as.length → α → β} {i : Nat} {h} :
|
||||
(as.mapFinIdx f)[i] = f ⟨i, by simp at h; omega⟩ (as[i]'(by simp at h; omega)) := by
|
||||
@[simp] theorem getElem_mapFinIdx {as : List α} {f : (i : Nat) → α → (h : i < as.length) → β} {i : Nat} {h} :
|
||||
(as.mapFinIdx f)[i] = f i (as[i]'(by simp at h; omega)) (by simp at h; omega) := by
|
||||
simp [mapFinIdx, getElem_mapFinIdx_go]
|
||||
|
||||
theorem mapFinIdx_eq_ofFn {as : List α} {f : Fin as.length → α → β} :
|
||||
as.mapFinIdx f = List.ofFn fun i : Fin as.length => f i as[i] := by
|
||||
theorem mapFinIdx_eq_ofFn {as : List α} {f : (i : Nat) → α → (h : i < as.length) → β} :
|
||||
as.mapFinIdx f = List.ofFn fun i : Fin as.length => f i as[i] i.2 := by
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx {l : List α} {f : Fin l.length → α → β} {i : Nat} :
|
||||
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f ⟨i, by simp [getElem?_eq_some_iff] at m; exact m.1⟩ x := by
|
||||
@[simp] theorem getElem?_mapFinIdx {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} {i : Nat} :
|
||||
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => f i x (by simp [getElem?_eq_some_iff] at m; exact m.1) := by
|
||||
simp only [getElem?_def, length_mapFinIdx, getElem_mapFinIdx]
|
||||
split <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_cons {l : List α} {a : α} {f : Fin (l.length + 1) → α → β} :
|
||||
mapFinIdx (a :: l) f = f 0 a :: mapFinIdx l (fun i => f i.succ) := by
|
||||
theorem mapFinIdx_cons {l : List α} {a : α} {f : (i : Nat) → α → (h : i < l.length + 1) → β} :
|
||||
mapFinIdx (a :: l) f = f 0 a (by omega) :: mapFinIdx l (fun i a h => f (i + 1) a (by omega)) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· rintro (_|i) h₁ h₂ <;> simp
|
||||
|
||||
theorem mapFinIdx_append {K L : List α} {f : Fin (K ++ L).length → α → β} :
|
||||
theorem mapFinIdx_append {K L : List α} {f : (i : Nat) → α → (h : i < (K ++ L).length) → β} :
|
||||
(K ++ L).mapFinIdx f =
|
||||
K.mapFinIdx (fun i => f (i.castLE (by simp))) ++ L.mapFinIdx (fun i => f ((i.natAdd K.length).cast (by simp))) := by
|
||||
K.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
|
||||
L.mapFinIdx (fun i a h => f (i + K.length) a (by simp; omega)) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro i h₁ h₂
|
||||
@@ -108,60 +119,57 @@ theorem mapFinIdx_append {K L : List α} {f : Fin (K ++ L).length → α → β}
|
||||
simp only [getElem_mapFinIdx, length_mapFinIdx]
|
||||
split <;> rename_i h
|
||||
· rw [getElem_append_left]
|
||||
congr
|
||||
· simp only [Nat.not_lt] at h
|
||||
rw [getElem_append_right h]
|
||||
congr
|
||||
simp
|
||||
omega
|
||||
|
||||
@[simp] theorem mapFinIdx_concat {l : List α} {e : α} {f : Fin (l ++ [e]).length → α → β}:
|
||||
(l ++ [e]).mapFinIdx f = l.mapFinIdx (fun i => f (i.castLE (by simp))) ++ [f ⟨l.length, by simp⟩ e] := by
|
||||
@[simp] theorem mapFinIdx_concat {l : List α} {e : α} {f : (i : Nat) → α → (h : i < (l ++ [e]).length) → β}:
|
||||
(l ++ [e]).mapFinIdx f = l.mapFinIdx (fun i a h => f i a (by simp; omega)) ++ [f l.length e (by simp)] := by
|
||||
simp [mapFinIdx_append]
|
||||
congr
|
||||
|
||||
theorem mapFinIdx_singleton {a : α} {f : Fin 1 → α → β} :
|
||||
[a].mapFinIdx f = [f ⟨0, by simp⟩ a] := by
|
||||
theorem mapFinIdx_singleton {a : α} {f : (i : Nat) → α → (h : i < 1) → β} :
|
||||
[a].mapFinIdx f = [f 0 a (by simp)] := by
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_enum_map {l : List α} {f : Fin l.length → α → β} :
|
||||
theorem mapFinIdx_eq_enum_map {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = l.enum.attach.map
|
||||
fun ⟨⟨i, x⟩, m⟩ =>
|
||||
f ⟨i, by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1⟩ x := by
|
||||
f i x (by rw [mk_mem_enum_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
|
||||
apply ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_eq_nil_iff {l : List α} {f : Fin l.length → α → β} :
|
||||
theorem mapFinIdx_eq_nil_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = [] ↔ l = [] := by
|
||||
rw [mapFinIdx_eq_enum_map, map_eq_nil_iff, attach_eq_nil_iff, enum_eq_nil_iff]
|
||||
|
||||
theorem mapFinIdx_ne_nil_iff {l : List α} {f : Fin l.length → α → β} :
|
||||
theorem mapFinIdx_ne_nil_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f ≠ [] ↔ l ≠ [] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapFinIdx {b : β} {l : List α} {f : Fin l.length → α → β}
|
||||
(h : b ∈ l.mapFinIdx f) : ∃ (i : Fin l.length), f i l[i] = b := by
|
||||
theorem exists_of_mem_mapFinIdx {b : β} {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β}
|
||||
(h : b ∈ l.mapFinIdx f) : ∃ (i : Nat) (h : i < l.length), f i l[i] h = b := by
|
||||
rw [mapFinIdx_eq_enum_map] at h
|
||||
replace h := exists_of_mem_map h
|
||||
simp only [mem_attach, true_and, Subtype.exists, Prod.exists, mk_mem_enum_iff_getElem?] at h
|
||||
obtain ⟨i, b, h, rfl⟩ := h
|
||||
rw [getElem?_eq_some_iff] at h
|
||||
obtain ⟨h', rfl⟩ := h
|
||||
exact ⟨⟨i, h'⟩, rfl⟩
|
||||
exact ⟨i, h', rfl⟩
|
||||
|
||||
@[simp] theorem mem_mapFinIdx {b : β} {l : List α} {f : Fin l.length → α → β} :
|
||||
b ∈ l.mapFinIdx f ↔ ∃ (i : Fin l.length), f i l[i] = b := by
|
||||
@[simp] theorem mem_mapFinIdx {b : β} {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
b ∈ l.mapFinIdx f ↔ ∃ (i : Nat) (h : i < l.length), f i l[i] h = b := by
|
||||
constructor
|
||||
· intro h
|
||||
exact exists_of_mem_mapFinIdx h
|
||||
· rintro ⟨i, h, rfl⟩
|
||||
rw [mem_iff_getElem]
|
||||
exact ⟨i, by simp⟩
|
||||
exact ⟨i, by simpa using h, by simp⟩
|
||||
|
||||
theorem mapFinIdx_eq_cons_iff {l : List α} {b : β} {f : Fin l.length → α → β} :
|
||||
theorem mapFinIdx_eq_cons_iff {l : List α} {b : β} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = b :: l₂ ↔
|
||||
∃ (a : α) (l₁ : List α) (h : l = a :: l₁),
|
||||
f ⟨0, by simp [h]⟩ a = b ∧ l₁.mapFinIdx (fun i => f (i.succ.cast (by simp [h]))) = l₂ := by
|
||||
∃ (a : α) (l₁ : List α) (w : l = a :: l₁),
|
||||
f 0 a (by simp [w]) = b ∧ l₁.mapFinIdx (fun i a h => f (i + 1) a (by simp [w]; omega)) = l₂ := by
|
||||
cases l with
|
||||
| nil => simp
|
||||
| cons x l' =>
|
||||
@@ -169,39 +177,91 @@ theorem mapFinIdx_eq_cons_iff {l : List α} {b : β} {f : Fin l.length → α
|
||||
exists_and_left]
|
||||
constructor
|
||||
· rintro ⟨rfl, rfl⟩
|
||||
refine ⟨x, rfl, l', by simp⟩
|
||||
· rintro ⟨a, ⟨rfl, h⟩, ⟨_, ⟨rfl, rfl⟩, h⟩⟩
|
||||
exact ⟨rfl, h⟩
|
||||
refine ⟨x, l', ⟨rfl, rfl⟩, by simp⟩
|
||||
· rintro ⟨a, l', ⟨rfl, rfl⟩, ⟨rfl, rfl⟩⟩
|
||||
exact ⟨rfl, by simp⟩
|
||||
|
||||
theorem mapFinIdx_eq_cons_iff' {l : List α} {b : β} {f : Fin l.length → α → β} :
|
||||
theorem mapFinIdx_eq_cons_iff' {l : List α} {b : β} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = b :: l₂ ↔
|
||||
l.head?.pbind (fun x m => (f ⟨0, by cases l <;> simp_all⟩ x)) = some b ∧
|
||||
l.tail?.attach.map (fun ⟨t, m⟩ => t.mapFinIdx fun i => f (i.succ.cast (by cases l <;> simp_all))) = some l₂ := by
|
||||
l.head?.pbind (fun x m => (f 0 x (by cases l <;> simp_all))) = some b ∧
|
||||
l.tail?.attach.map (fun ⟨t, m⟩ => t.mapFinIdx fun i a h => f (i + 1) a (by cases l <;> simp_all)) = some l₂ := by
|
||||
cases l <;> simp
|
||||
|
||||
theorem mapFinIdx_eq_iff {l : List α} {f : Fin l.length → α → β} :
|
||||
l.mapFinIdx f = l' ↔ ∃ h : l'.length = l.length, ∀ (i : Nat) (h : i < l.length), l'[i] = f ⟨i, h⟩ l[i] := by
|
||||
theorem mapFinIdx_eq_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = l' ↔ ∃ h : l'.length = l.length, ∀ (i : Nat) (h : i < l.length), l'[i] = f i l[i] h := by
|
||||
constructor
|
||||
· rintro rfl
|
||||
simp
|
||||
· rintro ⟨h, w⟩
|
||||
apply ext_getElem <;> simp_all
|
||||
|
||||
theorem mapFinIdx_eq_mapFinIdx_iff {l : List α} {f g : Fin l.length → α → β} :
|
||||
l.mapFinIdx f = l.mapFinIdx g ↔ ∀ (i : Fin l.length), f i l[i] = g i l[i] := by
|
||||
@[simp] theorem mapFinIdx_eq_singleton_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} {b : β} :
|
||||
l.mapFinIdx f = [b] ↔ ∃ (a : α) (w : l = [a]), f 0 a (by simp [w]) = b := by
|
||||
simp [mapFinIdx_eq_cons_iff]
|
||||
|
||||
theorem mapFinIdx_eq_append_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : List α) (l₂' : List α) (w : l = l₁' ++ l₂'),
|
||||
l₁'.mapFinIdx (fun i a h => f i a (by simp [w]; omega)) = l₁ ∧
|
||||
l₂'.mapFinIdx (fun i a h => f (i + l₁'.length) a (by simp [w]; omega)) = l₂ := by
|
||||
rw [mapFinIdx_eq_iff]
|
||||
constructor
|
||||
· intro ⟨h, w⟩
|
||||
simp only [length_append] at h
|
||||
refine ⟨l.take l₁.length, l.drop l₁.length, by simp, ?_⟩
|
||||
constructor
|
||||
· apply ext_getElem
|
||||
· simp
|
||||
omega
|
||||
· intro i hi₁ hi₂
|
||||
simp only [getElem_mapFinIdx, getElem_take]
|
||||
specialize w i (by omega)
|
||||
rw [getElem_append_left hi₂] at w
|
||||
exact w.symm
|
||||
· apply ext_getElem
|
||||
· simp
|
||||
omega
|
||||
· intro i hi₁ hi₂
|
||||
simp only [getElem_mapFinIdx, getElem_take]
|
||||
simp only [length_take, getElem_drop]
|
||||
have : l₁.length ≤ l.length := by omega
|
||||
simp only [Nat.min_eq_left this, Nat.add_comm]
|
||||
specialize w (i + l₁.length) (by omega)
|
||||
rw [getElem_append_right (by omega)] at w
|
||||
simpa using w.symm
|
||||
· rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
|
||||
refine ⟨by simp, fun i h => ?_⟩
|
||||
rw [getElem_append]
|
||||
split <;> rename_i h'
|
||||
· simp [getElem_append_left (by simpa using h')]
|
||||
· simp only [length_mapFinIdx, Nat.not_lt] at h'
|
||||
have : i - l₁'.length + l₁'.length = i := by omega
|
||||
simp [getElem_append_right h', this]
|
||||
|
||||
theorem mapFinIdx_eq_mapFinIdx_iff {l : List α} {f g : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.mapFinIdx f = l.mapFinIdx g ↔ ∀ (i : Nat) (h : i < l.length), f i l[i] h = g i l[i] h := by
|
||||
rw [eq_comm, mapFinIdx_eq_iff]
|
||||
simp [Fin.forall_iff]
|
||||
|
||||
@[simp] theorem mapFinIdx_mapFinIdx {l : List α} {f : Fin l.length → α → β} {g : Fin _ → β → γ} :
|
||||
(l.mapFinIdx f).mapFinIdx g = l.mapFinIdx (fun i => g (i.cast (by simp)) ∘ f i) := by
|
||||
@[simp] theorem mapFinIdx_mapFinIdx {l : List α}
|
||||
{f : (i : Nat) → α → (h : i < l.length) → β}
|
||||
{g : (i : Nat) → β → (h : i < (l.mapFinIdx f).length) → γ} :
|
||||
(l.mapFinIdx f).mapFinIdx g = l.mapFinIdx (fun i a h => g i (f i a h) (by simpa)) := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapFinIdx_eq_replicate_iff {l : List α} {f : Fin l.length → α → β} {b : β} :
|
||||
l.mapFinIdx f = replicate l.length b ↔ ∀ (i : Fin l.length), f i l[i] = b := by
|
||||
simp [eq_replicate_iff, length_mapFinIdx, mem_mapFinIdx, forall_exists_index, true_and]
|
||||
theorem mapFinIdx_eq_replicate_iff {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} {b : β} :
|
||||
l.mapFinIdx f = replicate l.length b ↔ ∀ (i : Nat) (h : i < l.length), f i l[i] h = b := by
|
||||
rw [eq_replicate_iff, length_mapFinIdx]
|
||||
simp only [mem_mapFinIdx, forall_exists_index, true_and]
|
||||
constructor
|
||||
· intro w i h
|
||||
exact w (f i l[i] h) i h rfl
|
||||
· rintro w b i h rfl
|
||||
exact w i h
|
||||
|
||||
@[simp] theorem mapFinIdx_reverse {l : List α} {f : Fin l.reverse.length → α → β} :
|
||||
l.reverse.mapFinIdx f = (l.mapFinIdx (fun i => f ⟨l.length - 1 - i, by simp; omega⟩)).reverse := by
|
||||
@[simp] theorem mapFinIdx_reverse {l : List α} {f : (i : Nat) → α → (h : i < l.reverse.length) → β} :
|
||||
l.reverse.mapFinIdx f =
|
||||
(l.mapFinIdx (fun i a h => f (l.length - 1 - i) a (by simp; omega))).reverse := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
intro i h
|
||||
congr
|
||||
@@ -262,13 +322,13 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {arr : Array β} {i : Nat},
|
||||
rw [← getElem?_eq_getElem, getElem?_mapIdx, getElem?_eq_getElem (by simpa using h)]
|
||||
simp
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_mapIdx {l : List α} {f : Fin l.length → α → β} {g : Nat → α → β}
|
||||
(h : ∀ (i : Fin l.length), f i l[i] = g i l[i]) :
|
||||
@[simp] theorem mapFinIdx_eq_mapIdx {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} {g : Nat → α → β}
|
||||
(h : ∀ (i : Nat) (h : i < l.length), f i l[i] h = g i l[i]) :
|
||||
l.mapFinIdx f = l.mapIdx g := by
|
||||
simp_all [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapFinIdx {l : List α} {f : Nat → α → β} :
|
||||
l.mapIdx f = l.mapFinIdx (fun i => f i) := by
|
||||
l.mapIdx f = l.mapFinIdx (fun i a _ => f i a) := by
|
||||
simp [mapFinIdx_eq_mapIdx]
|
||||
|
||||
theorem mapIdx_eq_enum_map {l : List α} :
|
||||
@@ -328,6 +388,10 @@ theorem mapIdx_eq_cons_iff' {l : List α} {b : β} :
|
||||
l.head?.map (f 0) = some b ∧ l.tail?.map (mapIdx fun i => f (i + 1)) = some l₂ := by
|
||||
cases l <;> simp
|
||||
|
||||
@[simp] theorem mapIdx_eq_singleton_iff {l : List α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f l = [b] ↔ ∃ (a : α), l = [a] ∧ f 0 a = b := by
|
||||
simp [mapIdx_eq_cons_iff]
|
||||
|
||||
theorem mapIdx_eq_iff {l : List α} : mapIdx f l = l' ↔ ∀ i : Nat, l'[i]? = l[i]?.map (f i) := by
|
||||
constructor
|
||||
· intro w i
|
||||
@@ -336,6 +400,19 @@ theorem mapIdx_eq_iff {l : List α} : mapIdx f l = l' ↔ ∀ i : Nat, l'[i]? =
|
||||
ext1 i
|
||||
simp [w]
|
||||
|
||||
theorem mapIdx_eq_append_iff {l : List α} :
|
||||
mapIdx f l = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : List α) (l₂' : List α), l = l₁' ++ l₂' ∧
|
||||
mapIdx f l₁' = l₁ ∧
|
||||
mapIdx (fun i => f (i + l₁'.length)) l₂' = l₂ := by
|
||||
rw [mapIdx_eq_mapFinIdx, mapFinIdx_eq_append_iff]
|
||||
simp only [mapFinIdx_eq_mapIdx, exists_and_left, exists_prop]
|
||||
constructor
|
||||
· rintro ⟨l₁, rfl, l₂, rfl, h⟩
|
||||
refine ⟨l₁, l₂, by simp_all⟩
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, rfl⟩
|
||||
refine ⟨l₁, rfl, l₂, by simp_all⟩
|
||||
|
||||
theorem mapIdx_eq_mapIdx_iff {l : List α} :
|
||||
mapIdx f l = mapIdx g l ↔ ∀ i : Nat, (h : i < l.length) → f i l[i] = g i l[i] := by
|
||||
constructor
|
||||
|
||||
@@ -195,24 +195,32 @@ theorem erase_range : (range n).erase i = range (min n i) ++ range' (i + 1) (n -
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
section
|
||||
set_option linter.deprecated false
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem iota_eq_reverse_range' : ∀ n : Nat, iota n = reverse (range' 1 n)
|
||||
| 0 => rfl
|
||||
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
|
||||
|
||||
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
|
||||
|
||||
@[simp] theorem iota_eq_nil {n : Nat} : iota n = [] ↔ n = 0 := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem iota_eq_nil {n : Nat} : iota n = [] ↔ n = 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem iota_ne_nil {n : Nat} : iota n ≠ [] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp]
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem mem_iota {m n : Nat} : m ∈ iota n ↔ 0 < m ∧ m ≤ n := by
|
||||
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
|
||||
omega
|
||||
|
||||
@[simp] theorem iota_inj : iota n = iota n' ↔ n = n' := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem iota_inj : iota n = iota n' ↔ n = n' := by
|
||||
constructor
|
||||
· intro h
|
||||
have h' := congrArg List.length h
|
||||
@@ -221,6 +229,7 @@ theorem mem_iota {m n : Nat} : m ∈ iota n ↔ 0 < m ∧ m ≤ n := by
|
||||
· rintro rfl
|
||||
simp
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem iota_eq_cons_iff : iota n = a :: xs ↔ n = a ∧ 0 < n ∧ xs = iota (n - 1) := by
|
||||
simp [iota_eq_reverse_range']
|
||||
simp [range'_eq_append_iff, reverse_eq_iff]
|
||||
@@ -234,6 +243,7 @@ theorem iota_eq_cons_iff : iota n = a :: xs ↔ n = a ∧ 0 < n ∧ xs = iota (n
|
||||
rw [eq_comm, range'_eq_singleton]
|
||||
omega
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem iota_eq_append_iff : iota n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs = (range' (k + 1) (n - k)).reverse ∧ ys = iota k := by
|
||||
simp only [iota_eq_reverse_range']
|
||||
rw [reverse_eq_append_iff]
|
||||
@@ -245,42 +255,52 @@ theorem iota_eq_append_iff : iota n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs = (rang
|
||||
· rintro ⟨k, h, rfl, rfl⟩
|
||||
exact ⟨k, by simp; omega⟩
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
|
||||
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
|
||||
(pairwise_gt_iota n).imp Nat.ne_of_gt
|
||||
|
||||
@[simp] theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
|
||||
cases n with
|
||||
| zero => simp at h
|
||||
| succ n => simp
|
||||
|
||||
@[simp] theorem tail_iota (n : Nat) : (iota n).tail = iota (n - 1) := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem tail_iota (n : Nat) : (iota n).tail = iota (n - 1) := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem reverse_iota : reverse (iota n) = range' 1 n := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem reverse_iota : reverse (iota n) = range' 1 n := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
|
||||
|
||||
@[simp] theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
|
||||
rw [getLast?_eq_head?_reverse]
|
||||
simp [head?_range']
|
||||
|
||||
@[simp] theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
|
||||
rw [getLast_eq_head_reverse]
|
||||
simp
|
||||
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
|
||||
theorem find?_iota_eq_none {n : Nat} {p : Nat → Bool} :
|
||||
(iota n).find? p = none ↔ ∀ i, 0 < i → i ≤ n → !p i := by
|
||||
simp
|
||||
|
||||
@[simp] theorem find?_iota_eq_some {n : Nat} {i : Nat} {p : Nat → Bool} :
|
||||
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
|
||||
theorem find?_iota_eq_some {n : Nat} {i : Nat} {p : Nat → Bool} :
|
||||
(iota n).find? p = some i ↔ p i ∧ i ∈ iota n ∧ ∀ j, i < j → j ≤ n → !p j := by
|
||||
rw [find?_eq_some_iff_append]
|
||||
simp only [iota_eq_reverse_range', reverse_eq_append_iff, reverse_cons, append_assoc, cons_append,
|
||||
@@ -317,6 +337,8 @@ theorem find?_iota_eq_none {n : Nat} {p : Nat → Bool} :
|
||||
· omega
|
||||
· omega
|
||||
|
||||
end
|
||||
|
||||
/-! ### enumFrom -/
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -47,41 +47,16 @@ length `> i`. Version designed to rewrite from the small list to the big list. -
|
||||
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
|
||||
rw [length_take, Nat.lt_min] at h; rw [getElem_take' L _ h.1]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_take' (since := "2024-06-12")]
|
||||
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
get L ⟨i, hi⟩ = get (L.take j) ⟨i, length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩⟩ := by
|
||||
simp
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_take (since := "2024-06-12")]
|
||||
theorem get_take' (L : List α) {j i} :
|
||||
get (L.take j) i =
|
||||
get L ⟨i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _)⟩ := by
|
||||
simp [getElem_take]
|
||||
|
||||
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n)[m]? = none :=
|
||||
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
|
||||
|
||||
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
|
||||
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n).get? m = none := by
|
||||
simp [getElem?_take_eq_none h]
|
||||
|
||||
theorem getElem?_take {l : List α} {n m : Nat} :
|
||||
(l.take n)[m]? = if m < n then l[m]? else none := by
|
||||
split
|
||||
· next h => exact getElem?_take_of_lt h
|
||||
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
|
||||
|
||||
@[deprecated getElem?_take (since := "2024-06-12")]
|
||||
theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
simp [getElem?_take]
|
||||
|
||||
theorem head?_take {l : List α} {n : Nat} :
|
||||
(l.take n).head? = if n = 0 then none else l.head? := by
|
||||
simp [head?_eq_getElem?, getElem?_take]
|
||||
@@ -226,13 +201,6 @@ theorem getElem_drop' (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
· simp [Nat.min_eq_left this, Nat.add_sub_cancel_left]
|
||||
· simp [Nat.min_eq_left this, Nat.le_add_right]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_drop' (since := "2024-06-12")]
|
||||
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
get L ⟨i + j, h⟩ = get (L.drop i) ⟨j, lt_length_drop L h⟩ := by
|
||||
simp [getElem_drop']
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
@[simp] theorem getElem_drop (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
|
||||
@@ -241,15 +209,6 @@ dropping the first `i` elements. Version designed to rewrite from the small list
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ h)) := by
|
||||
rw [getElem_drop']
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_drop' (since := "2024-06-12")]
|
||||
theorem get_drop' (L : List α) {i j} :
|
||||
get (L.drop i) j = get L ⟨i + j, by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ j.2)⟩ := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
|
||||
ext
|
||||
@@ -261,10 +220,6 @@ theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? :=
|
||||
rw [Nat.add_comm] at h
|
||||
apply Nat.lt_sub_of_add_lt h
|
||||
|
||||
@[deprecated getElem?_drop (since := "2024-06-12")]
|
||||
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
|
||||
simp
|
||||
|
||||
theorem mem_take_iff_getElem {l : List α} {a : α} :
|
||||
a ∈ l.take n ↔ ∃ (i : Nat) (hm : i < min n l.length), l[i] = a := by
|
||||
rw [mem_iff_getElem]
|
||||
|
||||
@@ -67,17 +67,9 @@ theorem getElem_cons_drop : ∀ (l : List α) (i : Nat) (h : i < l.length),
|
||||
| _::_, 0, _ => rfl
|
||||
| _::_, i+1, h => getElem_cons_drop _ i (Nat.add_one_lt_add_one_iff.mp h)
|
||||
|
||||
@[deprecated getElem_cons_drop (since := "2024-06-12")]
|
||||
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
|
||||
simp
|
||||
|
||||
theorem drop_eq_getElem_cons {n} {l : List α} (h : n < l.length) : drop n l = l[n] :: drop (n + 1) l :=
|
||||
(getElem_cons_drop _ n h).symm
|
||||
|
||||
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
|
||||
theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ :: drop (n + 1) l := by
|
||||
simp [drop_eq_getElem_cons]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
|
||||
induction n generalizing l m with
|
||||
@@ -91,10 +83,6 @@ theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m
|
||||
· simp
|
||||
· simpa using hn (Nat.lt_of_succ_lt_succ h)
|
||||
|
||||
@[deprecated getElem?_take_of_lt (since := "2024-06-12")]
|
||||
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
|
||||
simp [getElem?_take_of_lt, h]
|
||||
|
||||
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? := by simp
|
||||
|
||||
@[simp] theorem drop_drop (n : Nat) : ∀ (m) (l : List α), drop n (drop m l) = drop (m + n) l
|
||||
@@ -111,10 +99,6 @@ theorem take_drop : ∀ (m n : Nat) (l : List α), take n (drop m l) = drop m (t
|
||||
| _, _, [] => by simp
|
||||
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
|
||||
|
||||
@[deprecated drop_drop (since := "2024-06-15")]
|
||||
theorem drop_add (m n) (l : List α) : drop (m + n) l = drop n (drop m l) := by
|
||||
simp [drop_drop]
|
||||
|
||||
@[simp]
|
||||
theorem tail_drop (l : List α) (n : Nat) : (l.drop n).tail = l.drop (n + 1) := by
|
||||
induction l generalizing n with
|
||||
|
||||
@@ -143,6 +143,9 @@ theorem forM_toArray [Monad m] (l : List α) (f : α → m PUnit) :
|
||||
subst h
|
||||
rw [foldl_toList]
|
||||
|
||||
@[simp] theorem sum_toArray [Add α] [Zero α] (l : List α) : l.toArray.sum = l.sum := by
|
||||
simp [Array.sum, List.sum]
|
||||
|
||||
@[simp] theorem append_toArray (l₁ l₂ : List α) :
|
||||
l₁.toArray ++ l₂.toArray = (l₁ ++ l₂).toArray := by
|
||||
apply ext'
|
||||
@@ -394,4 +397,24 @@ theorem takeWhile_go_toArray (p : α → Bool) (l : List α) (i : Nat) :
|
||||
@[deprecated toArray_replicate (since := "2024-12-13")]
|
||||
abbrev _root_.Array.mkArray_eq_toArray_replicate := @toArray_replicate
|
||||
|
||||
@[simp] theorem flatMap_empty {β} (f : α → Array β) : (#[] : Array α).flatMap f = #[] := rfl
|
||||
|
||||
theorem flatMap_toArray_cons {β} (f : α → Array β) (a : α) (as : List α) :
|
||||
(a :: as).toArray.flatMap f = f a ++ as.toArray.flatMap f := by
|
||||
simp [Array.flatMap]
|
||||
suffices ∀ cs, List.foldl (fun bs a => bs ++ f a) (f a ++ cs) as =
|
||||
f a ++ List.foldl (fun bs a => bs ++ f a) cs as by
|
||||
erw [empty_append] -- Why doesn't this work via `simp`?
|
||||
simpa using this #[]
|
||||
intro cs
|
||||
induction as generalizing cs <;> simp_all
|
||||
|
||||
@[simp] theorem flatMap_toArray {β} (f : α → Array β) (as : List α) :
|
||||
as.toArray.flatMap f = (as.flatMap (fun a => (f a).toList)).toArray := by
|
||||
induction as with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
apply ext'
|
||||
simp [ih, flatMap_toArray_cons]
|
||||
|
||||
end List
|
||||
|
||||
@@ -76,15 +76,6 @@ theorem getElem?_zip_eq_some {l₁ : List α} {l₂ : List β} {z : α × β} {i
|
||||
· rintro ⟨h₀, h₁⟩
|
||||
exact ⟨_, _, h₀, h₁, rfl⟩
|
||||
|
||||
@[deprecated getElem?_zipWith (since := "2024-06-12")]
|
||||
theorem get?_zipWith {f : α → β → γ} :
|
||||
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
simp [getElem?_zipWith]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem?_zipWith (since := "2024-06-07")] abbrev zipWith_get? := @get?_zipWith
|
||||
|
||||
theorem head?_zipWith {f : α → β → γ} :
|
||||
(List.zipWith f as bs).head? = match as.head?, bs.head? with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
@@ -203,11 +194,11 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : Li
|
||||
cases l₂ with
|
||||
| nil =>
|
||||
constructor
|
||||
· simp only [zipWith_nil_right, nil_eq, append_eq_nil, exists_and_left, and_imp]
|
||||
· simp only [zipWith_nil_right, nil_eq, append_eq_nil_iff, exists_and_left, and_imp]
|
||||
rintro rfl rfl
|
||||
exact ⟨[], x₁ :: l₁, [], by simp⟩
|
||||
· rintro ⟨w, x, y, z, h₁, _, h₃, rfl, rfl⟩
|
||||
simp only [nil_eq, append_eq_nil] at h₃
|
||||
simp only [nil_eq, append_eq_nil_iff] at h₃
|
||||
obtain ⟨rfl, rfl⟩ := h₃
|
||||
simp
|
||||
| cons x₂ l₂ =>
|
||||
@@ -369,15 +360,6 @@ theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
|
||||
cases i <;> simp_all
|
||||
| cons b bs => cases i <;> simp_all
|
||||
|
||||
@[deprecated getElem?_zipWithAll (since := "2024-06-12")]
|
||||
theorem get?_zipWithAll {f : Option α → Option β → γ} :
|
||||
(zipWithAll f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
simp [getElem?_zipWithAll]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem?_zipWithAll (since := "2024-06-07")] abbrev zipWithAll_get? := @get?_zipWithAll
|
||||
|
||||
theorem head?_zipWithAll {f : Option α → Option β → γ} :
|
||||
(zipWithAll f as bs).head? = match as.head?, bs.head? with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
|
||||
@@ -788,9 +788,6 @@ theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := by
|
||||
theorem pred_lt_of_lt {n m : Nat} (h : m < n) : pred n < n :=
|
||||
pred_lt (not_eq_zero_of_lt h)
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated pred_lt_of_lt (since := "2024-06-01")] abbrev pred_lt' := @pred_lt_of_lt
|
||||
|
||||
theorem sub_one_lt_of_lt {n m : Nat} (h : m < n) : n - 1 < n :=
|
||||
sub_one_lt (not_eq_zero_of_lt h)
|
||||
|
||||
@@ -1074,9 +1071,6 @@ theorem pred_mul (n m : Nat) : pred n * m = n * m - m := by
|
||||
| zero => simp
|
||||
| succ n => rw [Nat.pred_succ, succ_mul, Nat.add_sub_cancel]
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated pred_mul (since := "2024-06-01")] abbrev mul_pred_left := @pred_mul
|
||||
|
||||
protected theorem sub_one_mul (n m : Nat) : (n - 1) * m = n * m - m := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
@@ -1086,9 +1080,6 @@ protected theorem sub_one_mul (n m : Nat) : (n - 1) * m = n * m - m := by
|
||||
theorem mul_pred (n m : Nat) : n * pred m = n * m - n := by
|
||||
rw [Nat.mul_comm, pred_mul, Nat.mul_comm]
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
@[deprecated mul_pred (since := "2024-06-01")] abbrev mul_pred_right := @mul_pred
|
||||
|
||||
theorem mul_sub_one (n m : Nat) : n * (m - 1) = n * m - n := by
|
||||
rw [Nat.mul_comm, Nat.sub_one_mul , Nat.mul_comm]
|
||||
|
||||
|
||||
@@ -711,6 +711,32 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
|
||||
rw [mod_two_eq_one_iff_testBit_zero, testBit_shiftLeft]
|
||||
simp
|
||||
|
||||
theorem testBit_mul_two_pow (x i n : Nat) :
|
||||
(x * 2 ^ n).testBit i = (decide (n ≤ i) && x.testBit (i - n)) := by
|
||||
rw [← testBit_shiftLeft, shiftLeft_eq]
|
||||
|
||||
theorem bitwise_mul_two_pow (of_false_false : f false false = false := by rfl) :
|
||||
(bitwise f x y) * 2 ^ n = bitwise f (x * 2 ^ n) (y * 2 ^ n) := by
|
||||
apply Nat.eq_of_testBit_eq
|
||||
simp only [testBit_mul_two_pow, testBit_bitwise of_false_false, Bool.if_false_right]
|
||||
intro i
|
||||
by_cases hn : n ≤ i
|
||||
· simp [hn]
|
||||
· simp [hn, of_false_false]
|
||||
|
||||
theorem shiftLeft_bitwise_distrib {a b : Nat} (of_false_false : f false false = false := by rfl) :
|
||||
(bitwise f a b) <<< i = bitwise f (a <<< i) (b <<< i) := by
|
||||
simp [shiftLeft_eq, bitwise_mul_two_pow of_false_false]
|
||||
|
||||
theorem shiftLeft_and_distrib {a b : Nat} : (a &&& b) <<< i = a <<< i &&& b <<< i :=
|
||||
shiftLeft_bitwise_distrib
|
||||
|
||||
theorem shiftLeft_or_distrib {a b : Nat} : (a ||| b) <<< i = a <<< i ||| b <<< i :=
|
||||
shiftLeft_bitwise_distrib
|
||||
|
||||
theorem shiftLeft_xor_distrib {a b : Nat} : (a ^^^ b) <<< i = a <<< i ^^^ b <<< i :=
|
||||
shiftLeft_bitwise_distrib
|
||||
|
||||
@[simp] theorem decide_shiftRight_mod_two_eq_one :
|
||||
decide (x >>> i % 2 = 1) = x.testBit i := by
|
||||
simp only [testBit, one_and_eq_mod_two, mod_two_bne_zero]
|
||||
|
||||
@@ -622,6 +622,14 @@ protected theorem pos_of_mul_pos_right {a b : Nat} (h : 0 < a * b) : 0 < a := by
|
||||
0 < a * b ↔ 0 < a :=
|
||||
⟨Nat.pos_of_mul_pos_right, fun w => Nat.mul_pos w h⟩
|
||||
|
||||
protected theorem pos_of_lt_mul_left {a b c : Nat} (h : a < b * c) : 0 < c := by
|
||||
replace h : 0 < b * c := by omega
|
||||
exact Nat.pos_of_mul_pos_left h
|
||||
|
||||
protected theorem pos_of_lt_mul_right {a b c : Nat} (h : a < b * c) : 0 < b := by
|
||||
replace h : 0 < b * c := by omega
|
||||
exact Nat.pos_of_mul_pos_right h
|
||||
|
||||
/-! ### div/mod -/
|
||||
|
||||
theorem mod_two_eq_zero_or_one (n : Nat) : n % 2 = 0 ∨ n % 2 = 1 :=
|
||||
@@ -995,11 +1003,6 @@ theorem shiftLeft_add (m n : Nat) : ∀ k, m <<< (n + k) = (m <<< n) <<< k
|
||||
| 0 => rfl
|
||||
| k + 1 => by simp [← Nat.add_assoc, shiftLeft_add _ _ k, shiftLeft_succ]
|
||||
|
||||
@[deprecated shiftLeft_add (since := "2024-06-02")]
|
||||
theorem shiftLeft_shiftLeft (m n : Nat) : ∀ k, (m <<< n) <<< k = m <<< (n + k)
|
||||
| 0 => rfl
|
||||
| k + 1 => by simp [← Nat.add_assoc, shiftLeft_shiftLeft _ _ k, shiftLeft_succ]
|
||||
|
||||
@[simp] theorem shiftLeft_shiftRight (x n : Nat) : x <<< n >>> n = x := by
|
||||
rw [Nat.shiftLeft_eq, Nat.shiftRight_eq_div_pow, Nat.mul_div_cancel _ (Nat.two_pow_pos _)]
|
||||
|
||||
|
||||
@@ -718,8 +718,7 @@ theorem Expr.eq_of_toNormPoly_eq (ctx : Context) (e e' : Expr) (h : e.toNormPoly
|
||||
|
||||
end Linear
|
||||
|
||||
def elimOffset {α : Sort u} (a b k : Nat) (h₁ : a + k = b + k) (h₂ : a = b → α) : α := by
|
||||
simp_arith at h₁
|
||||
exact h₂ h₁
|
||||
def elimOffset {α : Sort u} (a b k : Nat) (h₁ : a + k = b + k) (h₂ : a = b → α) : α :=
|
||||
h₂ (Nat.add_right_cancel h₁)
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -208,6 +208,15 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
|
||||
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some' ..
|
||||
|
||||
theorem map_inj_right {f : α → β} {o o' : Option α} (w : ∀ x y, f x = f y → x = y) :
|
||||
o.map f = o'.map f ↔ o = o' := by
|
||||
cases o with
|
||||
| none => cases o' <;> simp
|
||||
| some a =>
|
||||
cases o' with
|
||||
| none => simp
|
||||
| some a' => simpa using ⟨fun h => w _ _ h, fun h => congrArg f h⟩
|
||||
|
||||
@[simp] theorem map_if {f : α → β} [Decidable c] :
|
||||
(if c then some a else none).map f = if c then some (f a) else none := by
|
||||
split <;> rfl
|
||||
@@ -629,6 +638,15 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option
|
||||
· rintro ⟨h, rfl⟩
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_map (p : α → Prop) (f : α → β) (o : Option α) (H) :
|
||||
@pmap _ _ p (fun a _ => f a) o H = Option.map f o := by
|
||||
cases o <;> simp
|
||||
|
||||
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
|
||||
|
||||
/-! ### pelim -/
|
||||
|
||||
@[simp] theorem pelim_none : pelim none b f = b := rfl
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Johannes Hölzl
|
||||
-/
|
||||
prelude
|
||||
import Init.Ext
|
||||
import Init.Core
|
||||
|
||||
namespace Subtype
|
||||
|
||||
|
||||
@@ -13,11 +13,17 @@ macro "declare_bitwise_uint_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
|
||||
@[simp] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec % $bits) := rfl
|
||||
@[simp] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec >>> (b.toBitVec % $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec / b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec % 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 % $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec >>> (b.toBitVec % $bits) := rfl
|
||||
|
||||
@[simp] protected theorem toNat_and (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := by simp [toNat]
|
||||
@[simp] protected theorem toNat_or (a b : $typeName) : (a ||| b).toNat = a.toNat ||| b.toNat := by simp [toNat]
|
||||
@@ -37,3 +43,31 @@ declare_bitwise_uint_theorems UInt16 16
|
||||
declare_bitwise_uint_theorems UInt32 32
|
||||
declare_bitwise_uint_theorems UInt64 64
|
||||
declare_bitwise_uint_theorems USize System.Platform.numBits
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toUInt8 {b : Bool} :
|
||||
b.toUInt8.toBitVec = (BitVec.ofBool b).setWidth 8 := by
|
||||
cases b <;> simp [toUInt8]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toUInt16 {b : Bool} :
|
||||
b.toUInt16.toBitVec = (BitVec.ofBool b).setWidth 16 := by
|
||||
cases b <;> simp [toUInt16]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toUInt32 {b : Bool} :
|
||||
b.toUInt32.toBitVec = (BitVec.ofBool b).setWidth 32 := by
|
||||
cases b <;> simp [toUInt32]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toUInt64 {b : Bool} :
|
||||
b.toUInt64.toBitVec = (BitVec.ofBool b).setWidth 64 := by
|
||||
cases b <;> simp [toUInt64]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toUSize {b : Bool} :
|
||||
b.toUSize.toBitVec = (BitVec.ofBool b).setWidth System.Platform.numBits := by
|
||||
cases b
|
||||
· simp [toUSize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toUSize]
|
||||
|
||||
@@ -41,9 +41,9 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
rw [toNat, toBitVec_eq_of_lt h]
|
||||
|
||||
theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec ≤ b.toBitVec := .rfl
|
||||
@[int_toBitVec] theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec ≤ b.toBitVec := .rfl
|
||||
|
||||
theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec < b.toBitVec := .rfl
|
||||
@[int_toBitVec] theorem lt_def {a b : $typeName} : a < b ↔ a.toBitVec < b.toBitVec := .rfl
|
||||
|
||||
theorem le_iff_toNat_le {a b : $typeName} : a ≤ b ↔ a.toNat ≤ b.toNat := .rfl
|
||||
|
||||
@@ -74,6 +74,11 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
protected theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec ↔ a = b :=
|
||||
Iff.intro eq_of_toBitVec_eq toBitVec_eq_of_eq
|
||||
|
||||
open $typeName (eq_of_toBitVec_eq toBitVec_eq_of_eq) in
|
||||
@[int_toBitVec]
|
||||
protected theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b ↔ a.toBitVec = b.toBitVec :=
|
||||
Iff.intro toBitVec_eq_of_eq eq_of_toBitVec_eq
|
||||
|
||||
open $typeName (eq_of_toBitVec_eq) in
|
||||
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
|
||||
rcases a with ⟨⟨_⟩⟩; rcases b with ⟨⟨_⟩⟩; simp_all [val]
|
||||
@@ -82,10 +87,19 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
protected theorem val_inj {a b : $typeName} : a.val = b.val ↔ a = b :=
|
||||
Iff.intro eq_of_val_eq (congrArg val)
|
||||
|
||||
open $typeName (eq_of_toBitVec_eq) in
|
||||
protected theorem toBitVec_ne_of_ne {a b : $typeName} (h : a ≠ b) : a.toBitVec ≠ b.toBitVec :=
|
||||
fun h' => h (eq_of_toBitVec_eq h')
|
||||
|
||||
open $typeName (toBitVec_eq_of_eq) in
|
||||
protected theorem ne_of_toBitVec_ne {a b : $typeName} (h : a.toBitVec ≠ b.toBitVec) : a ≠ b :=
|
||||
fun h' => absurd (toBitVec_eq_of_eq h') h
|
||||
|
||||
open $typeName (ne_of_toBitVec_ne toBitVec_ne_of_ne) in
|
||||
@[int_toBitVec]
|
||||
protected theorem ne_iff_toBitVec_ne {a b : $typeName} : a ≠ b ↔ a.toBitVec ≠ b.toBitVec :=
|
||||
Iff.intro toBitVec_ne_of_ne ne_of_toBitVec_ne
|
||||
|
||||
open $typeName (ne_of_toBitVec_ne) in
|
||||
protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a ≠ b := by
|
||||
apply ne_of_toBitVec_ne
|
||||
@@ -159,7 +173,7 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
@[simp]
|
||||
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
|
||||
|
||||
@[simp]
|
||||
@[simp, int_toBitVec]
|
||||
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
|
||||
|
||||
@[simp]
|
||||
@@ -220,23 +234,3 @@ theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ≤ ofNa
|
||||
|
||||
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m ≤ n → m ≤ n.toNat := by
|
||||
simp [le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
|
||||
|
||||
@[deprecated UInt8.toNat_zero (since := "2024-06-23")] protected abbrev UInt8.zero_toNat := @UInt8.toNat_zero
|
||||
@[deprecated UInt8.toNat_div (since := "2024-06-23")] protected abbrev UInt8.div_toNat := @UInt8.toNat_div
|
||||
@[deprecated UInt8.toNat_mod (since := "2024-06-23")] protected abbrev UInt8.mod_toNat := @UInt8.toNat_mod
|
||||
|
||||
@[deprecated UInt16.toNat_zero (since := "2024-06-23")] protected abbrev UInt16.zero_toNat := @UInt16.toNat_zero
|
||||
@[deprecated UInt16.toNat_div (since := "2024-06-23")] protected abbrev UInt16.div_toNat := @UInt16.toNat_div
|
||||
@[deprecated UInt16.toNat_mod (since := "2024-06-23")] protected abbrev UInt16.mod_toNat := @UInt16.toNat_mod
|
||||
|
||||
@[deprecated UInt32.toNat_zero (since := "2024-06-23")] protected abbrev UInt32.zero_toNat := @UInt32.toNat_zero
|
||||
@[deprecated UInt32.toNat_div (since := "2024-06-23")] protected abbrev UInt32.div_toNat := @UInt32.toNat_div
|
||||
@[deprecated UInt32.toNat_mod (since := "2024-06-23")] protected abbrev UInt32.mod_toNat := @UInt32.toNat_mod
|
||||
|
||||
@[deprecated UInt64.toNat_zero (since := "2024-06-23")] protected abbrev UInt64.zero_toNat := @UInt64.toNat_zero
|
||||
@[deprecated UInt64.toNat_div (since := "2024-06-23")] protected abbrev UInt64.div_toNat := @UInt64.toNat_div
|
||||
@[deprecated UInt64.toNat_mod (since := "2024-06-23")] protected abbrev UInt64.mod_toNat := @UInt64.toNat_mod
|
||||
|
||||
@[deprecated USize.toNat_zero (since := "2024-06-23")] protected abbrev USize.zero_toNat := @USize.toNat_zero
|
||||
@[deprecated USize.toNat_div (since := "2024-06-23")] protected abbrev USize.div_toNat := @USize.toNat_div
|
||||
@[deprecated USize.toNat_mod (since := "2024-06-23")] protected abbrev USize.mod_toNat := @USize.toNat_mod
|
||||
|
||||
@@ -5,3 +5,7 @@ Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Vector.Basic
|
||||
import Init.Data.Vector.Lemmas
|
||||
import Init.Data.Vector.Lex
|
||||
import Init.Data.Vector.MapIdx
|
||||
import Init.Data.Vector.Count
|
||||
|
||||
551
src/Init/Data/Vector/Attach.lean
Normal file
551
src/Init/Data/Vector/Attach.lean
Normal file
@@ -0,0 +1,551 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Vector.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
|
||||
namespace Vector
|
||||
|
||||
/--
|
||||
`O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
|
||||
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
|
||||
but is defined only when all members of `l` satisfy `P`, using the proof
|
||||
to apply `f`.
|
||||
|
||||
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
|
||||
-/
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (l : Vector α n) (H : ∀ a ∈ l, P a) : Vector β n :=
|
||||
Vector.mk (l.toArray.pmap f (fun a m => H a (by simpa using m))) (by simp)
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
`Vector {x // P x} n` is the same as the input `Vector α n`.
|
||||
-/
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(xs : Vector α n) (P : α → Prop) (_ : ∀ x ∈ xs, P x) : Vector {x // P x} n := unsafeCast xs
|
||||
|
||||
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Vector α n) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Vector {x // P x} n :=
|
||||
Vector.mk (xs.toArray.attachWith P fun x h => H x (by simpa using h)) (by simp)
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new vector
|
||||
with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
@[inline] def attach (xs : Vector α n) : Vector {x // x ∈ xs} n := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem attachWith_mk {xs : Array α} {h : xs.size = n} {P : α → Prop} {H : ∀ x ∈ mk xs h, P x} :
|
||||
(mk xs h).attachWith P H = mk (xs.attachWith P (by simpa using H)) (by simpa using h) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem attach_mk {xs : Array α} {h : xs.size = n} :
|
||||
(mk xs h).attach = mk (xs.attachWith (· ∈ mk xs h) (by simp)) (by simpa using h):= by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem pmap_mk {xs : Array α} {h : xs.size = n} {P : α → Prop} {f : ∀ a, P a → β}
|
||||
{H : ∀ a ∈ mk xs h, P a} :
|
||||
(mk xs h).pmap f H = mk (xs.pmap f (by simpa using H)) (by simpa using h) := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem toArray_attachWith {l : Vector α n} {P : α → Prop} {H : ∀ x ∈ l, P x} :
|
||||
(l.attachWith P H).toArray = l.toArray.attachWith P (by simpa using H) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem toArray_attach {α : Type _} {l : Vector α n} :
|
||||
l.attach.toArray = l.toArray.attachWith (· ∈ l) (by simp) := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toArray_pmap {l : Vector α n} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l, P a} :
|
||||
(l.pmap f H).toArray = l.toArray.pmap f (fun a m => H a (by simpa using m)) := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem toList_attachWith {l : Vector α n} {P : α → Prop} {H : ∀ x ∈ l, P x} :
|
||||
(l.attachWith P H).toList = l.toList.attachWith P (by simpa using H) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem toList_attach {α : Type _} {l : Vector α n} :
|
||||
l.attach.toList = l.toList.attachWith (· ∈ l) (by simp) := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toList_pmap {l : Vector α n} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l, P a} :
|
||||
(l.pmap f H).toList = l.toList.pmap f (fun a m => H a (by simpa using m)) := by
|
||||
simp [pmap]
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (l : Vector α n) (H : ∀ a ∈ l, P a) :
|
||||
Vector β n := (l.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||||
|
||||
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||||
funext α β n p f L h'
|
||||
rcases L with ⟨L, rfl⟩
|
||||
simp only [pmap, pmapImpl, attachWith_mk, map_mk, Array.map_attachWith, eq_mk]
|
||||
apply Array.pmap_congr_left
|
||||
intro a m h₁ h₂
|
||||
congr
|
||||
|
||||
@[simp] theorem pmap_empty {P : α → Prop} (f : ∀ a, P a → β) : pmap f #v[] (by simp) = #v[] := rfl
|
||||
|
||||
@[simp] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (l : Vector α n) (h : ∀ b ∈ l.push a, P b) :
|
||||
pmap f (l.push a) h =
|
||||
(pmap f l (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem attach_empty : (#v[] : Vector α 0).attach = #v[] := rfl
|
||||
|
||||
@[simp] theorem attachWith_empty {P : α → Prop} (H : ∀ x ∈ #v[], P x) : (#v[] : Vector α 0).attachWith P H = #v[] := rfl
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_map (p : α → Prop) (f : α → β) (l : Vector α n) (H) :
|
||||
@pmap _ _ _ p (fun a _ => f a) l H = map f l := by
|
||||
cases l; simp
|
||||
|
||||
theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (l : Vector α n) {H₁ H₂}
|
||||
(h : ∀ a ∈ l, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp only [pmap_mk, eq_mk]
|
||||
apply Array.pmap_congr_left
|
||||
simpa using h
|
||||
|
||||
theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (l : Vector α n) (H) :
|
||||
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.map_pmap]
|
||||
|
||||
theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l : Vector α n) (H) :
|
||||
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun _ h => H _ (mem_map_of_mem _ h) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.pmap_map]
|
||||
|
||||
theorem attach_congr {l₁ l₂ : Vector α n} (h : l₁ = l₂) :
|
||||
l₁.attach = l₂.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {l₁ l₂ : Vector α n} (w : l₁ = l₂) {P : α → Prop} {H : ∀ x ∈ l₁, P x} :
|
||||
l₁.attachWith P H = l₂.attachWith P fun _ h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp] theorem attach_push {a : α} {l : Vector α n} :
|
||||
(l.push a).attach =
|
||||
(l.attach.map (fun ⟨x, h⟩ => ⟨x, mem_push_of_mem a h⟩)).push ⟨a, by simp⟩ := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.map_attachWith]
|
||||
|
||||
@[simp] theorem attachWith_push {a : α} {l : Vector α n} {P : α → Prop} {H : ∀ x ∈ l.push a, P x} :
|
||||
(l.push a).attachWith P H =
|
||||
(l.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push ⟨a, H a (by simp)⟩ := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l : Vector α n) (H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp only [pmap_mk, Array.pmap_eq_map_attach, attach_mk, map_mk, eq_mk]
|
||||
rw [Array.map_attach, Array.map_attachWith]
|
||||
ext i hi₁ hi₂ <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_attachWith {p q : α → Prop} (f : ∀ a, p a → q a) (l : Vector α n) (H) :
|
||||
pmap (fun a h => ⟨a, f a h⟩) l H = l.attachWith q (fun x h => f x (H x h)) := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem attach_map_coe (l : Vector α n) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
theorem attach_map_val (l : Vector α n) (f : α → β) : (l.attach.map fun i => f i.val) = l.map f :=
|
||||
attach_map_coe _ _
|
||||
|
||||
theorem attach_map_subtype_val (l : Vector α n) : l.attach.map Subtype.val = l := by
|
||||
cases l; simp
|
||||
|
||||
theorem attachWith_map_coe {p : α → Prop} (f : α → β) (l : Vector α n) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
|
||||
cases l; simp
|
||||
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (l : Vector α n) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun i => f i.val) = l.map f :=
|
||||
attachWith_map_coe _ _ _
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (l : Vector α n) (H : ∀ a ∈ l, p a) :
|
||||
(l.attachWith p H).map Subtype.val = l := by
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_attach (l : Vector α n) : ∀ x, x ∈ l.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||||
exact m
|
||||
|
||||
@[simp]
|
||||
theorem mem_attachWith (l : Vector α n) {q : α → Prop} (H) (x : {x // q x}) :
|
||||
x ∈ l.attachWith q H ↔ x.1 ∈ l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l : Vector α n} {H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
|
||||
|
||||
theorem mem_pmap_of_mem {p : α → Prop} {f : ∀ a, p a → β} {l : Vector α n} {H} {a} (h : a ∈ l) :
|
||||
f a (H a h) ∈ pmap f l H := by
|
||||
rw [mem_pmap]
|
||||
exact ⟨a, h, rfl⟩
|
||||
|
||||
theorem pmap_eq_self {l : Vector α n} {p : α → Prop} {hp : ∀ (a : α), a ∈ l → p a}
|
||||
{f : (a : α) → p a → α} : l.pmap f hp = l ↔ ∀ a (h : a ∈ l), f a (hp a h) = a := by
|
||||
cases l; simp [Array.pmap_eq_self]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Vector α n} (h : ∀ a ∈ l, p a) (i : Nat) :
|
||||
(pmap f l h)[i]? = Option.pmap f l[i]? fun x H => h x (mem_of_getElem? H) := by
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Vector α n} (h : ∀ a ∈ l, p a) {i : Nat}
|
||||
(hn : i < n) :
|
||||
(pmap f l h)[i] = f (l[i]) (h _ (by simp)) := by
|
||||
cases l; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_attachWith {xs : Vector α n} {i : Nat} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
|
||||
getElem?_pmap ..
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_attach {xs : Vector α n} {i : Nat} :
|
||||
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => mem_of_getElem? a) :=
|
||||
getElem?_attachWith
|
||||
|
||||
@[simp]
|
||||
theorem getElem_attachWith {xs : Vector α n} {P : α → Prop} {H : ∀ a ∈ xs, P a}
|
||||
{i : Nat} (h : i < n) :
|
||||
(xs.attachWith P H)[i] = ⟨xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h))⟩ :=
|
||||
getElem_pmap _ _ h
|
||||
|
||||
@[simp]
|
||||
theorem getElem_attach {xs : Vector α n} {i : Nat} (h : i < n) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
@[simp] theorem pmap_attach (l : Vector α n) {p : {x // x ∈ l} → Prop} (f : ∀ a, p a → β) (H) :
|
||||
pmap f l.attach H =
|
||||
l.pmap (P := fun a => ∃ h : a ∈ l, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem pmap_attachWith (l : Vector α n) {p : {x // q x} → Prop} (f : ∀ a, p a → β) (H₁ H₂) :
|
||||
pmap f (l.attachWith q H₁) H₂ =
|
||||
l.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
theorem foldl_pmap (l : Vector α n) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : γ → β → γ) (x : γ) :
|
||||
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
rw [pmap_eq_map_attach, foldl_map]
|
||||
|
||||
theorem foldr_pmap (l : Vector α n) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : β → γ → γ) (x : γ) :
|
||||
(l.pmap f H).foldr g x = l.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||||
rw [pmap_eq_map_attach, foldr_map]
|
||||
|
||||
/--
|
||||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||||
we get the same results as folding over `l` directly.
|
||||
|
||||
This is useful when we need to use `attach` to show termination.
|
||||
|
||||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
See however `foldl_subtype` below.
|
||||
-/
|
||||
theorem foldl_attach (l : Vector α n) (f : β → α → β) (b : β) :
|
||||
l.attach.foldl (fun acc t => f acc t.1) b = l.foldl f b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.foldl_attach]
|
||||
|
||||
/--
|
||||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||||
we get the same results as folding over `l` directly.
|
||||
|
||||
This is useful when we need to use `attach` to show termination.
|
||||
|
||||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
See however `foldr_subtype` below.
|
||||
-/
|
||||
theorem foldr_attach (l : Vector α n) (f : α → β → β) (b : β) :
|
||||
l.attach.foldr (fun t acc => f t.1 acc) b = l.foldr f b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.foldr_attach]
|
||||
|
||||
theorem attach_map {l : Vector α n} (f : α → β) :
|
||||
(l.map f).attach = l.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
cases l
|
||||
ext <;> simp
|
||||
|
||||
theorem attachWith_map {l : Vector α n} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ l.map f → P b} :
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem f h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.attachWith_map]
|
||||
|
||||
theorem map_attachWith {l : Vector α n} {P : α → Prop} {H : ∀ (a : α), a ∈ l → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(l.attachWith P H).map f =
|
||||
l.pmap (fun a (h : a ∈ l ∧ P a) => f ⟨a, H _ h.1⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
cases l
|
||||
ext <;> simp
|
||||
|
||||
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
|
||||
theorem map_attach {l : Vector α n} (f : { x // x ∈ l } → β) :
|
||||
l.attach.map f = l.pmap (fun a h => f ⟨a, h⟩) (fun _ => id) := by
|
||||
cases l
|
||||
ext <;> simp
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} (g : ∀ a, p a → β) (f : ∀ b, q b → γ) (l : Vector α n) (H₁ H₂) :
|
||||
pmap f (pmap g l H₁) H₂ =
|
||||
pmap (α := { x // x ∈ l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
|
||||
(fun a _ => H₁ a a.2) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ : Vector ι n) (l₂ : Vector ι m)
|
||||
(h : ∀ a ∈ l₁ ++ l₂, p a) :
|
||||
(l₁ ++ l₂).pmap f h =
|
||||
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
|
||||
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
simp
|
||||
|
||||
theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ : Vector α n) (l₂ : Vector α m)
|
||||
(h₁ : ∀ a ∈ l₁, p a) (h₂ : ∀ a ∈ l₂, p a) :
|
||||
((l₁ ++ l₂).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
|
||||
pmap_append f l₁ l₂ _
|
||||
|
||||
@[simp] theorem attach_append (xs : Vector α n) (ys : Vector α m) :
|
||||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => (⟨x, mem_append_left ys h⟩ : { x // x ∈ xs ++ ys })) ++
|
||||
ys.attach.map (fun ⟨y, h⟩ => (⟨y, mem_append_right xs h⟩ : { y // y ∈ xs ++ ys })) := by
|
||||
rcases xs with ⟨xs, rfl⟩
|
||||
rcases ys with ⟨ys, rfl⟩
|
||||
simp [Array.map_attachWith]
|
||||
|
||||
@[simp] theorem attachWith_append {P : α → Prop} {xs : Vector α n} {ys : Vector α m}
|
||||
{H : ∀ (a : α), a ∈ xs ++ ys → P a} :
|
||||
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
|
||||
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
|
||||
simp [attachWith, attach_append, map_pmap, pmap_append]
|
||||
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} (f : (a : α) → P a → β) (xs : Vector α n)
|
||||
(H : ∀ (a : α), a ∈ xs.reverse → P a) :
|
||||
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||||
induction xs <;> simp_all
|
||||
|
||||
theorem reverse_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Vector α n)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||||
rw [pmap_reverse]
|
||||
|
||||
@[simp] theorem attachWith_reverse {P : α → Prop} {xs : Vector α n}
|
||||
{H : ∀ (a : α), a ∈ xs.reverse → P a} :
|
||||
xs.reverse.attachWith P H =
|
||||
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
theorem reverse_attachWith {P : α → Prop} {xs : Vector α n}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem attach_reverse (xs : Vector α n) :
|
||||
xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
rw [attach_congr (reverse_mk ..)]
|
||||
simp [Array.map_attachWith]
|
||||
|
||||
theorem reverse_attach (xs : Vector α n) :
|
||||
xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
simp [Array.map_attachWith]
|
||||
|
||||
@[simp] theorem back?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Vector α n)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).back? = xs.attach.back?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem back?_attachWith {P : α → Prop} {xs : Vector α n}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back? h)⟩) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem back?_attach {xs : Vector α n} :
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back? h⟩ := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem countP_attach (l : Vector α n) (p : α → Bool) :
|
||||
l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
cases l
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem countP_attachWith {p : α → Prop} (l : Vector α n) (H : ∀ a ∈ l, p a) (q : α → Bool) :
|
||||
(l.attachWith p H).countP (fun a : {x // p x} => q a) = l.countP q := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem count_attach [DecidableEq α] (l : Vector α n) (a : {x // x ∈ l}) :
|
||||
l.attach.count a = l.count ↑a := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem count_attachWith [DecidableEq α] {p : α → Prop} (l : Vector α n) (H : ∀ a ∈ l, p a) (a : {x // p x}) :
|
||||
(l.attachWith p H).count a = l.count ↑a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pmap {p : α → Prop} (g : ∀ a, p a → β) (f : β → Bool) (l : Vector α n) (H₁) :
|
||||
(l.pmap g H₁).countP f =
|
||||
l.attach.countP (fun ⟨a, m⟩ => f (g a (H₁ a m))) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp only [pmap_mk, countP_mk, Array.countP_pmap]
|
||||
simp [Array.countP_eq_size_filter]
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`Vector.unattach` is the (one-sided) inverse of `Vector.attach`. It is a synonym for `Vector.map Subtype.val`.
|
||||
|
||||
We use it by providing a simp lemma `l.attach.unattach = l`, and simp lemmas which recognize higher order
|
||||
functions applied to `l : Vector { x // p x }` which only depend on the value, not the predicate, and rewrite these
|
||||
in terms of a simpler function applied to `l.unattach`.
|
||||
|
||||
Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as in intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
If not, usually the right approach is `simp [Vector.unattach, -Vector.map_subtype]` to unfold.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : Vector { x // p x } n) : Vector α n := l.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#v[] : Vector { x // p x } 0).unattach = #v[] := rfl
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {l : Vector { x // p x } n} :
|
||||
(l.push a).unattach = l.unattach.push a.1 := by
|
||||
simp only [unattach, Vector.map_push]
|
||||
|
||||
@[simp] theorem unattach_mk {p : α → Prop} {l : Array { x // p x }} {h : l.size = n} :
|
||||
(mk l h).unattach = mk l.unattach (by simpa using h) := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem toArray_unattach {p : α → Prop} {l : Vector { x // p x } n} :
|
||||
l.unattach.toArray = l.toArray.unattach := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem toList_unattach {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.unattach.toList = l.toList.unattach := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem unattach_attach {l : Vector α n} : l.attach.unattach = l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_attachWith {p : α → Prop} {l : Vector α n}
|
||||
{H : ∀ a ∈ l, p a} :
|
||||
(l.attachWith p H).unattach = l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_unattach {p : α → Prop} {l : Vector { x // p x } n} (i : Nat) :
|
||||
l.unattach[i]? = l[i]?.map Subtype.val := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem getElem_unattach
|
||||
{p : α → Prop} {l : Vector { x // p x } n} (i : Nat) (h : i < n) :
|
||||
l.unattach[i] = (l[i]'(by simpa using h)).1 := by
|
||||
simp [unattach]
|
||||
|
||||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||||
|
||||
/--
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldl_subtype {p : α → Prop} {l : Vector { x // p x } n}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} :
|
||||
l.foldl f x = l.unattach.foldl g x := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.foldl_subtype (hf := hf)]
|
||||
|
||||
/--
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldr_subtype {p : α → Prop} {l : Vector { x // p x } n}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} :
|
||||
l.foldr f x = l.unattach.foldr g x := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.foldr_subtype (hf := hf)]
|
||||
|
||||
/--
|
||||
This lemma identifies maps over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem map_subtype {p : α → Prop} {l : Vector { x // p x } n}
|
||||
{f : { x // p x } → β} {g : α → β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.map f = l.unattach.map g := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.map_subtype (hf := hf)]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {l : Vector { x // p x } n} :
|
||||
l.reverse.unattach = l.unattach.reverse := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.unattach_reverse]
|
||||
|
||||
|
||||
@[simp] theorem unattach_append {p : α → Prop} {l₁ l₂ : Vector { x // p x } n} :
|
||||
(l₁ ++ l₂).unattach = l₁.unattach ++ l₂.unattach := by
|
||||
rcases l₁
|
||||
rcases l₂
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_flatten {p : α → Prop} {l : Vector (Vector { x // p x } n) n} :
|
||||
l.flatten.unattach = (l.map unattach).flatten := by
|
||||
unfold unattach
|
||||
cases l using vector₂_induction
|
||||
simp only [flatten_mk, Array.map_map, Function.comp_apply, Array.map_subtype,
|
||||
Array.unattach_attach, Array.map_id_fun', id_eq, map_mk, Array.map_flatten, map_subtype,
|
||||
map_id_fun', unattach_mk, eq_mk]
|
||||
unfold Array.unattach
|
||||
rfl
|
||||
|
||||
@[simp] theorem unattach_mkVector {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(mkVector n x).unattach = mkVector n x.1 := by
|
||||
simp [unattach]
|
||||
|
||||
end Vector
|
||||
@@ -6,6 +6,7 @@ Authors: Shreyas Srinivas, François G. Dorais, Kim Morrison
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Range
|
||||
|
||||
/-!
|
||||
@@ -90,14 +91,12 @@ of bounds.
|
||||
/-- The last element of a vector. Panics if the vector is empty. -/
|
||||
@[inline] def back! [Inhabited α] (v : Vector α n) : α := v.toArray.back!
|
||||
|
||||
/-- The last element of a vector, or `none` if the array is empty. -/
|
||||
/-- The last element of a vector, or `none` if the vector is empty. -/
|
||||
@[inline] def back? (v : Vector α n) : Option α := v.toArray.back?
|
||||
|
||||
/-- The last element of a non-empty vector. -/
|
||||
@[inline] def back [NeZero n] (v : Vector α n) : α :=
|
||||
-- TODO: change to just `v[n]`
|
||||
have : Inhabited α := ⟨v[0]'(Nat.pos_of_neZero n)⟩
|
||||
v.back!
|
||||
v[n - 1]'(Nat.sub_one_lt (NeZero.ne n))
|
||||
|
||||
/-- The first element of a non-empty vector. -/
|
||||
@[inline] def head [NeZero n] (v : Vector α n) := v[0]'(Nat.pos_of_neZero n)
|
||||
@@ -170,6 +169,25 @@ result is empty. If `stop` is greater than the size of the vector, the size is u
|
||||
@[inline] def map (f : α → β) (v : Vector α n) : Vector β n :=
|
||||
⟨v.toArray.map f, by simp⟩
|
||||
|
||||
/-- Maps elements of a vector using the function `f`, which also receives the index of the element. -/
|
||||
@[inline] def mapIdx (f : Nat → α → β) (v : Vector α n) : Vector β n :=
|
||||
⟨v.toArray.mapIdx f, by simp⟩
|
||||
|
||||
/-- Maps elements of a vector using the function `f`,
|
||||
which also receives the index of the element, and the fact that the index is less than the size of the vector. -/
|
||||
@[inline] def mapFinIdx (v : Vector α n) (f : (i : Nat) → α → (h : i < n) → β) : Vector β n :=
|
||||
⟨v.toArray.mapFinIdx (fun i a h => f i a (by simpa [v.size_toArray] using h)), by simp⟩
|
||||
|
||||
@[inline] def flatten (v : Vector (Vector α n) m) : Vector α (m * n) :=
|
||||
⟨(v.toArray.map Vector.toArray).flatten,
|
||||
by rcases v; simp_all [Function.comp_def, Array.map_const']⟩
|
||||
|
||||
@[inline] def flatMap (v : Vector α n) (f : α → Vector β m) : Vector β (n * m) :=
|
||||
⟨v.toArray.flatMap fun a => (f a).toArray, by simp [Array.map_const']⟩
|
||||
|
||||
@[inline] def zipWithIndex (v : Vector α n) : Vector (α × Nat) n :=
|
||||
⟨v.toArray.zipWithIndex, by simp⟩
|
||||
|
||||
/-- Maps corresponding elements of two vectors of equal size using the function `f`. -/
|
||||
@[inline] def zipWith (a : Vector α n) (b : Vector β n) (f : α → β → φ) : Vector φ n :=
|
||||
⟨Array.zipWith a.toArray b.toArray f, by simp⟩
|
||||
@@ -294,6 +312,14 @@ no element of the index matches the given value.
|
||||
@[inline] def all (v : Vector α n) (p : α → Bool) : Bool :=
|
||||
v.toArray.all p
|
||||
|
||||
/-- Count the number of elements of a vector that satisfy the predicate `p`. -/
|
||||
@[inline] def countP (p : α → Bool) (v : Vector α n) : Nat :=
|
||||
v.toArray.countP p
|
||||
|
||||
/-- Count the number of elements of a vector that are equal to `a`. -/
|
||||
@[inline] def count [BEq α] (a : α) (v : Vector α n) : Nat :=
|
||||
v.toArray.count a
|
||||
|
||||
/-! ### Lexicographic ordering -/
|
||||
|
||||
instance instLT [LT α] : LT (Vector α n) := ⟨fun v w => v.toArray < w.toArray⟩
|
||||
|
||||
233
src/Init/Data/Vector/Count.lean
Normal file
233
src/Init/Data/Vector/Count.lean
Normal file
@@ -0,0 +1,233 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Count
|
||||
import Init.Data.Vector.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `Vector.countP` and `Vector.count`.
|
||||
-/
|
||||
|
||||
namespace Vector
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### countP -/
|
||||
section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem countP_empty : countP p #v[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos (l : Vector α n) (pa : p a) : countP p (l.push a) = countP p l + 1 := by
|
||||
rcases l with ⟨l⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_push_of_neg (l : Vector α n) (pa : ¬p a) : countP p (l.push a) = countP p l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp_all
|
||||
|
||||
theorem countP_push (a : α) (l : Vector α n) : countP p (l.push a) = countP p l + if p a then 1 else 0 := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.countP_push]
|
||||
|
||||
@[simp] theorem countP_singleton (a : α) : countP p #v[a] = if p a then 1 else 0 := by
|
||||
simp [countP_push]
|
||||
|
||||
theorem size_eq_countP_add_countP (l : Vector α n) : n = countP p l + countP (fun a => ¬p a) l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [List.length_eq_countP_add_countP (p := p)]
|
||||
|
||||
theorem countP_le_size {l : Vector α n} : countP p l ≤ n := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.countP_le_size (p := p)]
|
||||
|
||||
@[simp] theorem countP_append (l₁ : Vector α n) (l₂ : Vector α m) : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pos_iff {p} : 0 < countP p l ↔ ∃ a ∈ l, p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem one_le_countP_iff {p} : 1 ≤ countP p l ↔ ∃ a ∈ l, p a :=
|
||||
countP_pos_iff
|
||||
|
||||
@[simp] theorem countP_eq_zero {p} : countP p l = 0 ↔ ∀ a ∈ l, ¬p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_eq_size {p} : countP p l = l.size ↔ ∀ a ∈ l, p a := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_cast (p : α → Bool) (l : Vector α n) : countP p (l.cast h) = countP p l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
theorem countP_mkVector (p : α → Bool) (a : α) (n : Nat) :
|
||||
countP p (mkVector n a) = if p a then n else 0 := by
|
||||
simp only [mkVector_eq_toVector_mkArray, countP_cast, countP_mk]
|
||||
simp [Array.countP_mkArray]
|
||||
|
||||
theorem boole_getElem_le_countP (p : α → Bool) (l : Vector α n) (i : Nat) (h : i < n) :
|
||||
(if p l[i] then 1 else 0) ≤ l.countP p := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.boole_getElem_le_countP]
|
||||
|
||||
theorem countP_set (p : α → Bool) (l : Vector α n) (i : Nat) (a : α) (h : i < n) :
|
||||
(l.set i a).countP p = l.countP p - (if p l[i] then 1 else 0) + (if p a then 1 else 0) := by
|
||||
cases l
|
||||
simp [Array.countP_set, h]
|
||||
|
||||
@[simp] theorem countP_true : (countP fun (_ : α) => true) = (fun (_ : Vector α n) => n) := by
|
||||
funext l
|
||||
rw [countP]
|
||||
simp only [Array.countP_true, l.2]
|
||||
|
||||
@[simp] theorem countP_false : (countP fun (_ : α) => false) = (fun (_ : Vector α n) => 0) := by
|
||||
funext l
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_map (p : β → Bool) (f : α → β) (l : Vector α n) :
|
||||
countP p (map f l) = countP (p ∘ f) l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_flatten (l : Vector (Vector α m) n) :
|
||||
countP p l.flatten = (l.map (countP p)).sum := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Function.comp_def]
|
||||
|
||||
theorem countP_flatMap (p : β → Bool) (l : Vector α n) (f : α → Vector β m) :
|
||||
countP p (l.flatMap f) = (map (countP p ∘ f) l).sum := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.countP_flatMap, Function.comp_def]
|
||||
|
||||
@[simp] theorem countP_reverse (l : Vector α n) : countP p l.reverse = countP p l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
variable {p q}
|
||||
|
||||
theorem countP_mono_left (h : ∀ x ∈ l, p x → q x) : countP p l ≤ countP q l := by
|
||||
cases l
|
||||
simpa using Array.countP_mono_left (by simpa using h)
|
||||
|
||||
theorem countP_congr (h : ∀ x ∈ l, p x ↔ q x) : countP p l = countP q l :=
|
||||
Nat.le_antisymm
|
||||
(countP_mono_left fun x hx => (h x hx).1)
|
||||
(countP_mono_left fun x hx => (h x hx).2)
|
||||
|
||||
end countP
|
||||
|
||||
/-! ### count -/
|
||||
section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem count_empty (a : α) : count a #v[] = 0 := rfl
|
||||
|
||||
theorem count_push (a b : α) (l : Vector α n) :
|
||||
count a (l.push b) = count a l + if b == a then 1 else 0 := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_push]
|
||||
|
||||
theorem count_eq_countP (a : α) (l : Vector α n) : count a l = countP (· == a) l := rfl
|
||||
|
||||
theorem count_eq_countP' {a : α} : count (n := n) a = countP (· == a) := by
|
||||
funext l
|
||||
apply count_eq_countP
|
||||
|
||||
theorem count_le_size (a : α) (l : Vector α n) : count a l ≤ n := countP_le_size _
|
||||
|
||||
theorem count_le_count_push (a b : α) (l : Vector α n) : count a l ≤ count a (l.push b) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_push]
|
||||
|
||||
@[simp] theorem count_singleton (a b : α) : count a #v[b] = if b == a then 1 else 0 := by
|
||||
simp [count_eq_countP]
|
||||
|
||||
@[simp] theorem count_append (a : α) (l₁ : Vector α n) (l₂ : Vector α m) :
|
||||
count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
|
||||
countP_append ..
|
||||
|
||||
@[simp] theorem count_flatten (a : α) (l : Vector (Vector α m) n) :
|
||||
count a l.flatten = (l.map (count a)).sum := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_flatten, Function.comp_def]
|
||||
|
||||
@[simp] theorem count_reverse (a : α) (l : Vector α n) : count a l.reverse = count a l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
theorem boole_getElem_le_count (a : α) (l : Vector α n) (i : Nat) (h : i < n) :
|
||||
(if l[i] == a then 1 else 0) ≤ l.count a := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.boole_getElem_le_count, h]
|
||||
|
||||
theorem count_set (a b : α) (l : Vector α n) (i : Nat) (h : i < n) :
|
||||
(l.set i a).count b = l.count b - (if l[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_set, h]
|
||||
|
||||
@[simp] theorem count_cast (l : Vector α n) : (l.cast h).count a = l.count a := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem count_push_self (a : α) (l : Vector α n) : count a (l.push a) = count a l + 1 := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_push_self]
|
||||
|
||||
@[simp] theorem count_push_of_ne (h : b ≠ a) (l : Vector α n) : count a (l.push b) = count a l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_push_of_ne, h]
|
||||
|
||||
theorem count_singleton_self (a : α) : count a #v[a] = 1 := by simp
|
||||
|
||||
@[simp]
|
||||
theorem count_pos_iff {a : α} {l : Vector α n} : 0 < count a l ↔ a ∈ l := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_pos_iff, beq_iff_eq, exists_eq_right]
|
||||
|
||||
@[simp] theorem one_le_count_iff {a : α} {l : Vector α n} : 1 ≤ count a l ↔ a ∈ l :=
|
||||
count_pos_iff
|
||||
|
||||
theorem count_eq_zero_of_not_mem {a : α} {l : Vector α n} (h : a ∉ l) : count a l = 0 :=
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff.1 (Nat.pos_of_ne_zero h')
|
||||
|
||||
theorem not_mem_of_count_eq_zero {a : α} {l : Vector α n} (h : count a l = 0) : a ∉ l :=
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff.2 h') h.symm
|
||||
|
||||
theorem count_eq_zero {l : Vector α n} : count a l = 0 ↔ a ∉ l :=
|
||||
⟨not_mem_of_count_eq_zero, count_eq_zero_of_not_mem⟩
|
||||
|
||||
theorem count_eq_size {l : Vector α n} : count a l = l.size ↔ ∀ b ∈ l, a = b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_eq_size]
|
||||
|
||||
@[simp] theorem count_mkVector_self (a : α) (n : Nat) : count a (mkVector n a) = n := by
|
||||
simp only [mkVector_eq_toVector_mkArray, count_cast, count_mk]
|
||||
simp
|
||||
|
||||
theorem count_mkVector (a b : α) (n : Nat) : count a (mkVector n b) = if b == a then n else 0 := by
|
||||
simp only [mkVector_eq_toVector_mkArray, count_cast, count_mk]
|
||||
simp [Array.count_mkArray]
|
||||
|
||||
theorem count_le_count_map [DecidableEq β] (l : Vector α n) (f : α → β) (x : α) :
|
||||
count x l ≤ count (f x) (map f l) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_le_count_map]
|
||||
|
||||
theorem count_flatMap {α} [BEq β] (l : Vector α n) (f : α → Vector β m) (x : β) :
|
||||
count x (l.flatMap f) = (map (count x ∘ f) l).sum := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.count_flatMap, Function.comp_def]
|
||||
|
||||
end count
|
||||
File diff suppressed because it is too large
Load Diff
333
src/Init/Data/Vector/MapIdx.lean
Normal file
333
src/Init/Data/Vector/MapIdx.lean
Normal file
@@ -0,0 +1,333 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Vector.Lemmas
|
||||
|
||||
namespace Vector
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
@[simp] theorem getElem_mapFinIdx (a : Vector α n) (f : (i : Nat) → α → (h : i < n) → β) (i : Nat)
|
||||
(h : i < n) :
|
||||
(a.mapFinIdx f)[i] = f i a[i] h := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx (a : Vector α n) (f : (i : Nat) → α → (h : i < n) → β) (i : Nat) :
|
||||
(a.mapFinIdx f)[i]? =
|
||||
a[i]?.pbind fun b h => f i b (getElem?_eq_some_iff.1 h).1 := by
|
||||
simp only [getElem?_def, getElem_mapFinIdx]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
@[simp] theorem getElem_mapIdx (f : Nat → α → β) (a : Vector α n) (i : Nat) (h : i < n) :
|
||||
(a.mapIdx f)[i] = f i (a[i]'(by simp_all)) := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_mapIdx (f : Nat → α → β) (a : Vector α n) (i : Nat) :
|
||||
(a.mapIdx f)[i]? = a[i]?.map (f i) := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
end Vector
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem mapFinIdx_toVector (l : Array α) (f : (i : Nat) → α → (h : i < l.size) → β) :
|
||||
l.toVector.mapFinIdx f = (l.mapFinIdx f).toVector.cast (by simp) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem mapIdx_toVector (f : Nat → α → β) (l : Array α) :
|
||||
l.toVector.mapIdx f = (l.mapIdx f).toVector.cast (by simp) := by
|
||||
ext <;> simp
|
||||
|
||||
end Array
|
||||
|
||||
namespace Vector
|
||||
|
||||
/-! ### zipWithIndex -/
|
||||
|
||||
@[simp] theorem toList_zipWithIndex (a : Vector α n) :
|
||||
a.zipWithIndex.toList = a.toList.enum.map (fun (i, a) => (a, i)) := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem_zipWithIndex (a : Vector α n) (i : Nat) (h : i < n) :
|
||||
(a.zipWithIndex)[i] = (a[i]'(by simp_all), i) := by
|
||||
rcases a with ⟨a, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem zipWithIndex_toVector {l : Array α} :
|
||||
l.toVector.zipWithIndex = l.zipWithIndex.toVector.cast (by simp) := by
|
||||
ext <;> simp
|
||||
|
||||
theorem mk_mem_zipWithIndex_iff_getElem? {x : α} {i : Nat} {l : Vector α n} :
|
||||
(x, i) ∈ l.zipWithIndex ↔ l[i]? = x := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.mk_mem_zipWithIndex_iff_getElem?]
|
||||
|
||||
theorem mem_enum_iff_getElem? {x : α × Nat} {l : Vector α n} :
|
||||
x ∈ l.zipWithIndex ↔ l[x.2]? = some x.1 :=
|
||||
mk_mem_zipWithIndex_iff_getElem?
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
@[congr] theorem mapFinIdx_congr {xs ys : Vector α n} (w : xs = ys)
|
||||
(f : (i : Nat) → α → (h : i < n) → β) :
|
||||
mapFinIdx xs f = mapFinIdx ys f := by
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_empty {f : (i : Nat) → α → (h : i < 0) → β} : mapFinIdx #v[] f = #v[] :=
|
||||
rfl
|
||||
|
||||
theorem mapFinIdx_eq_ofFn {as : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} :
|
||||
as.mapFinIdx f = Vector.ofFn fun i : Fin n => f i as[i] i.2 := by
|
||||
rcases as with ⟨as, rfl⟩
|
||||
simp [Array.mapFinIdx_eq_ofFn]
|
||||
|
||||
theorem mapFinIdx_append {K : Vector α n} {L : Vector α m} {f : (i : Nat) → α → (h : i < n + m) → β} :
|
||||
(K ++ L).mapFinIdx f =
|
||||
K.mapFinIdx (fun i a h => f i a (by omega)) ++
|
||||
L.mapFinIdx (fun i a h => f (i + n) a (by omega)) := by
|
||||
rcases K with ⟨K, rfl⟩
|
||||
rcases L with ⟨L, rfl⟩
|
||||
simp [Array.mapFinIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_push {l : Vector α n} {a : α} {f : (i : Nat) → α → (h : i < n + 1) → β} :
|
||||
mapFinIdx (l.push a) f =
|
||||
(mapFinIdx l (fun i a h => f i a (by omega))).push (f l.size a (by simp)) := by
|
||||
simp [← append_singleton, mapFinIdx_append]
|
||||
|
||||
theorem mapFinIdx_singleton {a : α} {f : (i : Nat) → α → (h : i < 1) → β} :
|
||||
#v[a].mapFinIdx f = #v[f 0 a (by simp)] := by
|
||||
simp
|
||||
|
||||
-- FIXME this lemma can't be stated until we've aligned `List/Array/Vector.attach`:
|
||||
-- theorem mapFinIdx_eq_zipWithIndex_map {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} :
|
||||
-- l.mapFinIdx f = l.zipWithIndex.attach.map
|
||||
-- fun ⟨⟨x, i⟩, m⟩ =>
|
||||
-- f i x (by simp [mk_mem_zipWithIndex_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
|
||||
-- ext <;> simp
|
||||
|
||||
theorem exists_of_mem_mapFinIdx {b : β} {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β}
|
||||
(h : b ∈ l.mapFinIdx f) : ∃ (i : Nat) (h : i < n), f i l[i] h = b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
exact List.exists_of_mem_mapFinIdx (by simpa using h)
|
||||
|
||||
@[simp] theorem mem_mapFinIdx {b : β} {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} :
|
||||
b ∈ l.mapFinIdx f ↔ ∃ (i : Nat) (h : i < n), f i l[i] h = b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_iff {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} :
|
||||
l.mapFinIdx f = l' ↔ ∀ (i : Nat) (h : i < n), l'[i] = f i l[i] h := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
rcases l' with ⟨l', h⟩
|
||||
simp [mapFinIdx_mk, eq_mk, getElem_mk, Array.mapFinIdx_eq_iff, h]
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_singleton_iff {l : Vector α 1} {f : (i : Nat) → α → (h : i < 1) → β} {b : β} :
|
||||
l.mapFinIdx f = #v[b] ↔ ∃ (a : α), l = #v[a] ∧ f 0 a (by omega) = b := by
|
||||
rcases l with ⟨l, h⟩
|
||||
simp only [mapFinIdx_mk, eq_mk, Array.mapFinIdx_eq_singleton_iff]
|
||||
constructor
|
||||
· rintro ⟨a, rfl, rfl⟩
|
||||
exact ⟨a, by simp⟩
|
||||
· rintro ⟨a, rfl, rfl⟩
|
||||
exact ⟨a, by simp⟩
|
||||
|
||||
theorem mapFinIdx_eq_append_iff {l : Vector α (n + m)} {f : (i : Nat) → α → (h : i < n + m) → β}
|
||||
{l₁ : Vector β n} {l₂ : Vector β m} :
|
||||
l.mapFinIdx f = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : Vector α n) (l₂' : Vector α m), l = l₁' ++ l₂' ∧
|
||||
l₁'.mapFinIdx (fun i a h => f i a (by omega)) = l₁ ∧
|
||||
l₂'.mapFinIdx (fun i a h => f (i + n) a (by omega)) = l₂ := by
|
||||
rcases l with ⟨l, h⟩
|
||||
rcases l₁ with ⟨l₁, rfl⟩
|
||||
rcases l₂ with ⟨l₂, rfl⟩
|
||||
simp only [mapFinIdx_mk, mk_append_mk, eq_mk, Array.mapFinIdx_eq_append_iff, toArray_mapFinIdx,
|
||||
mk_eq, toArray_append, exists_and_left, exists_prop]
|
||||
constructor
|
||||
· rintro ⟨l₁', l₂', rfl, h₁, h₂⟩
|
||||
have h₁' := congrArg Array.size h₁
|
||||
have h₂' := congrArg Array.size h₂
|
||||
simp only [Array.size_mapFinIdx] at h₁' h₂'
|
||||
exact ⟨⟨l₁', h₁'⟩, ⟨l₂', h₂'⟩, by simp_all⟩
|
||||
· rintro ⟨⟨l₁, s₁⟩, ⟨l₂, s₂⟩, rfl, h₁, h₂⟩
|
||||
refine ⟨l₁, l₂, by simp_all⟩
|
||||
|
||||
theorem mapFinIdx_eq_push_iff {l : Vector α (n + 1)} {b : β} {f : (i : Nat) → α → (h : i < n + 1) → β} {l₂ : Vector β n} :
|
||||
l.mapFinIdx f = l₂.push b ↔
|
||||
∃ (l₁ : Vector α n) (a : α), l = l₁.push a ∧
|
||||
l₁.mapFinIdx (fun i a h => f i a (by omega)) = l₂ ∧ b = f n a (by omega) := by
|
||||
rcases l with ⟨l, h⟩
|
||||
rcases l₂ with ⟨l₂, rfl⟩
|
||||
simp only [mapFinIdx_mk, push_mk, eq_mk, Array.mapFinIdx_eq_push_iff, mk_eq, toArray_push,
|
||||
toArray_mapFinIdx]
|
||||
constructor
|
||||
· rintro ⟨l₁, a, rfl, h₁, rfl⟩
|
||||
simp only [Array.size_push, Nat.add_right_cancel_iff] at h
|
||||
exact ⟨⟨l₁, h⟩, a, by simp_all⟩
|
||||
· rintro ⟨⟨l₁, h⟩, a, rfl, h₁, rfl⟩
|
||||
exact ⟨l₁, a, by simp_all⟩
|
||||
|
||||
theorem mapFinIdx_eq_mapFinIdx_iff {l : Vector α n} {f g : (i : Nat) → α → (h : i < n) → β} :
|
||||
l.mapFinIdx f = l.mapFinIdx g ↔ ∀ (i : Nat) (h : i < n), f i l[i] h = g i l[i] h := by
|
||||
rw [eq_comm, mapFinIdx_eq_iff]
|
||||
simp
|
||||
|
||||
@[simp] theorem mapFinIdx_mapFinIdx {l : Vector α n}
|
||||
{f : (i : Nat) → α → (h : i < n) → β}
|
||||
{g : (i : Nat) → β → (h : i < n) → γ} :
|
||||
(l.mapFinIdx f).mapFinIdx g = l.mapFinIdx (fun i a h => g i (f i a h) h) := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapFinIdx_eq_mkVector_iff {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} {b : β} :
|
||||
l.mapFinIdx f = mkVector n b ↔ ∀ (i : Nat) (h : i < n), f i l[i] h = b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.mapFinIdx_eq_mkArray_iff]
|
||||
|
||||
@[simp] theorem mapFinIdx_reverse {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} :
|
||||
l.reverse.mapFinIdx f = (l.mapFinIdx (fun i a h => f (n - 1 - i) a (by omega))).reverse := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_empty {f : Nat → α → β} : mapIdx f #v[] = #v[] :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_mapIdx {l : Vector α n} {f : (i : Nat) → α → (h : i < n) → β} {g : Nat → α → β}
|
||||
(h : ∀ (i : Nat) (h : i < n), f i l[i] h = g i l[i]) :
|
||||
l.mapFinIdx f = l.mapIdx g := by
|
||||
simp_all [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapFinIdx {l : Vector α n} {f : Nat → α → β} :
|
||||
l.mapIdx f = l.mapFinIdx (fun i a _ => f i a) := by
|
||||
simp [mapFinIdx_eq_mapIdx]
|
||||
|
||||
theorem mapIdx_eq_zipWithIndex_map {l : Vector α n} {f : Nat → α → β} :
|
||||
l.mapIdx f = l.zipWithIndex.map fun ⟨a, i⟩ => f i a := by
|
||||
ext <;> simp
|
||||
|
||||
theorem mapIdx_append {K : Vector α n} {L : Vector α m} :
|
||||
(K ++ L).mapIdx f = K.mapIdx f ++ L.mapIdx fun i => f (i + K.size) := by
|
||||
rcases K with ⟨K, rfl⟩
|
||||
rcases L with ⟨L, rfl⟩
|
||||
simp [Array.mapIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_push {l : Vector α n} {a : α} :
|
||||
mapIdx f (l.push a) = (mapIdx f l).push (f l.size a) := by
|
||||
simp [← append_singleton, mapIdx_append]
|
||||
|
||||
theorem mapIdx_singleton {a : α} : mapIdx f #v[a] = #v[f 0 a] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapIdx {b : β} {l : Vector α n}
|
||||
(h : b ∈ l.mapIdx f) : ∃ (i : Nat) (h : i < n), f i l[i] = b := by
|
||||
rw [mapIdx_eq_mapFinIdx] at h
|
||||
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
|
||||
|
||||
@[simp] theorem mem_mapIdx {b : β} {l : Vector α n} :
|
||||
b ∈ l.mapIdx f ↔ ∃ (i : Nat) (h : i < n), f i l[i] = b := by
|
||||
constructor
|
||||
· intro h
|
||||
exact exists_of_mem_mapIdx h
|
||||
· rintro ⟨i, h, rfl⟩
|
||||
rw [mem_iff_getElem]
|
||||
exact ⟨i, by simpa using h, by simp⟩
|
||||
|
||||
theorem mapIdx_eq_push_iff {l : Vector α (n + 1)} {b : β} :
|
||||
mapIdx f l = l₂.push b ↔
|
||||
∃ (a : α) (l₁ : Vector α n), l = l₁.push a ∧ mapIdx f l₁ = l₂ ∧ f l₁.size a = b := by
|
||||
rw [mapIdx_eq_mapFinIdx, mapFinIdx_eq_push_iff]
|
||||
simp only [mapFinIdx_eq_mapIdx, exists_and_left, exists_prop]
|
||||
constructor
|
||||
· rintro ⟨l₁, a, rfl, rfl, rfl⟩
|
||||
exact ⟨a, l₁, by simp⟩
|
||||
· rintro ⟨a, l₁, rfl, rfl, rfl⟩
|
||||
exact ⟨l₁, a, rfl, by simp⟩
|
||||
|
||||
@[simp] theorem mapIdx_eq_singleton_iff {l : Vector α 1} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f l = #v[b] ↔ ∃ (a : α), l = #v[a] ∧ f 0 a = b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp
|
||||
|
||||
theorem mapIdx_eq_append_iff {l : Vector α (n + m)} {f : Nat → α → β} {l₁ : Vector β n} {l₂ : Vector β m} :
|
||||
mapIdx f l = l₁ ++ l₂ ↔
|
||||
∃ (l₁' : Vector α n) (l₂' : Vector α m), l = l₁' ++ l₂' ∧
|
||||
l₁'.mapIdx f = l₁ ∧
|
||||
l₂'.mapIdx (fun i => f (i + l₁'.size)) = l₂ := by
|
||||
rcases l with ⟨l, h⟩
|
||||
rcases l₁ with ⟨l₁, rfl⟩
|
||||
rcases l₂ with ⟨l₂, rfl⟩
|
||||
rw [mapIdx_eq_mapFinIdx, mapFinIdx_eq_append_iff]
|
||||
simp
|
||||
|
||||
theorem mapIdx_eq_iff {l : Vector α n} :
|
||||
mapIdx f l = l' ↔ ∀ (i : Nat) (h : i < n), f i l[i] = l'[i] := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
rcases l' with ⟨l', h⟩
|
||||
simp only [mapIdx_mk, eq_mk, Array.mapIdx_eq_iff, getElem_mk]
|
||||
constructor
|
||||
· rintro h' i h
|
||||
specialize h' i
|
||||
simp_all
|
||||
· intro h' i
|
||||
specialize h' i
|
||||
by_cases w : i < l.size
|
||||
· specialize h' w
|
||||
simp_all
|
||||
· simp only [Nat.not_lt] at w
|
||||
simp_all [Array.getElem?_eq_none_iff.mpr w]
|
||||
|
||||
theorem mapIdx_eq_mapIdx_iff {l : Vector α n} :
|
||||
mapIdx f l = mapIdx g l ↔ ∀ (i : Nat) (h : i < n), f i l[i] = g i l[i] := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.mapIdx_eq_mapIdx_iff]
|
||||
|
||||
@[simp] theorem mapIdx_set {l : Vector α n} {i : Nat} {h : i < n} {a : α} :
|
||||
(l.set i a).mapIdx f = (l.mapIdx f).set i (f i a) (by simpa) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem mapIdx_setIfInBounds {l : Vector α n} {i : Nat} {a : α} :
|
||||
(l.setIfInBounds i a).mapIdx f = (l.mapIdx f).setIfInBounds i (f i a) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem back?_mapIdx {l : Vector α n} {f : Nat → α → β} :
|
||||
(mapIdx f l).back? = (l.back?).map (f (l.size - 1)) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem back_mapIdx [NeZero n] {l : Vector α n} {f : Nat → α → β} :
|
||||
(mapIdx f l).back = f (l.size - 1) (l.back) := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem mapIdx_mapIdx {l : Vector α n} {f : Nat → α → β} {g : Nat → β → γ} :
|
||||
(l.mapIdx f).mapIdx g = l.mapIdx (fun i => g i ∘ f i) := by
|
||||
simp [mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mkVector_iff {l : Vector α n} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f l = mkVector n b ↔ ∀ (i : Nat) (h : i < n), f i l[i] = b := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.mapIdx_eq_mkArray_iff]
|
||||
|
||||
@[simp] theorem mapIdx_reverse {l : Vector α n} {f : Nat → α → β} :
|
||||
l.reverse.mapIdx f = (mapIdx (fun i => f (l.size - 1 - i)) l).reverse := by
|
||||
rcases l with ⟨l, rfl⟩
|
||||
simp [Array.mapIdx_reverse]
|
||||
|
||||
end Vector
|
||||
@@ -11,3 +11,4 @@ import Init.Grind.Cases
|
||||
import Init.Grind.Propagator
|
||||
import Init.Grind.Util
|
||||
import Init.Grind.Offset
|
||||
import Init.Grind.PP
|
||||
|
||||
@@ -5,11 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
import Init.Grind.Tactics
|
||||
|
||||
attribute [grind_cases] And Prod False Empty True Unit Exists
|
||||
|
||||
namespace Lean.Grind.Eager
|
||||
|
||||
attribute [scoped grind_cases] Or
|
||||
|
||||
end Lean.Grind.Eager
|
||||
attribute [grind cases eager] And Prod False Empty True Unit Exists
|
||||
attribute [grind cases] Or
|
||||
|
||||
@@ -12,6 +12,9 @@ import Init.Grind.Util
|
||||
|
||||
namespace Lean.Grind
|
||||
|
||||
theorem rfl_true : true = true :=
|
||||
rfl
|
||||
|
||||
theorem intro_with_eq (p p' q : Prop) (he : p = p') (h : p' → q) : p → q :=
|
||||
fun hp => h (he.mp hp)
|
||||
|
||||
@@ -66,6 +69,12 @@ 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₂)]
|
||||
|
||||
/- The following two helper theorems are used to case-split `a = b` representing `iff`. -/
|
||||
theorem of_eq_eq_true {a b : Prop} (h : (a = b) = True) : (¬a ∨ b) ∧ (¬b ∨ a) := by
|
||||
by_cases a <;> by_cases b <;> simp_all
|
||||
theorem of_eq_eq_false {a b : Prop} (h : (a = b) = False) : (¬a ∨ ¬b) ∧ (b ∨ a) := by
|
||||
by_cases a <;> by_cases b <;> simp_all
|
||||
|
||||
/-! Forall -/
|
||||
|
||||
theorem forall_propagator (p : Prop) (q : p → Prop) (q' : Prop) (h₁ : p = True) (h₂ : q (of_eq_true h₁) = q') : (∀ hp : p, q hp) = q' := by
|
||||
|
||||
@@ -12,110 +12,105 @@ import Init.ByCases
|
||||
namespace Lean.Grind
|
||||
/-!
|
||||
Normalization theorems for the `grind` tactic.
|
||||
|
||||
We are also going to use simproc's in the future.
|
||||
-/
|
||||
|
||||
-- Not
|
||||
attribute [grind_norm] Classical.not_not
|
||||
|
||||
-- Ne
|
||||
attribute [grind_norm] ne_eq
|
||||
|
||||
-- Iff
|
||||
@[grind_norm] theorem iff_eq (p q : Prop) : (p ↔ q) = (p = q) := by
|
||||
theorem iff_eq (p q : Prop) : (p ↔ q) = (p = q) := by
|
||||
by_cases p <;> by_cases q <;> simp [*]
|
||||
|
||||
-- Eq
|
||||
attribute [grind_norm] eq_self heq_eq_eq
|
||||
|
||||
-- Prop equality
|
||||
@[grind_norm] theorem eq_true_eq (p : Prop) : (p = True) = p := by simp
|
||||
@[grind_norm] theorem eq_false_eq (p : Prop) : (p = False) = ¬p := by simp
|
||||
@[grind_norm] theorem not_eq_prop (p q : Prop) : (¬(p = q)) = (p = ¬q) := by
|
||||
theorem eq_true_eq (p : Prop) : (p = True) = p := by simp
|
||||
theorem eq_false_eq (p : Prop) : (p = False) = ¬p := by simp
|
||||
theorem not_eq_prop (p q : Prop) : (¬(p = q)) = (p = ¬q) := by
|
||||
by_cases p <;> by_cases q <;> simp [*]
|
||||
|
||||
-- True
|
||||
attribute [grind_norm] not_true
|
||||
|
||||
-- False
|
||||
attribute [grind_norm] not_false_eq_true
|
||||
|
||||
-- Remark: we disabled the following normalization rule because we want this information when implementing splitting heuristics
|
||||
-- Implication as a clause
|
||||
theorem imp_eq (p q : Prop) : (p → q) = (¬ p ∨ q) := by
|
||||
by_cases p <;> by_cases q <;> simp [*]
|
||||
|
||||
-- And
|
||||
@[grind_norm↓] theorem not_and (p q : Prop) : (¬(p ∧ q)) = (¬p ∨ ¬q) := by
|
||||
theorem true_imp_eq (p : Prop) : (True → p) = p := by simp
|
||||
theorem false_imp_eq (p : Prop) : (False → p) = True := by simp
|
||||
theorem imp_true_eq (p : Prop) : (p → True) = True := by simp
|
||||
theorem imp_false_eq (p : Prop) : (p → False) = ¬p := by simp
|
||||
theorem imp_self_eq (p : Prop) : (p → p) = True := by simp
|
||||
|
||||
theorem not_and (p q : Prop) : (¬(p ∧ q)) = (¬p ∨ ¬q) := by
|
||||
by_cases p <;> by_cases q <;> simp [*]
|
||||
attribute [grind_norm] and_true true_and and_false false_and and_assoc
|
||||
|
||||
-- Or
|
||||
attribute [grind_norm↓] not_or
|
||||
attribute [grind_norm] or_true true_or or_false false_or or_assoc
|
||||
|
||||
-- ite
|
||||
attribute [grind_norm] ite_true ite_false
|
||||
@[grind_norm↓] theorem not_ite {_ : Decidable p} (q r : Prop) : (¬ite p q r) = ite p (¬q) (¬r) := by
|
||||
theorem not_ite {_ : Decidable p} (q r : Prop) : (¬ite p q r) = ite p (¬q) (¬r) := by
|
||||
by_cases p <;> simp [*]
|
||||
|
||||
@[grind_norm] theorem ite_true_false {_ : Decidable p} : (ite p True False) = p := by
|
||||
theorem ite_true_false {_ : Decidable p} : (ite p True False) = p := by
|
||||
by_cases p <;> simp
|
||||
|
||||
@[grind_norm] theorem ite_false_true {_ : Decidable p} : (ite p False True) = ¬p := by
|
||||
theorem ite_false_true {_ : Decidable p} : (ite p False True) = ¬p := by
|
||||
by_cases p <;> simp
|
||||
|
||||
-- Forall
|
||||
@[grind_norm↓] theorem not_forall (p : α → Prop) : (¬∀ x, p x) = ∃ x, ¬p x := by simp
|
||||
attribute [grind_norm] forall_and
|
||||
theorem not_forall (p : α → Prop) : (¬∀ x, p x) = ∃ x, ¬p x := by simp
|
||||
|
||||
-- Exists
|
||||
@[grind_norm↓] theorem not_exists (p : α → Prop) : (¬∃ x, p x) = ∀ x, ¬p x := by simp
|
||||
attribute [grind_norm] exists_const exists_or exists_prop exists_and_left exists_and_right
|
||||
theorem not_exists (p : α → Prop) : (¬∃ x, p x) = ∀ x, ¬p x := by simp
|
||||
|
||||
-- Bool cond
|
||||
@[grind_norm] theorem cond_eq_ite (c : Bool) (a b : α) : cond c a b = ite c a b := by
|
||||
theorem cond_eq_ite (c : Bool) (a b : α) : cond c a b = ite c a b := by
|
||||
cases c <;> simp [*]
|
||||
|
||||
-- Bool or
|
||||
attribute [grind_norm]
|
||||
Bool.or_false Bool.or_true Bool.false_or Bool.true_or Bool.or_eq_true Bool.or_assoc
|
||||
|
||||
-- Bool and
|
||||
attribute [grind_norm]
|
||||
Bool.and_false Bool.and_true Bool.false_and Bool.true_and Bool.and_eq_true Bool.and_assoc
|
||||
|
||||
-- Bool not
|
||||
attribute [grind_norm]
|
||||
Bool.not_not
|
||||
|
||||
-- beq
|
||||
attribute [grind_norm] beq_iff_eq
|
||||
|
||||
-- bne
|
||||
attribute [grind_norm] bne_iff_ne
|
||||
|
||||
-- Bool not eq true/false
|
||||
attribute [grind_norm] Bool.not_eq_true Bool.not_eq_false
|
||||
|
||||
-- decide
|
||||
attribute [grind_norm] decide_eq_true_eq decide_not not_decide_eq_true
|
||||
|
||||
-- Nat LE
|
||||
attribute [grind_norm] Nat.le_zero_eq
|
||||
|
||||
-- Nat/Int LT
|
||||
@[grind_norm] theorem Nat.lt_eq (a b : Nat) : (a < b) = (a + 1 ≤ b) := by
|
||||
theorem Nat.lt_eq (a b : Nat) : (a < b) = (a + 1 ≤ b) := by
|
||||
simp [Nat.lt, LT.lt]
|
||||
|
||||
@[grind_norm] theorem Int.lt_eq (a b : Int) : (a < b) = (a + 1 ≤ b) := by
|
||||
theorem Int.lt_eq (a b : Int) : (a < b) = (a + 1 ≤ b) := by
|
||||
simp [Int.lt, LT.lt]
|
||||
|
||||
-- GT GE
|
||||
attribute [grind_norm] GT.gt GE.ge
|
||||
theorem ge_eq [LE α] (a b : α) : (a ≥ b) = (b ≤ a) := rfl
|
||||
theorem gt_eq [LT α] (a b : α) : (a > b) = (b < a) := rfl
|
||||
|
||||
-- Succ
|
||||
attribute [grind_norm] Nat.succ_eq_add_one
|
||||
init_grind_norm
|
||||
/- Pre theorems -/
|
||||
not_and not_or not_ite not_forall not_exists
|
||||
|
|
||||
/- Post theorems -/
|
||||
Classical.not_not
|
||||
ne_eq iff_eq eq_self heq_eq_eq
|
||||
-- Prop equality
|
||||
eq_true_eq eq_false_eq not_eq_prop
|
||||
-- True
|
||||
not_true
|
||||
-- False
|
||||
not_false_eq_true
|
||||
-- Implication
|
||||
true_imp_eq false_imp_eq imp_true_eq imp_false_eq imp_self_eq
|
||||
-- And
|
||||
and_true true_and and_false false_and and_assoc
|
||||
-- Or
|
||||
or_true true_or or_false false_or or_assoc
|
||||
-- ite
|
||||
ite_true ite_false ite_true_false ite_false_true
|
||||
-- Forall
|
||||
forall_and
|
||||
-- Exists
|
||||
exists_const exists_or exists_prop exists_and_left exists_and_right
|
||||
-- Bool cond
|
||||
cond_eq_ite
|
||||
-- Bool or
|
||||
Bool.or_false Bool.or_true Bool.false_or Bool.true_or Bool.or_eq_true Bool.or_assoc
|
||||
-- Bool and
|
||||
Bool.and_false Bool.and_true Bool.false_and Bool.true_and Bool.and_eq_true Bool.and_assoc
|
||||
-- Bool not
|
||||
Bool.not_not
|
||||
-- beq
|
||||
beq_iff_eq
|
||||
-- bne
|
||||
bne_iff_ne
|
||||
-- Bool not eq true/false
|
||||
Bool.not_eq_true Bool.not_eq_false
|
||||
-- decide
|
||||
decide_eq_true_eq decide_not not_decide_eq_true
|
||||
-- Nat LE
|
||||
Nat.le_zero_eq
|
||||
-- Nat/Int LT
|
||||
Nat.lt_eq
|
||||
-- Nat.succ
|
||||
Nat.succ_eq_add_one
|
||||
-- Int
|
||||
Int.lt_eq
|
||||
-- GT GE
|
||||
ge_eq gt_eq
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -7,159 +7,86 @@ prelude
|
||||
import Init.Core
|
||||
import Init.Omega
|
||||
|
||||
namespace Lean.Grind.Offset
|
||||
namespace Lean.Grind
|
||||
abbrev isLt (x y : Nat) : Bool := x < y
|
||||
abbrev isLE (x y : Nat) : Bool := x ≤ y
|
||||
|
||||
abbrev Var := Nat
|
||||
abbrev Context := Lean.RArray Nat
|
||||
/-! Theorems for transitivity. -/
|
||||
theorem Nat.le_ro (u w v k : Nat) : u ≤ w → w ≤ v + k → u ≤ v + k := by
|
||||
omega
|
||||
theorem Nat.le_lo (u w v k : Nat) : u ≤ w → w + k ≤ v → u + k ≤ v := by
|
||||
omega
|
||||
theorem Nat.lo_le (u w v k : Nat) : u + k ≤ w → w ≤ v → u + k ≤ v := by
|
||||
omega
|
||||
theorem Nat.lo_lo (u w v k₁ k₂ : Nat) : u + k₁ ≤ w → w + k₂ ≤ v → u + (k₁ + k₂) ≤ v := by
|
||||
omega
|
||||
theorem Nat.lo_ro_1 (u w v k₁ k₂ : Nat) : isLt k₂ k₁ = true → u + k₁ ≤ w → w ≤ v + k₂ → u + (k₁ - k₂) ≤ v := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.lo_ro_2 (u w v k₁ k₂ : Nat) : u + k₁ ≤ w → w ≤ v + k₂ → u ≤ v + (k₂ - k₁) := by
|
||||
omega
|
||||
theorem Nat.ro_le (u w v k : Nat) : u ≤ w + k → w ≤ v → u ≤ v + k := by
|
||||
omega
|
||||
theorem Nat.ro_lo_1 (u w v k₁ k₂ : Nat) : u ≤ w + k₁ → w + k₂ ≤ v → u ≤ v + (k₁ - k₂) := by
|
||||
omega
|
||||
theorem Nat.ro_lo_2 (u w v k₁ k₂ : Nat) : isLt k₁ k₂ = true → u ≤ w + k₁ → w + k₂ ≤ v → u + (k₂ - k₁) ≤ v := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.ro_ro (u w v k₁ k₂ : Nat) : u ≤ w + k₁ → w ≤ v + k₂ → u ≤ v + (k₁ + k₂) := by
|
||||
omega
|
||||
|
||||
def fixedVar := 100000000 -- Any big number should work here
|
||||
/-! Theorems for negating constraints. -/
|
||||
theorem Nat.of_le_eq_false (u v : Nat) : ((u ≤ v) = False) → v + 1 ≤ u := by
|
||||
simp; omega
|
||||
theorem Nat.of_lo_eq_false_1 (u v : Nat) : ((u + 1 ≤ v) = False) → v ≤ u := by
|
||||
simp; omega
|
||||
theorem Nat.of_lo_eq_false (u v k : Nat) : ((u + k ≤ v) = False) → v ≤ u + (k-1) := by
|
||||
simp; omega
|
||||
theorem Nat.of_ro_eq_false (u v k : Nat) : ((u ≤ v + k) = False) → v + (k+1) ≤ u := by
|
||||
simp; omega
|
||||
|
||||
def Var.denote (ctx : Context) (v : Var) : Nat :=
|
||||
bif v == fixedVar then 1 else ctx.get v
|
||||
/-! Theorems for closing a goal. -/
|
||||
theorem Nat.unsat_le_lo (u v k : Nat) : isLt 0 k = true → u ≤ v → v + k ≤ u → False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.unsat_lo_lo (u v k₁ k₂ : Nat) : isLt 0 (k₁+k₂) = true → u + k₁ ≤ v → v + k₂ ≤ u → False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.unsat_lo_ro (u v k₁ k₂ : Nat) : isLt k₂ k₁ = true → u + k₁ ≤ v → v ≤ u + k₂ → False := by
|
||||
simp [isLt]; omega
|
||||
|
||||
structure Cnstr where
|
||||
x : Var
|
||||
y : Var
|
||||
k : Nat := 0
|
||||
l : Bool := true
|
||||
deriving Repr, DecidableEq, Inhabited
|
||||
/-! Theorems for propagating constraints to `True` -/
|
||||
theorem Nat.lo_eq_true_of_lo (u v k₁ k₂ : Nat) : isLE k₂ k₁ = true → u + k₁ ≤ v → (u + k₂ ≤ v) = True :=
|
||||
by simp [isLt]; omega
|
||||
theorem Nat.le_eq_true_of_lo (u v k : Nat) : u + k ≤ v → (u ≤ v) = True :=
|
||||
by simp; omega
|
||||
theorem Nat.le_eq_true_of_le (u v : Nat) : u ≤ v → (u ≤ v) = True :=
|
||||
by simp
|
||||
theorem Nat.ro_eq_true_of_lo (u v k₁ k₂ : Nat) : u + k₁ ≤ v → (u ≤ v + k₂) = True :=
|
||||
by simp; omega
|
||||
theorem Nat.ro_eq_true_of_le (u v k : Nat) : u ≤ v → (u ≤ v + k) = True :=
|
||||
by simp; omega
|
||||
theorem Nat.ro_eq_true_of_ro (u v k₁ k₂ : Nat) : isLE k₁ k₂ = true → u ≤ v + k₁ → (u ≤ v + k₂) = True :=
|
||||
by simp [isLE]; omega
|
||||
|
||||
def Cnstr.denote (c : Cnstr) (ctx : Context) : Prop :=
|
||||
if c.l then
|
||||
c.x.denote ctx + c.k ≤ c.y.denote ctx
|
||||
else
|
||||
c.x.denote ctx ≤ c.y.denote ctx + c.k
|
||||
/-!
|
||||
Theorems for propagating constraints to `False`.
|
||||
They are variants of the theorems for closing a goal.
|
||||
-/
|
||||
theorem Nat.lo_eq_false_of_le (u v k : Nat) : isLt 0 k = true → u ≤ v → (v + k ≤ u) = False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.le_eq_false_of_lo (u v k : Nat) : isLt 0 k = true → u + k ≤ v → (v ≤ u) = False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.lo_eq_false_of_lo (u v k₁ k₂ : Nat) : isLt 0 (k₁+k₂) = true → u + k₁ ≤ v → (v + k₂ ≤ u) = False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.ro_eq_false_of_lo (u v k₁ k₂ : Nat) : isLt k₂ k₁ = true → u + k₁ ≤ v → (v ≤ u + k₂) = False := by
|
||||
simp [isLt]; omega
|
||||
theorem Nat.lo_eq_false_of_ro (u v k₁ k₂ : Nat) : isLt k₁ k₂ = true → u ≤ v + k₁ → (v + k₂ ≤ u) = False := by
|
||||
simp [isLt]; omega
|
||||
|
||||
def trivialCnstr : Cnstr := { x := 0, y := 0, k := 0, l := true }
|
||||
/-!
|
||||
Helper theorems for equality propagation
|
||||
-/
|
||||
|
||||
@[simp] theorem denote_trivial (ctx : Context) : trivialCnstr.denote ctx := by
|
||||
simp [Cnstr.denote, trivialCnstr]
|
||||
theorem Nat.le_of_eq_1 (u v : Nat) : u = v → u ≤ v := by omega
|
||||
theorem Nat.le_of_eq_2 (u v : Nat) : u = v → v ≤ u := by omega
|
||||
theorem Nat.eq_of_le_of_le (u v : Nat) : u ≤ v → v ≤ u → u = v := by omega
|
||||
theorem Nat.le_offset (a k : Nat) : k ≤ a + k := by omega
|
||||
|
||||
def Cnstr.trans (c₁ c₂ : Cnstr) : Cnstr :=
|
||||
if c₁.y = c₂.x then
|
||||
let { x, k := k₁, l := l₁, .. } := c₁
|
||||
let { y, k := k₂, l := l₂, .. } := c₂
|
||||
match l₁, l₂ with
|
||||
| false, false =>
|
||||
{ x, y, k := k₁ + k₂, l := false }
|
||||
| false, true =>
|
||||
if k₁ < k₂ then
|
||||
{ x, y, k := k₂ - k₁, l := true }
|
||||
else
|
||||
{ x, y, k := k₁ - k₂, l := false }
|
||||
| true, false =>
|
||||
if k₁ < k₂ then
|
||||
{ x, y, k := k₂ - k₁, l := false }
|
||||
else
|
||||
{ x, y, k := k₁ - k₂, l := true }
|
||||
| true, true =>
|
||||
{ x, y, k := k₁ + k₂, l := true }
|
||||
else
|
||||
trivialCnstr
|
||||
|
||||
@[simp] theorem Cnstr.denote_trans_easy (ctx : Context) (c₁ c₂ : Cnstr) (h : c₁.y ≠ c₂.x) : (c₁.trans c₂).denote ctx := by
|
||||
simp [*, Cnstr.trans]
|
||||
|
||||
@[simp] theorem Cnstr.denote_trans (ctx : Context) (c₁ c₂ : Cnstr) : c₁.denote ctx → c₂.denote ctx → (c₁.trans c₂).denote ctx := by
|
||||
by_cases c₁.y = c₂.x
|
||||
case neg => simp [*]
|
||||
simp [trans, *]
|
||||
let { x, k := k₁, l := l₁, .. } := c₁
|
||||
let { y, k := k₂, l := l₂, .. } := c₂
|
||||
simp_all; split
|
||||
· simp [denote]; omega
|
||||
· split <;> simp [denote] <;> omega
|
||||
· split <;> simp [denote] <;> omega
|
||||
· simp [denote]; omega
|
||||
|
||||
def Cnstr.isTrivial (c : Cnstr) : Bool := c.x == c.y && c.k == 0
|
||||
|
||||
theorem Cnstr.of_isTrivial (ctx : Context) (c : Cnstr) : c.isTrivial = true → c.denote ctx := by
|
||||
cases c; simp [isTrivial]; intros; simp [*, denote]
|
||||
|
||||
def Cnstr.isFalse (c : Cnstr) : Bool := c.x == c.y && c.k != 0 && c.l == true
|
||||
|
||||
theorem Cnstr.of_isFalse (ctx : Context) {c : Cnstr} : c.isFalse = true → ¬c.denote ctx := by
|
||||
cases c; simp [isFalse]; intros; simp [*, denote]; omega
|
||||
|
||||
def Cnstrs := List Cnstr
|
||||
|
||||
def Cnstrs.denoteAnd' (ctx : Context) (c₁ : Cnstr) (c₂ : Cnstrs) : Prop :=
|
||||
match c₂ with
|
||||
| [] => c₁.denote ctx
|
||||
| c::cs => c₁.denote ctx ∧ Cnstrs.denoteAnd' ctx c cs
|
||||
|
||||
theorem Cnstrs.denote'_trans (ctx : Context) (c₁ c : Cnstr) (cs : Cnstrs) : c₁.denote ctx → denoteAnd' ctx c cs → denoteAnd' ctx (c₁.trans c) cs := by
|
||||
induction cs
|
||||
next => simp [denoteAnd', *]; apply Cnstr.denote_trans
|
||||
next c cs ih => simp [denoteAnd']; intros; simp [*]
|
||||
|
||||
def Cnstrs.trans' (c₁ : Cnstr) (c₂ : Cnstrs) : Cnstr :=
|
||||
match c₂ with
|
||||
| [] => c₁
|
||||
| c::c₂ => trans' (c₁.trans c) c₂
|
||||
|
||||
@[simp] theorem Cnstrs.denote'_trans' (ctx : Context) (c₁ : Cnstr) (c₂ : Cnstrs) : denoteAnd' ctx c₁ c₂ → (trans' c₁ c₂).denote ctx := by
|
||||
induction c₂ generalizing c₁
|
||||
next => intros; simp_all [trans', denoteAnd']
|
||||
next c cs ih => simp [denoteAnd']; intros; simp [trans']; apply ih; apply denote'_trans <;> assumption
|
||||
|
||||
def Cnstrs.denoteAnd (ctx : Context) (c : Cnstrs) : Prop :=
|
||||
match c with
|
||||
| [] => True
|
||||
| c::cs => denoteAnd' ctx c cs
|
||||
|
||||
def Cnstrs.trans (c : Cnstrs) : Cnstr :=
|
||||
match c with
|
||||
| [] => trivialCnstr
|
||||
| c::cs => trans' c cs
|
||||
|
||||
theorem Cnstrs.of_denoteAnd_trans {ctx : Context} {c : Cnstrs} : c.denoteAnd ctx → c.trans.denote ctx := by
|
||||
cases c <;> simp [*, trans, denoteAnd] <;> intros <;> simp [*]
|
||||
|
||||
def Cnstrs.isFalse (c : Cnstrs) : Bool :=
|
||||
c.trans.isFalse
|
||||
|
||||
theorem Cnstrs.unsat' (ctx : Context) (c : Cnstrs) : c.isFalse = true → ¬ c.denoteAnd ctx := by
|
||||
simp [isFalse]; intro h₁ h₂
|
||||
have := of_denoteAnd_trans h₂
|
||||
have := Cnstr.of_isFalse ctx h₁
|
||||
contradiction
|
||||
|
||||
/-- `denote ctx [c_1, ..., c_n] C` is `c_1.denote ctx → ... → c_n.denote ctx → C` -/
|
||||
def Cnstrs.denote (ctx : Context) (cs : Cnstrs) (C : Prop) : Prop :=
|
||||
match cs with
|
||||
| [] => C
|
||||
| c::cs => c.denote ctx → denote ctx cs C
|
||||
|
||||
theorem Cnstrs.not_denoteAnd'_eq (ctx : Context) (c : Cnstr) (cs : Cnstrs) (C : Prop) : (denoteAnd' ctx c cs → C) = denote ctx (c::cs) C := by
|
||||
simp [denote]
|
||||
induction cs generalizing c
|
||||
next => simp [denoteAnd', denote]
|
||||
next c' cs ih =>
|
||||
simp [denoteAnd', denote, *]
|
||||
|
||||
theorem Cnstrs.not_denoteAnd_eq (ctx : Context) (cs : Cnstrs) (C : Prop) : (denoteAnd ctx cs → C) = denote ctx cs C := by
|
||||
cases cs
|
||||
next => simp [denoteAnd, denote]
|
||||
next c cs => apply not_denoteAnd'_eq
|
||||
|
||||
def Cnstr.isImpliedBy (cs : Cnstrs) (c : Cnstr) : Bool :=
|
||||
cs.trans == c
|
||||
|
||||
/-! Main theorems used by `grind`. -/
|
||||
|
||||
/-- Auxiliary theorem used by `grind` to prove that a system of offset inequalities is unsatisfiable. -/
|
||||
theorem Cnstrs.unsat (ctx : Context) (cs : Cnstrs) : cs.isFalse = true → cs.denote ctx False := by
|
||||
intro h
|
||||
rw [← not_denoteAnd_eq]
|
||||
apply unsat'
|
||||
assumption
|
||||
|
||||
/-- Auxiliary theorem used by `grind` to prove an implied offset inequality. -/
|
||||
theorem Cnstrs.imp (ctx : Context) (cs : Cnstrs) (c : Cnstr) (h : c.isImpliedBy cs = true) : cs.denote ctx (c.denote ctx) := by
|
||||
rw [← eq_of_beq h]
|
||||
rw [← not_denoteAnd_eq]
|
||||
apply of_denoteAnd_trans
|
||||
|
||||
end Lean.Grind.Offset
|
||||
end Lean.Grind
|
||||
|
||||
30
src/Init/Grind/PP.lean
Normal file
30
src/Init/Grind/PP.lean
Normal file
@@ -0,0 +1,30 @@
|
||||
/-
|
||||
Copyright (c) 2024 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.NotationExtra
|
||||
|
||||
namespace Lean.Grind
|
||||
/-!
|
||||
This is a hackish module for hovering node information in the `grind` tactic state.
|
||||
-/
|
||||
|
||||
inductive NodeDef where
|
||||
| unit
|
||||
|
||||
set_option linter.unusedVariables false in
|
||||
def node_def (_ : Nat) {α : Sort u} {a : α} : NodeDef := .unit
|
||||
|
||||
@[app_unexpander node_def]
|
||||
def nodeDefUnexpander : PrettyPrinter.Unexpander := fun stx => do
|
||||
match stx with
|
||||
| `($_ $id:num) => return mkIdent <| Name.mkSimple $ "#" ++ toString id.getNat
|
||||
| _ => throw ()
|
||||
|
||||
@[app_unexpander NodeDef]
|
||||
def NodeDefUnexpander : PrettyPrinter.Unexpander := fun _ => do
|
||||
return mkIdent <| Name.mkSimple "NodeDef"
|
||||
|
||||
end Lean.Grind
|
||||
@@ -11,10 +11,15 @@ namespace Lean.Parser.Attr
|
||||
syntax grindEq := "="
|
||||
syntax grindEqBoth := atomic("_" "=" "_")
|
||||
syntax grindEqRhs := atomic("=" "_")
|
||||
syntax grindEqBwd := atomic("←" "=")
|
||||
syntax grindBwd := "←"
|
||||
syntax grindFwd := "→"
|
||||
syntax grindCases := &"cases"
|
||||
syntax grindCasesEager := atomic(&"cases" &"eager")
|
||||
|
||||
syntax (name := grind) "grind" (grindEqBoth <|> grindEqRhs <|> grindEq <|> grindBwd <|> grindFwd)? : attr
|
||||
syntax grindMod := grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd <|> grindFwd <|> grindCasesEager <|> grindCases
|
||||
|
||||
syntax (name := grind) "grind" (grindMod)? : attr
|
||||
|
||||
end Lean.Parser.Attr
|
||||
|
||||
@@ -25,7 +30,7 @@ Passed to `grind` using, for example, the `grind (config := { matchEqs := true }
|
||||
-/
|
||||
structure Config where
|
||||
/-- Maximum number of case-splits in a proof search branch. It does not include splits performed during normalization. -/
|
||||
splits : Nat := 5
|
||||
splits : Nat := 8
|
||||
/-- Maximum number of E-matching (aka heuristic theorem instantiation) rounds before each case split. -/
|
||||
ematch : Nat := 5
|
||||
/--
|
||||
@@ -43,8 +48,14 @@ structure Config where
|
||||
splitIte : Bool := true
|
||||
/--
|
||||
If `splitIndPred` is `true`, `grind` performs case-splitting on inductive predicates.
|
||||
Otherwise, it performs case-splitting only on types marked with `[grind_split]` attribute. -/
|
||||
splitIndPred : Bool := true
|
||||
Otherwise, it performs case-splitting only on types marked with `[grind cases]` attribute. -/
|
||||
splitIndPred : Bool := false
|
||||
/-- By default, `grind` halts as soon as it encounters a sub-goal where no further progress can be made. -/
|
||||
failures : Nat := 1
|
||||
/-- Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test. -/
|
||||
canonHeartbeats : Nat := 1000
|
||||
/-- If `ext` is `true`, `grind` uses extensionality theorems available in the environment. -/
|
||||
ext : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
end Lean.Grind
|
||||
@@ -55,7 +66,13 @@ namespace Lean.Parser.Tactic
|
||||
`grind` tactic and related tactics.
|
||||
-/
|
||||
|
||||
-- TODO: parameters
|
||||
syntax (name := grind) "grind" optConfig ("on_failure " term)? : tactic
|
||||
syntax grindErase := "-" ident
|
||||
syntax grindLemma := (Attr.grindMod)? ident
|
||||
syntax grindParam := grindErase <|> grindLemma
|
||||
|
||||
syntax (name := grind)
|
||||
"grind" optConfig (&" only")?
|
||||
(" [" withoutPosition(grindParam,*) "]")?
|
||||
("on_failure " term)? : tactic
|
||||
|
||||
end Lean.Parser.Tactic
|
||||
|
||||
@@ -9,18 +9,20 @@ import Init.Core
|
||||
namespace Lean.Grind
|
||||
|
||||
/-- A helper gadget for annotating nested proofs in goals. -/
|
||||
def nestedProof (p : Prop) (h : p) : p := h
|
||||
def nestedProof (p : Prop) {h : p} : p := h
|
||||
|
||||
/--
|
||||
Gadget for marking terms that should not be normalized by `grind`s simplifier.
|
||||
`grind` uses a simproc to implement this feature.
|
||||
Gadget for marking `match`-expressions that should not be reduced by the `grind` simplifier, but the discriminants should be normalized.
|
||||
We use it when adding instances of `match`-equations to prevent them from being simplified to true.
|
||||
-/
|
||||
def doNotSimp {α : Sort u} (a : α) : α := a
|
||||
def simpMatchDiscrsOnly {α : Sort u} (a : α) : α := a
|
||||
|
||||
/-- Gadget for representing offsets `t+k` in patterns. -/
|
||||
def offset (a b : Nat) : Nat := a + b
|
||||
|
||||
/-- Gadget for representing `a = b` in patterns for backward propagation. -/
|
||||
def eqBwdPattern (a b : α) : Prop := a = b
|
||||
|
||||
/--
|
||||
Gadget for annotating the equalities in `match`-equations conclusions.
|
||||
`_origin` is the term used to instantiate the `match`-equation using E-matching.
|
||||
@@ -28,7 +30,15 @@ When `EqMatch a b origin` is `True`, we mark `origin` as a resolved case-split.
|
||||
-/
|
||||
def EqMatch (a b : α) {_origin : α} : Prop := a = b
|
||||
|
||||
theorem nestedProof_congr (p q : Prop) (h : p = q) (hp : p) (hq : q) : HEq (nestedProof p hp) (nestedProof q hq) := by
|
||||
/--
|
||||
Gadget for annotating conditions of `match` equational lemmas.
|
||||
We use this annotation for two different reasons:
|
||||
- We don't want to normalize them.
|
||||
- We have a propagator for them.
|
||||
-/
|
||||
def MatchCond (p : Prop) : Prop := p
|
||||
|
||||
theorem nestedProof_congr (p q : Prop) (h : p = q) (hp : p) (hq : q) : HEq (@nestedProof p hp) (@nestedProof q hq) := by
|
||||
subst h; apply HEq.refl
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -5,4 +5,5 @@ Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Init.Internal.Order.Basic
|
||||
import Init.Internal.Order.Lemmas
|
||||
import Init.Internal.Order.Tactic
|
||||
|
||||
@@ -104,7 +104,7 @@ variable {α : Sort u} [PartialOrder α]
|
||||
variable {β : Sort v} [PartialOrder β]
|
||||
|
||||
/--
|
||||
A function is monotone if if it maps related elements to releated elements.
|
||||
A function is monotone if it maps related elements to releated elements.
|
||||
|
||||
This is intended to be used in the construction of `partial_fixpoint`, and not meant to be used otherwise.
|
||||
-/
|
||||
@@ -401,6 +401,7 @@ theorem monotone_letFun
|
||||
(hmono : ∀ y, monotone (fun x => k x y)) :
|
||||
monotone fun (x : α) => letFun v (k x) := hmono v
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_ite
|
||||
{α : Sort u} {β : Sort v} [PartialOrder α] [PartialOrder β]
|
||||
(c : Prop) [Decidable c]
|
||||
@@ -411,6 +412,7 @@ theorem monotone_ite
|
||||
· apply hmono₁
|
||||
· apply hmono₂
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_dite
|
||||
{α : Sort u} {β : Sort v} [PartialOrder α] [PartialOrder β]
|
||||
(c : Prop) [Decidable c]
|
||||
@@ -440,38 +442,41 @@ instance [PartialOrder α] [PartialOrder β] : PartialOrder (α ×' β) where
|
||||
dsimp at *
|
||||
rw [rel_antisymm ha.1 hb.1, rel_antisymm ha.2 hb.2]
|
||||
|
||||
theorem monotone_pprod [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem PProd.monotone_mk [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
{f : γ → α} {g : γ → β} (hf : monotone f) (hg : monotone g) :
|
||||
monotone (fun x => PProd.mk (f x) (g x)) :=
|
||||
fun _ _ h12 => ⟨hf _ _ h12, hg _ _ h12⟩
|
||||
|
||||
theorem monotone_pprod_fst [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem PProd.monotone_fst [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
{f : γ → α ×' β} (hf : monotone f) : monotone (fun x => (f x).1) :=
|
||||
fun _ _ h12 => (hf _ _ h12).1
|
||||
|
||||
theorem monotone_pprod_snd [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem PProd.monotone_snd [PartialOrder α] [PartialOrder β] [PartialOrder γ]
|
||||
{f : γ → α ×' β} (hf : monotone f) : monotone (fun x => (f x).2) :=
|
||||
fun _ _ h12 => (hf _ _ h12).2
|
||||
|
||||
def chain_pprod_fst [CCPO α] [CCPO β] (c : α ×' β → Prop) : α → Prop := fun a => ∃ b, c ⟨a, b⟩
|
||||
def chain_pprod_snd [CCPO α] [CCPO β] (c : α ×' β → Prop) : β → Prop := fun b => ∃ a, c ⟨a, b⟩
|
||||
def PProd.chain.fst [CCPO α] [CCPO β] (c : α ×' β → Prop) : α → Prop := fun a => ∃ b, c ⟨a, b⟩
|
||||
def PProd.chain.snd [CCPO α] [CCPO β] (c : α ×' β → Prop) : β → Prop := fun b => ∃ a, c ⟨a, b⟩
|
||||
|
||||
theorem chain.pprod_fst [CCPO α] [CCPO β] (c : α ×' β → Prop) (hchain : chain c) :
|
||||
chain (chain_pprod_fst c) := by
|
||||
theorem PProd.chain.chain_fst [CCPO α] [CCPO β] {c : α ×' β → Prop} (hchain : chain c) :
|
||||
chain (chain.fst c) := by
|
||||
intro a₁ a₂ ⟨b₁, h₁⟩ ⟨b₂, h₂⟩
|
||||
cases hchain ⟨a₁, b₁⟩ ⟨a₂, b₂⟩ h₁ h₂
|
||||
case inl h => left; exact h.1
|
||||
case inr h => right; exact h.1
|
||||
|
||||
theorem chain.pprod_snd [CCPO α] [CCPO β] (c : α ×' β → Prop) (hchain : chain c) :
|
||||
chain (chain_pprod_snd c) := by
|
||||
theorem PProd.chain.chain_snd [CCPO α] [CCPO β] {c : α ×' β → Prop} (hchain : chain c) :
|
||||
chain (chain.snd c) := by
|
||||
intro b₁ b₂ ⟨a₁, h₁⟩ ⟨a₂, h₂⟩
|
||||
cases hchain ⟨a₁, b₁⟩ ⟨a₂, b₂⟩ h₁ h₂
|
||||
case inl h => left; exact h.2
|
||||
case inr h => right; exact h.2
|
||||
|
||||
instance [CCPO α] [CCPO β] : CCPO (α ×' β) where
|
||||
csup c := ⟨CCPO.csup (chain_pprod_fst c), CCPO.csup (chain_pprod_snd c)⟩
|
||||
instance instCCPOPProd [CCPO α] [CCPO β] : CCPO (α ×' β) where
|
||||
csup c := ⟨CCPO.csup (PProd.chain.fst c), CCPO.csup (PProd.chain.snd c)⟩
|
||||
csup_spec := by
|
||||
intro ⟨a, b⟩ c hchain
|
||||
dsimp
|
||||
@@ -480,32 +485,32 @@ instance [CCPO α] [CCPO β] : CCPO (α ×' β) where
|
||||
intro ⟨h₁, h₂⟩ ⟨a', b'⟩ cab
|
||||
constructor <;> dsimp at *
|
||||
· apply rel_trans ?_ h₁
|
||||
apply le_csup hchain.pprod_fst
|
||||
apply le_csup (PProd.chain.chain_fst hchain)
|
||||
exact ⟨b', cab⟩
|
||||
· apply rel_trans ?_ h₂
|
||||
apply le_csup hchain.pprod_snd
|
||||
apply le_csup (PProd.chain.chain_snd hchain)
|
||||
exact ⟨a', cab⟩
|
||||
next =>
|
||||
intro h
|
||||
constructor <;> dsimp
|
||||
· apply csup_le hchain.pprod_fst
|
||||
· apply csup_le (PProd.chain.chain_fst hchain)
|
||||
intro a' ⟨b', hcab⟩
|
||||
apply (h _ hcab).1
|
||||
· apply csup_le hchain.pprod_snd
|
||||
· apply csup_le (PProd.chain.chain_snd hchain)
|
||||
intro b' ⟨a', hcab⟩
|
||||
apply (h _ hcab).2
|
||||
|
||||
theorem admissible_pprod_fst {α : Sort u} {β : Sort v} [CCPO α] [CCPO β] (P : α → Prop)
|
||||
(hadm : admissible P) : admissible (fun (x : α ×' β) => P x.1) := by
|
||||
intro c hchain h
|
||||
apply hadm _ hchain.pprod_fst
|
||||
apply hadm _ (PProd.chain.chain_fst hchain)
|
||||
intro x ⟨y, hxy⟩
|
||||
apply h ⟨x,y⟩ hxy
|
||||
|
||||
theorem admissible_pprod_snd {α : Sort u} {β : Sort v} [CCPO α] [CCPO β] (P : β → Prop)
|
||||
(hadm : admissible P) : admissible (fun (x : α ×' β) => P x.2) := by
|
||||
intro c hchain h
|
||||
apply hadm _ hchain.pprod_snd
|
||||
apply hadm _ (PProd.chain.chain_snd hchain)
|
||||
intro y ⟨x, hxy⟩
|
||||
apply h ⟨x,y⟩ hxy
|
||||
|
||||
@@ -609,6 +614,7 @@ class MonoBind (m : Type u → Type v) [Bind m] [∀ α, PartialOrder (m α)] wh
|
||||
bind_mono_left {a₁ a₂ : m α} {f : α → m b} (h : a₁ ⊑ a₂) : a₁ >>= f ⊑ a₂ >>= f
|
||||
bind_mono_right {a : m α} {f₁ f₂ : α → m b} (h : ∀ x, f₁ x ⊑ f₂ x) : a >>= f₁ ⊑ a >>= f₂
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_bind
|
||||
(m : Type u → Type v) [Bind m] [∀ α, PartialOrder (m α)] [MonoBind m]
|
||||
{α β : Type u}
|
||||
@@ -634,7 +640,7 @@ noncomputable instance : MonoBind Option where
|
||||
· exact FlatOrder.rel.refl
|
||||
· exact h _
|
||||
|
||||
theorem admissible_eq_some (P : Prop) (y : α) :
|
||||
theorem Option.admissible_eq_some (P : Prop) (y : α) :
|
||||
admissible (fun (x : Option α) => x = some y → P) := by
|
||||
apply admissible_flatOrder; simp
|
||||
|
||||
@@ -677,7 +683,7 @@ theorem find_spec : ∀ n m, find P n = some m → n ≤ m ∧ P m := by
|
||||
refine fix_induct (motive := fun (f : Nat → Option Nat) => ∀ n m, f n = some m → n ≤ m ∧ P m) _ ?hadm ?hstep
|
||||
case hadm =>
|
||||
-- apply admissible_pi_apply does not work well, hard to infer everything
|
||||
exact admissible_pi_apply _ (fun n => admissible_pi _ (fun m => admissible_eq_some _ m))
|
||||
exact admissible_pi_apply _ (fun n => admissible_pi _ (fun m => Option.admissible_eq_some _ m))
|
||||
case hstep =>
|
||||
intro f ih n m heq
|
||||
simp only [findF] at heq
|
||||
|
||||
685
src/Init/Internal/Order/Lemmas.lean
Normal file
685
src/Init/Internal/Order/Lemmas.lean
Normal file
@@ -0,0 +1,685 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
prelude
|
||||
|
||||
import Init.Data.List.Control
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Internal.Order.Basic
|
||||
|
||||
/-!
|
||||
|
||||
This file contains monotonicity lemmas for higher-order monadic operations (e.g. `mapM`) in the
|
||||
standard library. This allows recursive definitions using `partial_fixpoint` to use nested
|
||||
recursion.
|
||||
|
||||
Ideally, every higher-order monadic funciton in the standard library has a lemma here. At the time
|
||||
of writing, this file covers functions from
|
||||
|
||||
* Init/Data/Option/Basic.lean
|
||||
* Init/Data/List/Control.lean
|
||||
* Init/Data/Array/Basic.lean
|
||||
|
||||
in the order of their apperance there. No automation to check the exhaustiveness exists yet.
|
||||
|
||||
The lemma statements are written manually, but follow a predictable scheme, and could be automated.
|
||||
Likewise, the proofs are written very naively. Most of them could be handled by a tactic like
|
||||
`monotonicity` (extended to make use of local hypotheses).
|
||||
-/
|
||||
|
||||
namespace Lean.Order
|
||||
|
||||
open Lean.Order
|
||||
|
||||
variable {m : Type u → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m]
|
||||
variable {α β : Type u}
|
||||
variable {γ : Type w} [PartialOrder γ]
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem Functor.monotone_map [LawfulMonad m] (f : γ → m α) (g : α → β) (hmono : monotone f) :
|
||||
monotone (fun x => g <$> f x) := by
|
||||
simp only [← LawfulMonad.bind_pure_comp ]
|
||||
apply monotone_bind _ _ _ hmono
|
||||
apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem Seq.monotone_seq [LawfulMonad m] (f : γ → m α) (g : γ → m (α → β))
|
||||
(hmono₁ : monotone g) (hmono₂ : monotone f) :
|
||||
monotone (fun x => g x <*> f x) := by
|
||||
simp only [← LawfulMonad.bind_map ]
|
||||
apply monotone_bind
|
||||
· assumption
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply Functor.monotone_map
|
||||
assumption
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem SeqLeft.monotone_seqLeft [LawfulMonad m] (f : γ → m α) (g : γ → m β)
|
||||
(hmono₁ : monotone g) (hmono₂ : monotone f) :
|
||||
monotone (fun x => g x <* f x) := by
|
||||
simp only [seqLeft_eq]
|
||||
apply Seq.monotone_seq
|
||||
· apply Functor.monotone_map
|
||||
assumption
|
||||
· assumption
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem SeqRight.monotone_seqRight [LawfulMonad m] (f : γ → m α) (g : γ → m β)
|
||||
(hmono₁ : monotone g) (hmono₂ : monotone f) :
|
||||
monotone (fun x => g x *> f x) := by
|
||||
simp only [seqRight_eq]
|
||||
apply Seq.monotone_seq
|
||||
· apply Functor.monotone_map
|
||||
assumption
|
||||
· assumption
|
||||
|
||||
namespace Option
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_bindM (f : γ → α → m (Option β)) (xs : Option α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.bindM (f x)) := by
|
||||
cases xs with
|
||||
| none => apply monotone_const
|
||||
| some x =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_mapM (f : γ → α → m β) (xs : Option α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.mapM (f x)) := by
|
||||
cases xs with
|
||||
| none => apply monotone_const
|
||||
| some x =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_elimM (a : γ → m (Option α)) (n : γ → m β) (s : γ → α → m β)
|
||||
(hmono₁ : monotone a) (hmono₂ : monotone n) (hmono₃ : monotone s) :
|
||||
monotone (fun x => Option.elimM (a x) (n x) (s x)) := by
|
||||
apply monotone_bind
|
||||
· apply hmono₁
|
||||
· apply monotone_of_monotone_apply
|
||||
intro o
|
||||
cases o
|
||||
case none => apply hmono₂
|
||||
case some y =>
|
||||
dsimp only [Option.elim]
|
||||
apply monotone_apply
|
||||
apply hmono₃
|
||||
|
||||
omit [MonoBind m] in
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_getDM (o : Option α) (y : γ → m α) (hmono : monotone y) :
|
||||
monotone (fun x => o.getDM (y x)) := by
|
||||
cases o
|
||||
· apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
end Option
|
||||
|
||||
|
||||
namespace List
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_mapM (f : γ → α → m β) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.mapM (f x)) := by
|
||||
cases xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ xs =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
dsimp
|
||||
generalize [y] = ys
|
||||
induction xs generalizing ys with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forM (f : γ → α → m PUnit) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.forM (f x)) := by
|
||||
induction xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_filterAuxM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs acc : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.filterAuxM (f x) acc) := by
|
||||
induction xs generalizing acc with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_filterM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.filterM (f x)) := by
|
||||
apply monotone_bind
|
||||
· exact monotone_filterAuxM f xs [] hmono
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_filterRevM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.filterRevM (f x)) := by
|
||||
exact monotone_filterAuxM f xs.reverse [] hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldlM
|
||||
(f : γ → β → α → m β) (init : β) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.foldlM (f x) (init := init)) := by
|
||||
induction xs generalizing init with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldrM
|
||||
(f : γ → α → β → m β) (init : β) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.foldrM (f x) (init := init)) := by
|
||||
apply monotone_foldlM
|
||||
apply monotone_of_monotone_apply
|
||||
intro s
|
||||
apply monotone_of_monotone_apply
|
||||
intro a
|
||||
apply monotone_apply (a := s)
|
||||
apply monotone_apply (a := a)
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_anyM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.anyM (f x)) := by
|
||||
induction xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y
|
||||
· apply ih
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_allM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.allM (f x)) := by
|
||||
induction xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y
|
||||
· apply monotone_const
|
||||
· apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findM?
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findM? (f x)) := by
|
||||
induction xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y
|
||||
· apply ih
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findSomeM?
|
||||
(f : γ → α → m (Option β)) (xs : List α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findSomeM? (f x)) := by
|
||||
induction xs with
|
||||
| nil => apply monotone_const
|
||||
| cons _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y
|
||||
· apply ih
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn'_loop {α : Type uu}
|
||||
(as : List α) (f : γ → (a : α) → a ∈ as → β → m (ForInStep β)) (as' : List α) (b : β)
|
||||
(p : Exists (fun bs => bs ++ as' = as)) (hmono : monotone f) :
|
||||
monotone (fun x => List.forIn'.loop as (f x) as' b p) := by
|
||||
induction as' generalizing b with
|
||||
| nil => apply monotone_const
|
||||
| cons a as' ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y with
|
||||
| done => apply monotone_const
|
||||
| yield => apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn' {α : Type uu}
|
||||
(as : List α) (init : β) (f : γ → (a : α) → a ∈ as → β → m (ForInStep β)) (hmono : monotone f) :
|
||||
monotone (fun x => forIn' as init (f x)) := by
|
||||
apply monotone_forIn'_loop
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn {α : Type uu}
|
||||
(as : List α) (init : β) (f : γ → (a : α) → β → m (ForInStep β)) (hmono : monotone f) :
|
||||
monotone (fun x => forIn as init (f x)) := by
|
||||
apply monotone_forIn' as init _
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro p
|
||||
apply monotone_apply (a := y)
|
||||
apply hmono
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_modifyM (a : Array α) (i : Nat) (f : γ → α → m α) (hmono : monotone f) :
|
||||
monotone (fun x => a.modifyM i (f x)) := by
|
||||
unfold Array.modifyM
|
||||
split
|
||||
· apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn'_loop {α : Type uu}
|
||||
(as : Array α) (f : γ → (a : α) → a ∈ as → β → m (ForInStep β)) (i : Nat) (h : i ≤ as.size)
|
||||
(b : β) (hmono : monotone f) :
|
||||
monotone (fun x => Array.forIn'.loop as (f x) i h b) := by
|
||||
induction i, h, b using Array.forIn'.loop.induct with
|
||||
| case1 => apply monotone_const
|
||||
| case2 _ _ _ _ _ _ _ ih =>
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y with
|
||||
| done => apply monotone_const
|
||||
| yield => apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn' {α : Type uu}
|
||||
(as : Array α) (init : β) (f : γ → (a : α) → a ∈ as → β → m (ForInStep β)) (hmono : monotone f) :
|
||||
monotone (fun x => forIn' as init (f x)) := by
|
||||
apply monotone_forIn'_loop
|
||||
apply hmono
|
||||
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_forIn {α : Type uu}
|
||||
(as : Array α) (init : β) (f : γ → (a : α) → β → m (ForInStep β)) (hmono : monotone f) :
|
||||
monotone (fun x => forIn as init (f x)) := by
|
||||
apply monotone_forIn' as init _
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro p
|
||||
apply monotone_apply (a := y)
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldlM_loop
|
||||
(f : γ → β → α → m β) (xs : Array α) (stop : Nat) (h : stop ≤ xs.size) (i j : Nat) (b : β)
|
||||
(hmono : monotone f) : monotone (fun x => Array.foldlM.loop (f x) xs stop h i j b) := by
|
||||
induction i, j, b using Array.foldlM.loop.induct (h := h) with
|
||||
| case1 =>
|
||||
simp only [Array.foldlM.loop, ↓reduceDIte, *]
|
||||
apply monotone_const
|
||||
| case2 _ _ _ _ _ ih =>
|
||||
unfold Array.foldlM.loop
|
||||
simp only [↓reduceDIte, *]
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
apply ih
|
||||
| case3 =>
|
||||
simp only [Array.foldlM.loop, ↓reduceDIte, *]
|
||||
apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldlM
|
||||
(f : γ → β → α → m β) (init : β) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.foldlM (f x) init start stop) := by
|
||||
unfold Array.foldlM
|
||||
split <;> apply monotone_foldlM_loop (hmono := hmono)
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldrM_fold
|
||||
(f : γ → α → β → m β) (xs : Array α) (stop i : Nat) (h : i ≤ xs.size) (b : β)
|
||||
(hmono : monotone f) : monotone (fun x => Array.foldrM.fold (f x) xs stop i h b) := by
|
||||
induction i, h, b using Array.foldrM.fold.induct (stop := stop) with
|
||||
| case1 =>
|
||||
unfold Array.foldrM.fold
|
||||
simp only [↓reduceIte, *]
|
||||
apply monotone_const
|
||||
| case2 =>
|
||||
unfold Array.foldrM.fold
|
||||
simp only [↓reduceIte, *]
|
||||
apply monotone_const
|
||||
| case3 _ _ _ _ _ _ ih =>
|
||||
unfold Array.foldrM.fold
|
||||
simp only [reduceCtorEq, ↓reduceIte, *]
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_foldrM
|
||||
(f : γ → α → β → m β) (init : β) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.foldrM (f x) init start stop) := by
|
||||
unfold Array.foldrM
|
||||
split
|
||||
· split
|
||||
· apply monotone_foldrM_fold (hmono := hmono)
|
||||
· apply monotone_const
|
||||
· split
|
||||
· apply monotone_foldrM_fold (hmono := hmono)
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_mapM (xs : Array α) (f : γ → α → m β) (hmono : monotone f) :
|
||||
monotone (fun x => xs.mapM (f x)) := by
|
||||
suffices ∀ i r, monotone (fun x => Array.mapM.map (f x) xs i r) by apply this
|
||||
intros i r
|
||||
induction i, r using Array.mapM.map.induct xs
|
||||
case case1 ih =>
|
||||
unfold Array.mapM.map
|
||||
simp only [↓reduceDIte, *]
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· intro y
|
||||
apply monotone_of_monotone_apply
|
||||
apply ih
|
||||
case case2 =>
|
||||
unfold Array.mapM.map
|
||||
simp only [↓reduceDIte, *]
|
||||
apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_mapFinIdxM (xs : Array α) (f : γ → (i : Nat) → α → i < xs.size → m β)
|
||||
(hmono : monotone f) : monotone (fun x => xs.mapFinIdxM (f x)) := by
|
||||
suffices ∀ i j (h : i + j = xs.size) r, monotone (fun x => Array.mapFinIdxM.map xs (f x) i j h r) by apply this
|
||||
intros i j h r
|
||||
induction i, j, h, r using Array.mapFinIdxM.map.induct xs
|
||||
case case1 =>
|
||||
apply monotone_const
|
||||
case case2 ih =>
|
||||
apply monotone_bind
|
||||
· dsimp
|
||||
apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
· intro y
|
||||
apply monotone_of_monotone_apply
|
||||
apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findSomeM?
|
||||
(f : γ → α → m (Option β)) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findSomeM? (f x)) := by
|
||||
unfold Array.findSomeM?
|
||||
apply monotone_bind
|
||||
· apply monotone_forIn
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro r
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findM?
|
||||
{m : Type → Type} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findM? (f x)) := by
|
||||
unfold Array.findM?
|
||||
apply monotone_bind
|
||||
· apply monotone_forIn
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro r
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findIdxM?
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type u}
|
||||
(f : γ → α → m Bool) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findIdxM? (f x)) := by
|
||||
unfold Array.findIdxM?
|
||||
apply monotone_bind
|
||||
· apply monotone_forIn
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro r
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_anyM_loop
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type u}
|
||||
(f : γ → α → m Bool) (xs : Array α) (stop : Nat) (h : stop ≤ xs.size) (j : Nat)
|
||||
(hmono : monotone f) : monotone (fun x => Array.anyM.loop (f x) xs stop h j) := by
|
||||
induction j using Array.anyM.loop.induct (h := h) with
|
||||
| case2 =>
|
||||
unfold Array.anyM.loop
|
||||
simp only [↓reduceDIte, *]
|
||||
apply monotone_const
|
||||
| case1 _ _ _ ih =>
|
||||
unfold Array.anyM.loop
|
||||
simp only [↓reduceDIte, *]
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
split
|
||||
· apply monotone_const
|
||||
· apply ih
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_anyM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type u}
|
||||
(f : γ → α → m Bool) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.anyM (f x) start stop) := by
|
||||
unfold Array.anyM
|
||||
split
|
||||
· apply monotone_anyM_loop
|
||||
apply hmono
|
||||
· apply monotone_anyM_loop
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_allM
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type u}
|
||||
(f : γ → α → m Bool) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.allM (f x) start stop) := by
|
||||
unfold Array.allM
|
||||
apply monotone_bind
|
||||
· apply monotone_anyM
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findSomeRevM?
|
||||
(f : γ → α → m (Option β)) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findSomeRevM? (f x)) := by
|
||||
unfold Array.findSomeRevM?
|
||||
suffices ∀ i (h : i ≤ xs.size), monotone (fun x => Array.findSomeRevM?.find (f x) xs i h) by apply this
|
||||
intros i h
|
||||
induction i, h using Array.findSomeRevM?.find.induct with
|
||||
| case1 =>
|
||||
unfold Array.findSomeRevM?.find
|
||||
apply monotone_const
|
||||
| case2 _ _ _ _ ih =>
|
||||
unfold Array.findSomeRevM?.find
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_of_monotone_apply
|
||||
intro y
|
||||
cases y with
|
||||
| none => apply ih
|
||||
| some y => apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_findRevM?
|
||||
{m : Type → Type v} [Monad m] [∀ α, PartialOrder (m α)] [MonoBind m] {α : Type}
|
||||
(f : γ → α → m Bool) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.findRevM? (f x)) := by
|
||||
unfold Array.findRevM?
|
||||
apply monotone_findSomeRevM?
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_array_forM
|
||||
(f : γ → α → m PUnit) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.forM (f x) start stop) := by
|
||||
unfold Array.forM
|
||||
apply monotone_foldlM
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_array_forRevM
|
||||
(f : γ → α → m PUnit) (xs : Array α) (start stop : Nat) (hmono : monotone f) :
|
||||
monotone (fun x => xs.forRevM (f x) start stop) := by
|
||||
unfold Array.forRevM
|
||||
apply monotone_foldrM
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro z
|
||||
apply monotone_apply
|
||||
apply hmono
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_flatMapM
|
||||
(f : γ → α → m (Array β)) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.flatMapM (f x)) := by
|
||||
unfold Array.flatMapM
|
||||
apply monotone_foldlM
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro z
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
|
||||
@[partial_fixpoint_monotone]
|
||||
theorem monotone_array_filterMapM
|
||||
(f : γ → α → m (Option β)) (xs : Array α) (hmono : monotone f) :
|
||||
monotone (fun x => xs.filterMapM (f x)) := by
|
||||
unfold Array.filterMapM
|
||||
apply monotone_foldlM
|
||||
apply monotone_of_monotone_apply
|
||||
intro y
|
||||
apply monotone_of_monotone_apply
|
||||
intro z
|
||||
apply monotone_bind
|
||||
· apply monotone_apply
|
||||
apply hmono
|
||||
· apply monotone_const
|
||||
|
||||
end Array
|
||||
|
||||
end Lean.Order
|
||||
@@ -93,7 +93,8 @@ def isLetterLike (c : Char) : Bool :=
|
||||
def isSubScriptAlnum (c : Char) : Bool :=
|
||||
isNumericSubscript c ||
|
||||
(0x2090 ≤ c.val && c.val ≤ 0x209c) ||
|
||||
(0x1d62 ≤ c.val && c.val ≤ 0x1d6a)
|
||||
(0x1d62 ≤ c.val && c.val ≤ 0x1d6a) ||
|
||||
c.val == 0x2c7c
|
||||
|
||||
@[inline] def isIdFirst (c : Char) : Bool :=
|
||||
c.isAlpha || c = '_' || isLetterLike c
|
||||
|
||||
@@ -109,6 +109,11 @@ structure Config where
|
||||
to find candidate `simp` theorems. It approximates Lean 3 `simp` behavior.
|
||||
-/
|
||||
index : Bool := true
|
||||
/--
|
||||
When `true` (default : `true`), then simps will remove unused let-declarations:
|
||||
`let x := v; e` simplifies to `e` when `x` does not occur in `e`.
|
||||
-/
|
||||
zetaUnused : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
end DSimp
|
||||
@@ -228,6 +233,11 @@ structure Config where
|
||||
input and output terms are definitionally equal.
|
||||
-/
|
||||
implicitDefEqProofs : Bool := true
|
||||
/--
|
||||
When `true` (default : `true`), then simps will remove unused let-declarations:
|
||||
`let x := v; e` simplifies to `e` when `x` does not occur in `e`.
|
||||
-/
|
||||
zetaUnused : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
-- Configuration object for `simp_all`
|
||||
@@ -248,6 +258,7 @@ def neutralConfig : Simp.Config := {
|
||||
autoUnfold := false
|
||||
ground := false
|
||||
zetaDelta := false
|
||||
zetaUnused := false
|
||||
}
|
||||
|
||||
structure NormCastConfig extends Simp.Config where
|
||||
|
||||
@@ -150,6 +150,9 @@ It can also be written as `()`.
|
||||
/-- Marker for information that has been erased by the code generator. -/
|
||||
unsafe axiom lcErased : Type
|
||||
|
||||
/-- Marker for type dependency that has been erased by the code generator. -/
|
||||
unsafe axiom lcAny : Type
|
||||
|
||||
/--
|
||||
Auxiliary unsafe constant used by the Compiler when erasing proofs from code.
|
||||
|
||||
@@ -3702,8 +3705,7 @@ inductive Syntax where
|
||||
/-- Node in the syntax tree.
|
||||
|
||||
The `info` field is used by the delaborator to store the position of the
|
||||
subexpression corresponding to this node. The parser sets the `info` field
|
||||
to `none`.
|
||||
subexpression corresponding to this node.
|
||||
The parser sets the `info` field to `none`, with position retrieval continuing recursively.
|
||||
Nodes created by quotations use the result from `SourceInfo.fromRef` so that they are marked
|
||||
as synthetic even when the leading/trailing token is not.
|
||||
|
||||
@@ -1648,17 +1648,6 @@ If there are several with the same priority, it is uses the "most recent one". E
|
||||
-/
|
||||
syntax (name := simp) "simp" (Tactic.simpPre <|> Tactic.simpPost)? patternIgnore("← " <|> "<- ")? (ppSpace prio)? : attr
|
||||
|
||||
/--
|
||||
Theorems tagged with the `grind_norm` attribute are used by the `grind` tactic normalizer/pre-processor.
|
||||
-/
|
||||
syntax (name := grind_norm) "grind_norm" (Tactic.simpPre <|> Tactic.simpPost)? (ppSpace prio)? : attr
|
||||
|
||||
/--
|
||||
Simplification procedures tagged with the `grind_norm_proc` attribute are used by the `grind` tactic normalizer/pre-processor.
|
||||
-/
|
||||
syntax (name := grind_norm_proc) "grind_norm_proc" (Tactic.simpPre <|> Tactic.simpPost)? : attr
|
||||
|
||||
|
||||
/-- The possible `norm_cast` kinds: `elim`, `move`, or `squash`. -/
|
||||
syntax normCastLabel := &"elim" <|> &"move" <|> &"squash"
|
||||
|
||||
|
||||
@@ -14,26 +14,85 @@ register_builtin_option debug.skipKernelTC : Bool := {
|
||||
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"
|
||||
}
|
||||
|
||||
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment :=
|
||||
/-- 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 :=
|
||||
if debug.skipKernelTC.get opts then
|
||||
addDeclWithoutChecking env decl
|
||||
else
|
||||
addDeclCore env (Core.getMaxHeartbeats opts).toUSize decl cancelTk?
|
||||
|
||||
def Environment.addAndCompile (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment := do
|
||||
let env ← addDecl env opts decl cancelTk?
|
||||
compileDecl env opts decl
|
||||
private def Environment.addDeclAux (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except Kernel.Exception Environment :=
|
||||
env.addDeclCore (Core.getMaxHeartbeats opts).toUSize decl cancelTk? (!debug.skipKernelTC.get opts)
|
||||
|
||||
@[deprecated "use `Lean.addDecl` instead to ensure new namespaces are registered" (since := "2024-12-03")]
|
||||
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
|
||||
(cancelTk? : Option IO.CancelToken := none) : Except Kernel.Exception Environment :=
|
||||
Environment.addDeclAux env opts decl cancelTk?
|
||||
|
||||
private def isNamespaceName : Name → Bool
|
||||
| .str .anonymous _ => true
|
||||
| .str p _ => isNamespaceName p
|
||||
| _ => false
|
||||
|
||||
private def registerNamePrefixes (env : Environment) (name : Name) : Environment :=
|
||||
match name with
|
||||
| .str _ s =>
|
||||
if s.get 0 == '_' then
|
||||
-- Do not register namespaces that only contain internal declarations.
|
||||
env
|
||||
else
|
||||
go env name
|
||||
| _ => env
|
||||
where go env
|
||||
| .str p _ => if isNamespaceName p then go (env.registerNamespace p) p else env
|
||||
| _ => env
|
||||
|
||||
def addDecl (decl : Declaration) : CoreM Unit := do
|
||||
let mut env ← getEnv
|
||||
-- register namespaces for newly added constants; this used to be done by the kernel itself
|
||||
-- but that is incompatible with moving it to a separate task
|
||||
env := decl.getNames.foldl registerNamePrefixes env
|
||||
if let .inductDecl _ _ types _ := decl then
|
||||
env := types.foldl (registerNamePrefixes · <| ·.name ++ `rec) env
|
||||
|
||||
if !Elab.async.get (← getOptions) then
|
||||
setEnv env
|
||||
return (← doAdd)
|
||||
|
||||
-- convert `Declaration` to `ConstantInfo` to use as a preliminary value in the environment until
|
||||
-- kernel checking has finished; not all cases are supported yet
|
||||
let (name, info, kind) ← match decl with
|
||||
| .thmDecl thm => pure (thm.name, .thmInfo thm, .thm)
|
||||
| .defnDecl defn => pure (defn.name, .defnInfo defn, .defn)
|
||||
| .mutualDefnDecl [defn] => pure (defn.name, .defnInfo defn, .defn)
|
||||
| _ => return (← doAdd)
|
||||
|
||||
-- no environment extension changes to report after kernel checking; ensures we do not
|
||||
-- accidentally wait for this snapshot when querying extension states
|
||||
let async ← env.addConstAsync (reportExts := false) name kind
|
||||
-- report preliminary constant info immediately
|
||||
async.commitConst async.asyncEnv (some info)
|
||||
setEnv async.mainEnv
|
||||
let checkAct ← Core.wrapAsyncAsSnapshot fun _ => do
|
||||
try
|
||||
setEnv async.asyncEnv
|
||||
doAdd
|
||||
async.commitCheckEnv (← getEnv)
|
||||
finally
|
||||
async.commitFailure
|
||||
let t ← BaseIO.mapTask (fun _ => checkAct) env.checked
|
||||
let endRange? := (← getRef).getTailPos?.map fun pos => ⟨pos, pos⟩
|
||||
Core.logSnapshotTask { range? := endRange?, task := t }
|
||||
where doAdd := do
|
||||
profileitM Exception "type checking" (← getOptions) do
|
||||
withTraceNode `Kernel (fun _ => return m!"typechecking declaration") do
|
||||
withTraceNode `Kernel (fun _ => return m!"typechecking declarations {decl.getNames}") do
|
||||
if !(← MonadLog.hasErrors) && decl.hasSorry then
|
||||
logWarning "declaration uses 'sorry'"
|
||||
match (← getEnv).addDecl (← getOptions) decl (← read).cancelTk? with
|
||||
| .ok env => setEnv env
|
||||
| .error ex => throwKernelException ex
|
||||
logWarning m!"declaration uses 'sorry'"
|
||||
let env ← (← getEnv).addDeclAux (← getOptions) decl (← read).cancelTk?
|
||||
|> ofExceptKernelException
|
||||
setEnv env
|
||||
|
||||
def addAndCompile (decl : Declaration) : CoreM Unit := do
|
||||
addDecl decl
|
||||
|
||||
@@ -144,11 +144,7 @@ def declareBuiltin (forDecl : Name) (value : Expr) : CoreM Unit := do
|
||||
let type := mkApp (mkConst `IO) (mkConst `Unit)
|
||||
let decl := Declaration.defnDecl { name, levelParams := [], type, value, hints := ReducibilityHints.opaque,
|
||||
safety := DefinitionSafety.safe }
|
||||
match (← getEnv).addAndCompile {} decl with
|
||||
-- TODO: pretty print error
|
||||
| Except.error e => do
|
||||
let msg ← (e.toMessageData {}).toString
|
||||
throwError "failed to emit registration code for builtin '{forDecl}': {msg}"
|
||||
| Except.ok env => IO.ofExcept (setBuiltinInitAttr env name) >>= setEnv
|
||||
addAndCompile decl
|
||||
IO.ofExcept (setBuiltinInitAttr (← getEnv) name) >>= setEnv
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -33,6 +33,7 @@ def shouldGenerateCode (declName : Name) : CoreM Bool := do
|
||||
let some info ← getDeclInfo? declName | return false
|
||||
unless info.hasValue (allowOpaque := true) do return false
|
||||
if hasMacroInlineAttribute env declName then return false
|
||||
if (getImplementedBy? env declName).isSome then return false
|
||||
if (← Meta.isMatcher declName) then return false
|
||||
if isCasesOnRecursor env declName then return false
|
||||
-- TODO: check if type class instance
|
||||
|
||||
@@ -72,23 +72,23 @@ The type contains only `→` and constants.
|
||||
-/
|
||||
partial def toMonoType (type : Expr) : CoreM Expr := do
|
||||
let type := type.headBeta
|
||||
if type.isErased then
|
||||
return erasedExpr
|
||||
else if type.isErased then
|
||||
return erasedExpr
|
||||
else if isTypeFormerType type then
|
||||
return erasedExpr
|
||||
else match type with
|
||||
| .const .. => visitApp type #[]
|
||||
| .app .. => type.withApp visitApp
|
||||
| .forallE _ d b _ => mkArrow (← toMonoType d) (← toMonoType (b.instantiate1 erasedExpr))
|
||||
| _ => return erasedExpr
|
||||
match type with
|
||||
| .const .. => visitApp type #[]
|
||||
| .app .. => type.withApp visitApp
|
||||
| .forallE _ d b _ =>
|
||||
let monoB ← toMonoType (b.instantiate1 anyExpr)
|
||||
match monoB with
|
||||
| .const ``lcErased _ => return erasedExpr
|
||||
| _ => mkArrow (← toMonoType d) monoB
|
||||
| .sort _ => return erasedExpr
|
||||
| _ => return anyExpr
|
||||
where
|
||||
visitApp (f : Expr) (args : Array Expr) : CoreM Expr := do
|
||||
match f with
|
||||
| .const ``lcErased _ => return erasedExpr
|
||||
| .const ``lcAny _ => return anyExpr
|
||||
| .const ``Decidable _ => return mkConst ``Bool
|
||||
| .const declName us =>
|
||||
if declName == ``Decidable then
|
||||
return mkConst ``Bool
|
||||
if let some info ← hasTrivialStructure? declName then
|
||||
let ctorType ← getOtherDeclBaseType info.ctorName []
|
||||
toMonoType (getParamTypes (← instantiateForall ctorType args[:info.numParams]))[info.fieldIdx]!
|
||||
@@ -98,15 +98,13 @@ where
|
||||
for arg in args do
|
||||
let .forallE _ d b _ := type.headBeta | unreachable!
|
||||
let arg := arg.headBeta
|
||||
if arg.isErased then
|
||||
result := mkApp result arg
|
||||
else if d.isErased || d matches .sort _ then
|
||||
if d matches .const ``lcErased _ | .sort _ then
|
||||
result := mkApp result (← toMonoType arg)
|
||||
else
|
||||
result := mkApp result erasedExpr
|
||||
type := b.instantiate1 arg
|
||||
return result
|
||||
| _ => return erasedExpr
|
||||
| _ => return anyExpr
|
||||
|
||||
/--
|
||||
State for the environment extension used to save the LCNF mono phase type for declarations
|
||||
|
||||
@@ -81,7 +81,10 @@ def ppLetDecl (letDecl : LetDecl) : M Format := do
|
||||
return f!"let {letDecl.binderName} := {← ppLetValue letDecl.value}"
|
||||
|
||||
def getFunType (ps : Array Param) (type : Expr) : CoreM Expr :=
|
||||
instantiateForall type (ps.map (mkFVar ·.fvarId))
|
||||
if type.isErased then
|
||||
pure type
|
||||
else
|
||||
instantiateForall type (ps.map (mkFVar ·.fvarId))
|
||||
|
||||
mutual
|
||||
partial def ppFunDecl (funDecl : FunDecl) : M Format := do
|
||||
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Lean.ProjFns
|
||||
import Lean.Meta.CtorRecognizer
|
||||
import Lean.Compiler.BorrowedAnnotation
|
||||
import Lean.Compiler.CSimpAttr
|
||||
import Lean.Compiler.LCNF.Types
|
||||
import Lean.Compiler.LCNF.Bind
|
||||
import Lean.Compiler.LCNF.InferType
|
||||
@@ -472,7 +473,7 @@ where
|
||||
|
||||
/-- Giving `f` a constant `.const declName us`, convert `args` into `args'`, and return `.const declName us args'` -/
|
||||
visitAppDefaultConst (f : Expr) (args : Array Expr) : M Arg := do
|
||||
let .const declName us := f | unreachable!
|
||||
let .const declName us := CSimp.replaceConstants (← getEnv) f | unreachable!
|
||||
let args ← args.mapM visitAppArg
|
||||
letValueToArg <| .const declName us args
|
||||
|
||||
@@ -670,7 +671,7 @@ where
|
||||
visitApp (e : Expr) : M Arg := do
|
||||
if let some (args, n, t, v, b) := e.letFunAppArgs? then
|
||||
visitCore <| mkAppN (.letE n t v b (nonDep := true)) args
|
||||
else if let .const declName _ := e.getAppFn then
|
||||
else if let .const declName _ := CSimp.replaceConstants (← getEnv) e.getAppFn then
|
||||
if declName == ``Quot.lift then
|
||||
visitQuotLift e
|
||||
else if declName == ``Quot.mk then
|
||||
|
||||
@@ -150,18 +150,7 @@ where
|
||||
|
||||
def toMono : Pass where
|
||||
name := `toMono
|
||||
run := fun decls => do
|
||||
let decls ← decls.filterM fun decl => do
|
||||
if hasLocalInst decl.type then
|
||||
/-
|
||||
Declaration is a "template" for the code specialization pass.
|
||||
So, we should delete it before going to next phase.
|
||||
-/
|
||||
decl.erase
|
||||
return false
|
||||
else
|
||||
return true
|
||||
decls.mapM (·.toMono)
|
||||
run := (·.mapM (·.toMono))
|
||||
phase := .base
|
||||
phaseOut := .mono
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ scoped notation:max "◾" => lcErased
|
||||
namespace LCNF
|
||||
|
||||
def erasedExpr := mkConst ``lcErased
|
||||
def anyExpr := mkConst ``lcAny
|
||||
|
||||
def _root_.Lean.Expr.isErased (e : Expr) :=
|
||||
e.isAppOf ``lcErased
|
||||
|
||||
@@ -53,18 +53,3 @@ def isUnsafeRecName? : Name → Option Name
|
||||
| _ => none
|
||||
|
||||
end Compiler
|
||||
|
||||
namespace Environment
|
||||
|
||||
/--
|
||||
Compile the given block of mutual declarations.
|
||||
Assumes the declarations have already been added to the environment using `addDecl`.
|
||||
-/
|
||||
@[extern "lean_compile_decls"]
|
||||
opaque compileDecls (env : Environment) (opt : @& Options) (decls : @& List Name) : Except KernelException Environment
|
||||
|
||||
/-- Compile the given declaration, it assumes the declaration has already been added to the environment using `addDecl`. -/
|
||||
def compileDecl (env : Environment) (opt : @& Options) (decl : @& Declaration) : Except KernelException Environment :=
|
||||
compileDecls env opt (Compiler.getDeclNamesForCodeGen decl)
|
||||
|
||||
end Environment
|
||||
|
||||
@@ -36,7 +36,7 @@ register_builtin_option Elab.async : Bool := {
|
||||
descr := "perform elaboration using multiple threads where possible\
|
||||
\n\
|
||||
\nThis option defaults to `false` but (when not explicitly set) is overridden to `true` in \
|
||||
`Lean.Language.Lean.process` as used by the cmdline driver and language server. \
|
||||
the language server. \
|
||||
Metaprogramming users driving elaboration directly via e.g. \
|
||||
`Lean.Elab.Command.elabCommandTopLevel` can opt into asynchronous elaboration by setting \
|
||||
this option but then are responsible for processing messages and other data not only in the \
|
||||
@@ -423,7 +423,11 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (desc : String := by exact d
|
||||
IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get (← getOptions)) do
|
||||
let tid ← IO.getTID
|
||||
-- reset trace state and message log so as not to report them twice
|
||||
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
|
||||
modify fun st => { st with
|
||||
messages := st.messages.markAllReported
|
||||
traceState := { tid }
|
||||
snapshotTasks := #[]
|
||||
}
|
||||
try
|
||||
withTraceNode `Elab.async (fun _ => return desc) do
|
||||
act ()
|
||||
@@ -514,28 +518,39 @@ register_builtin_option compiler.enableNew : Bool := {
|
||||
@[extern "lean_lcnf_compile_decls"]
|
||||
opaque compileDeclsNew (declNames : List Name) : CoreM Unit
|
||||
|
||||
@[extern "lean_compile_decls"]
|
||||
opaque compileDeclsOld (env : Environment) (opt : @& Options) (decls : @& List Name) : Except Kernel.Exception Environment
|
||||
|
||||
def compileDecl (decl : Declaration) : CoreM Unit := do
|
||||
-- don't compile if kernel errored; should be converted into a task dependency when compilation
|
||||
-- is made async as well
|
||||
if !decl.getNames.all (← getEnv).constants.contains then
|
||||
return
|
||||
let opts ← getOptions
|
||||
let decls := Compiler.getDeclNamesForCodeGen decl
|
||||
if compiler.enableNew.get opts then
|
||||
compileDeclsNew decls
|
||||
let res ← withTraceNode `compiler (fun _ => return m!"compiling old: {decls}") do
|
||||
return (← getEnv).compileDecl opts decl
|
||||
return compileDeclsOld (← getEnv) opts decls
|
||||
match res with
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error (KernelException.other msg) =>
|
||||
| Except.error (.other msg) =>
|
||||
checkUnsupported decl -- Generate nicer error message for unsupported recursors and axioms
|
||||
throwError msg
|
||||
| Except.error ex =>
|
||||
throwKernelException ex
|
||||
|
||||
def compileDecls (decls : List Name) : CoreM Unit := do
|
||||
-- don't compile if kernel errored; should be converted into a task dependency when compilation
|
||||
-- is made async as well
|
||||
if !decls.all (← getEnv).constants.contains then
|
||||
return
|
||||
let opts ← getOptions
|
||||
if compiler.enableNew.get opts then
|
||||
compileDeclsNew decls
|
||||
match (← getEnv).compileDecls opts decls with
|
||||
match compileDeclsOld (← getEnv) opts decls with
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error (KernelException.other msg) =>
|
||||
| Except.error (.other msg) =>
|
||||
throwError msg
|
||||
| Except.error ex =>
|
||||
throwKernelException ex
|
||||
|
||||
@@ -24,7 +24,7 @@ abbrev empty : AssocList α β :=
|
||||
|
||||
instance : EmptyCollection (AssocList α β) := ⟨empty⟩
|
||||
|
||||
abbrev insert (m : AssocList α β) (k : α) (v : β) : AssocList α β :=
|
||||
abbrev insertNew (m : AssocList α β) (k : α) (v : β) : AssocList α β :=
|
||||
m.cons k v
|
||||
|
||||
def isEmpty : AssocList α β → Bool
|
||||
@@ -77,6 +77,12 @@ def replace [BEq α] (a : α) (b : β) : AssocList α β → AssocList α β
|
||||
| true => cons a b es
|
||||
| false => cons k v (replace a b es)
|
||||
|
||||
def insert [BEq α] (m : AssocList α β) (k : α) (v : β) : AssocList α β :=
|
||||
if m.contains k then
|
||||
m.replace k v
|
||||
else
|
||||
m.insertNew k v
|
||||
|
||||
def erase [BEq α] (a : α) : AssocList α β → AssocList α β
|
||||
| nil => nil
|
||||
| cons k v es => match k == a with
|
||||
|
||||
@@ -54,6 +54,10 @@ instance : EmptyCollection (NameTrie β) where
|
||||
def NameTrie.find? (t : NameTrie β) (k : Name) : Option β :=
|
||||
PrefixTree.find? t (toKey k)
|
||||
|
||||
@[inline, inherit_doc PrefixTree.findLongestPrefix?]
|
||||
def NameTrie.findLongestPrefix? (t : NameTrie β) (k : Name) : Option β :=
|
||||
PrefixTree.findLongestPrefix? t (toKey k)
|
||||
|
||||
@[inline]
|
||||
def NameTrie.foldMatchingM [Monad m] (t : NameTrie β) (k : Name) (init : σ) (f : β → σ → m σ) : m σ :=
|
||||
PrefixTree.foldMatchingM t (toKey k) init f
|
||||
|
||||
@@ -48,6 +48,17 @@ partial def find? (t : PrefixTreeNode α β) (cmp : α → α → Ordering) (k :
|
||||
| some t => loop t ks
|
||||
loop t k
|
||||
|
||||
/-- Returns the the value of the longest key in `t` that is a prefix of `k`, if any. -/
|
||||
@[specialize]
|
||||
partial def findLongestPrefix? (t : PrefixTreeNode α β) (cmp : α → α → Ordering) (k : List α) : Option β :=
|
||||
let rec loop acc?
|
||||
| PrefixTreeNode.Node val _, [] => val <|> acc?
|
||||
| PrefixTreeNode.Node val m, k :: ks =>
|
||||
match RBNode.find cmp m k with
|
||||
| none => val
|
||||
| some t => loop (val <|> acc?) t ks
|
||||
loop none t k
|
||||
|
||||
@[specialize]
|
||||
partial def foldMatchingM [Monad m] (t : PrefixTreeNode α β) (cmp : α → α → Ordering) (k : List α) (init : σ) (f : β → σ → m σ) : m σ :=
|
||||
let rec fold : PrefixTreeNode α β → σ → m σ
|
||||
@@ -92,6 +103,10 @@ def PrefixTree.insert (t : PrefixTree α β p) (k : List α) (v : β) : PrefixTr
|
||||
def PrefixTree.find? (t : PrefixTree α β p) (k : List α) : Option β :=
|
||||
t.val.find? p k
|
||||
|
||||
@[inline, inherit_doc PrefixTreeNode.findLongestPrefix?]
|
||||
def PrefixTree.findLongestPrefix? (t : PrefixTree α β p) (k : List α) : Option β :=
|
||||
t.val.findLongestPrefix? p k
|
||||
|
||||
@[inline]
|
||||
def PrefixTree.foldMatchingM [Monad m] (t : PrefixTree α β p) (k : List α) (init : σ) (f : β → σ → m σ) : m σ :=
|
||||
t.val.foldMatchingM p k init f
|
||||
|
||||
@@ -193,6 +193,19 @@ def Declaration.definitionVal! : Declaration → DefinitionVal
|
||||
| .defnDecl val => val
|
||||
| _ => panic! "Expected a `Declaration.defnDecl`."
|
||||
|
||||
/--
|
||||
Returns all top-level names to be defined by adding this declaration to the environment. This does
|
||||
not include auxiliary definitions such as projections.
|
||||
-/
|
||||
def Declaration.getNames : Declaration → List Name
|
||||
| .axiomDecl val => [val.name]
|
||||
| .defnDecl val => [val.name]
|
||||
| .thmDecl val => [val.name]
|
||||
| .opaqueDecl val => [val.name]
|
||||
| .quotDecl => [``Quot, ``Quot.mk, ``Quot.lift, ``Quot.ind]
|
||||
| .mutualDefnDecl defns => defns.map (·.name)
|
||||
| .inductDecl _ _ types _ => types.map (·.name)
|
||||
|
||||
@[specialize] def Declaration.foldExprM {α} {m : Type → Type} [Monad m] (d : Declaration) (f : α → Expr → m α) (a : α) : m α :=
|
||||
match d with
|
||||
| .quotDecl => pure a
|
||||
@@ -469,6 +482,10 @@ def isInductive : ConstantInfo → Bool
|
||||
| .inductInfo _ => true
|
||||
| _ => false
|
||||
|
||||
def isDefinition : ConstantInfo → Bool
|
||||
| .defnInfo _ => true
|
||||
| _ => false
|
||||
|
||||
def isTheorem : ConstantInfo → Bool
|
||||
| .thmInfo _ => true
|
||||
| _ => false
|
||||
|
||||
@@ -1474,7 +1474,7 @@ where
|
||||
| field::fields, false => .fieldName field field.getId.getString! none fIdent :: toLVals fields false
|
||||
|
||||
/-- Resolve `(.$id:ident)` using the expected type to infer namespace. -/
|
||||
private partial def resolveDotName (id : Syntax) (expectedType? : Option Expr) : TermElabM Name := do
|
||||
private partial def resolveDotName (id : Syntax) (expectedType? : Option Expr) : TermElabM Expr := do
|
||||
tryPostponeIfNoneOrMVar expectedType?
|
||||
let some expectedType := expectedType?
|
||||
| throwError "invalid dotted identifier notation, expected type must be known"
|
||||
@@ -1489,7 +1489,7 @@ where
|
||||
withForallBody body k
|
||||
else
|
||||
k body
|
||||
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM Name := do
|
||||
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM Expr := do
|
||||
let resultType ← instantiateMVars resultType
|
||||
let resultTypeFn := resultType.cleanupAnnotations.getAppFn
|
||||
try
|
||||
@@ -1497,9 +1497,12 @@ where
|
||||
let .const declName .. := resultTypeFn.cleanupAnnotations
|
||||
| throwError "invalid dotted identifier notation, expected type is not of the form (... → C ...) where C is a constant{indentExpr expectedType}"
|
||||
let idNew := declName ++ id.getId.eraseMacroScopes
|
||||
unless (← getEnv).contains idNew do
|
||||
if (← getEnv).contains idNew then
|
||||
mkConst idNew
|
||||
else if let some (fvar, []) ← resolveLocalName idNew then
|
||||
return fvar
|
||||
else
|
||||
throwError "invalid dotted identifier notation, unknown identifier `{idNew}` from expected type{indentExpr expectedType}"
|
||||
return idNew
|
||||
catch
|
||||
| ex@(.error ..) =>
|
||||
match (← unfoldDefinition? resultType) with
|
||||
@@ -1548,7 +1551,7 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
|
||||
| `(_) => throwError "placeholders '_' cannot be used where a function is expected"
|
||||
| `(.$id:ident) =>
|
||||
addCompletionInfo <| CompletionInfo.dotId f id.getId (← getLCtx) expectedType?
|
||||
let fConst ← mkConst (← resolveDotName id expectedType?)
|
||||
let fConst ← resolveDotName id expectedType?
|
||||
let s ← observing do
|
||||
-- Use (force := true) because we want to record the result of .ident resolution even in patterns
|
||||
let fConst ← addTermInfo f fConst expectedType? (force := true)
|
||||
|
||||
@@ -124,9 +124,7 @@ private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM
|
||||
n[1].forArgsM addUnivLevel
|
||||
|
||||
@[builtin_command_elab «init_quot»] def elabInitQuot : CommandElab := fun _ => do
|
||||
match (← getEnv).addDecl (← getOptions) Declaration.quotDecl with
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error ex => throwError (ex.toMessageData (← getOptions))
|
||||
liftCoreM <| addDecl Declaration.quotDecl
|
||||
|
||||
@[builtin_command_elab «export»] def elabExport : CommandElab := fun stx => do
|
||||
let `(export $ns ($ids*)) := stx | throwUnsupportedSyntax
|
||||
@@ -294,7 +292,7 @@ def failIfSucceeds (x : CommandElabM Unit) : CommandElabM Unit := do
|
||||
modify fun s => { s with messages := {} };
|
||||
pure messages
|
||||
let restoreMessages (prevMessages : MessageLog) : CommandElabM Unit := do
|
||||
modify fun s => { s with messages := prevMessages ++ s.messages.errorsToWarnings }
|
||||
modify fun s => { s with messages := prevMessages ++ s.messages.errorsToInfos }
|
||||
let prevMessages ← resetMessages
|
||||
let succeeded ← try
|
||||
x
|
||||
|
||||
@@ -313,7 +313,11 @@ def wrapAsyncAsSnapshot (act : Unit → CommandElabM Unit)
|
||||
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get (← getOptions)) do
|
||||
let tid ← IO.getTID
|
||||
-- reset trace state and message log so as not to report them twice
|
||||
modify fun st => { st with messages := st.messages.markAllReported, traceState := { tid } }
|
||||
modify fun st => { st with
|
||||
messages := st.messages.markAllReported
|
||||
traceState := { tid }
|
||||
snapshotTasks := #[]
|
||||
}
|
||||
try
|
||||
withTraceNode `Elab.async (fun _ => return desc) do
|
||||
act ()
|
||||
|
||||
@@ -5,7 +5,7 @@ Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Parser.Module
|
||||
import Lean.Data.Json
|
||||
import Lean.Util.Paths
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
@@ -42,4 +42,12 @@ def printImports (input : String) (fileName : Option String) : IO Unit := do
|
||||
let fname ← findOLean dep.module
|
||||
IO.println fname
|
||||
|
||||
@[export lean_print_import_srcs]
|
||||
def printImportSrcs (input : String) (fileName : Option String) : IO Unit := do
|
||||
let sp ← initSrcSearchPath
|
||||
let (deps, _, _) ← parseImports input fileName
|
||||
for dep in deps do
|
||||
let fname ← findLean sp dep.module
|
||||
IO.println fname
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
@@ -9,6 +9,7 @@ import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Structural
|
||||
import Lean.Elab.PreDefinition.WF.Main
|
||||
import Lean.Elab.PreDefinition.MkInhabitant
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint
|
||||
|
||||
namespace Lean.Elab
|
||||
open Meta
|
||||
@@ -162,7 +163,8 @@ def ensureFunIndReservedNamesAvailable (preDefs : Array PreDefinition) : MetaM U
|
||||
Checks consistency of a clique of TerminationHints:
|
||||
|
||||
* If not all have a hint, the hints are ignored (log error)
|
||||
* If one has `structural`, check that all have it, (else throw error)
|
||||
* None have both `termination_by` and `nontermination` (throw error)
|
||||
* If one has `structural` or `partialFixpoint`, check that all have it (else throw error)
|
||||
* A `structural` should not have a `decreasing_by` (else log error)
|
||||
|
||||
-/
|
||||
@@ -171,21 +173,26 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
let preDefsWithout := preDefs.filter (·.termination.terminationBy?.isNone)
|
||||
let structural :=
|
||||
preDefWith.termination.terminationBy? matches some {structural := true, ..}
|
||||
let partialFixpoint := preDefWith.termination.partialFixpoint?.isSome
|
||||
for preDef in preDefs do
|
||||
if let .some termBy := preDef.termination.terminationBy? then
|
||||
if !structural && !preDefsWithout.isEmpty then
|
||||
if let .some partialFixpointStx := preDef.termination.partialFixpoint? then
|
||||
throwErrorAt partialFixpointStx.ref m!"conflicting annotations: this function cannot \
|
||||
be both terminating and a partial fixpoint"
|
||||
|
||||
if !structural && !partialFixpoint && !preDefsWithout.isEmpty then
|
||||
let m := MessageData.andList (preDefsWithout.toList.map (m!"{·.declName}"))
|
||||
let doOrDoes := if preDefsWithout.size = 1 then "does" else "do"
|
||||
logErrorAt termBy.ref (m!"incomplete set of `termination_by` annotations:\n"++
|
||||
m!"This function is mutually with {m}, which {doOrDoes} not have " ++
|
||||
m!"This function is mutually recursive with {m}, which {doOrDoes} not have " ++
|
||||
m!"a `termination_by` clause.\n" ++
|
||||
m!"The present clause is ignored.")
|
||||
|
||||
if structural && ! termBy.structural then
|
||||
if structural && !termBy.structural then
|
||||
throwErrorAt termBy.ref (m!"Invalid `termination_by`; this function is mutually " ++
|
||||
m!"recursive with {preDefWith.declName}, which is marked as `termination_by " ++
|
||||
m!"structural` so this one also needs to be marked `structural`.")
|
||||
if ! structural && termBy.structural then
|
||||
if !structural && termBy.structural then
|
||||
throwErrorAt termBy.ref (m!"Invalid `termination_by`; this function is mutually " ++
|
||||
m!"recursive with {preDefWith.declName}, which is not marked as `structural` " ++
|
||||
m!"so this one cannot be `structural` either.")
|
||||
@@ -194,20 +201,41 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
logErrorAt decr.ref (m!"Invalid `decreasing_by`; this function is marked as " ++
|
||||
m!"structurally recursive, so no explicit termination proof is needed.")
|
||||
|
||||
if partialFixpoint && preDef.termination.partialFixpoint?.isNone then
|
||||
throwErrorAt preDef.ref (m!"Invalid `termination_by`; this function is mutually " ++
|
||||
m!"recursive with {preDefWith.declName}, which is marked as " ++
|
||||
m!"`nontermination_partialFixpointursive` so this one also needs to be marked " ++
|
||||
m!"`nontermination_partialFixpointursive`.")
|
||||
|
||||
if preDef.termination.partialFixpoint?.isSome then
|
||||
if let .some decr := preDef.termination.decreasingBy? then
|
||||
logErrorAt decr.ref (m!"Invalid `decreasing_by`; this function is marked as " ++
|
||||
m!"nonterminating, so no explicit termination proof is needed.")
|
||||
|
||||
if !partialFixpoint then
|
||||
if let some stx := preDef.termination.partialFixpoint? then
|
||||
throwErrorAt stx.ref (m!"Invalid `termination_by`; this function is mutually " ++
|
||||
m!"recursive with {preDefWith.declName}, which is not also marked as " ++
|
||||
m!"`nontermination_partialFixpointursive`, so this one cannot be either.")
|
||||
|
||||
/--
|
||||
Elaborates the `TerminationHint` in the clique to `TerminationArguments`
|
||||
Elaborates the `TerminationHint` in the clique to `TerminationMeasures`
|
||||
-/
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationArgument)) := do
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationMeasure)) := do
|
||||
preDefs.mapM fun preDef => do
|
||||
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
|
||||
let hints := preDef.termination
|
||||
hints.terminationBy?.mapM
|
||||
(TerminationArgument.elab preDef.declName preDef.type arity hints.extraParams ·)
|
||||
(TerminationMeasure.elab preDef.declName preDef.type arity hints.extraParams ·)
|
||||
|
||||
def shouldUseStructural (preDefs : Array PreDefinition) : Bool :=
|
||||
preDefs.any fun preDef =>
|
||||
preDef.termination.terminationBy? matches some {structural := true, ..}
|
||||
|
||||
def shouldUsepartialFixpoint (preDefs : Array PreDefinition) : Bool :=
|
||||
preDefs.any fun preDef =>
|
||||
preDef.termination.partialFixpoint?.isSome
|
||||
|
||||
def shouldUseWF (preDefs : Array PreDefinition) : Bool :=
|
||||
preDefs.any fun preDef =>
|
||||
preDef.termination.terminationBy? matches some {structural := false, ..} ||
|
||||
@@ -251,16 +279,18 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
|
||||
try
|
||||
checkCodomainsLevel preDefs
|
||||
checkTerminationByHints preDefs
|
||||
let termArg?s ← elabTerminationByHints preDefs
|
||||
let termMeasures?s ← elabTerminationByHints preDefs
|
||||
if shouldUseStructural preDefs then
|
||||
structuralRecursion preDefs termArg?s
|
||||
structuralRecursion preDefs termMeasures?s
|
||||
else if shouldUsepartialFixpoint preDefs then
|
||||
partialFixpoint preDefs
|
||||
else if shouldUseWF preDefs then
|
||||
wfRecursion preDefs termArg?s
|
||||
wfRecursion preDefs termMeasures?s
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs termArg?s)
|
||||
(wfRecursion preDefs termArg?s))
|
||||
(structuralRecursion preDefs termMeasures?s)
|
||||
(wfRecursion preDefs termMeasures?s))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
|
||||
92
src/Lean/Elab/PreDefinition/Mutual.lean
Normal file
92
src/Lean/Elab/PreDefinition/Mutual.lean
Normal file
@@ -0,0 +1,92 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
|
||||
/-!
|
||||
This module contains code common to mutual-via-fixedpoint constructions, i.e.
|
||||
well-founded recursion and partial fixed-points, but independent of the details of the mutual packing.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Mutual
|
||||
open Meta
|
||||
|
||||
partial def withCommonTelescope (preDefs : Array PreDefinition) (k : Array Expr → Array Expr → MetaM α) : MetaM α :=
|
||||
go #[] (preDefs.map (·.value))
|
||||
where
|
||||
go (fvars : Array Expr) (vals : Array Expr) : MetaM α := do
|
||||
if !(vals.all fun val => val.isLambda) then
|
||||
k fvars vals
|
||||
else if !(← vals.allM fun val => isDefEq val.bindingDomain! vals[0]!.bindingDomain!) then
|
||||
k fvars vals
|
||||
else
|
||||
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
|
||||
go (fvars.push x) (vals.map fun val => val.bindingBody!.instantiate1 x)
|
||||
|
||||
def getFixedPrefix (preDefs : Array PreDefinition) : MetaM Nat :=
|
||||
withCommonTelescope preDefs fun xs vals => do
|
||||
let resultRef ← IO.mkRef xs.size
|
||||
for val in vals do
|
||||
if (← resultRef.get) == 0 then return 0
|
||||
forEachExpr' val fun e => do
|
||||
if preDefs.any fun preDef => e.isAppOf preDef.declName then
|
||||
let args := e.getAppArgs
|
||||
resultRef.modify (min args.size ·)
|
||||
for arg in args, x in xs do
|
||||
if !(← withoutProofIrrelevance <| withReducible <| isDefEq arg x) then
|
||||
-- We continue searching if e's arguments are not a prefix of `xs`
|
||||
return true
|
||||
return false
|
||||
else
|
||||
return true
|
||||
resultRef.get
|
||||
|
||||
def addPreDefsFromUnary (preDefs : Array PreDefinition) (preDefsNonrec : Array PreDefinition)
|
||||
(unaryPreDefNonRec : PreDefinition) : TermElabM Unit := do
|
||||
/-
|
||||
We must remove `implemented_by` attributes from the auxiliary application because
|
||||
this attribute is only relevant for code that is compiled. Moreover, the `[implemented_by <decl>]`
|
||||
attribute would check whether the `unaryPreDef` type matches with `<decl>`'s type, and produce
|
||||
and error. See issue #2899
|
||||
-/
|
||||
let preDefNonRec := unaryPreDefNonRec.filterAttrs fun attr => attr.name != `implemented_by
|
||||
let declNames := preDefs.toList.map (·.declName)
|
||||
|
||||
-- Do not complain if the user sets @[semireducible], which usually is a noop,
|
||||
-- we recognize that below and then do not set @[irreducible]
|
||||
withOptions (allowUnsafeReducibility.set · true) do
|
||||
if unaryPreDefNonRec.declName = preDefs[0]!.declName then
|
||||
addNonRec preDefNonRec (applyAttrAfterCompilation := false)
|
||||
else
|
||||
withEnableInfoTree false do
|
||||
addNonRec preDefNonRec (applyAttrAfterCompilation := false)
|
||||
preDefsNonrec.forM (addNonRec · (applyAttrAfterCompilation := false) (all := declNames))
|
||||
|
||||
/--
|
||||
Cleans the right-hand-sides of the predefinitions, to prepare for inclusion in the EqnInfos:
|
||||
* Remove RecAppSyntax markers
|
||||
* Abstracts nested proofs (and for that, add the `_unsafe_rec` definitions)
|
||||
-/
|
||||
def cleanPreDefs (preDefs : Array PreDefinition) : TermElabM (Array PreDefinition) := do
|
||||
addAndCompilePartialRec preDefs
|
||||
let preDefs ← preDefs.mapM (eraseRecAppSyntax ·)
|
||||
let preDefs ← preDefs.mapM (abstractNestedProofs ·)
|
||||
return preDefs
|
||||
|
||||
/--
|
||||
Assign final attributes to the definitions. Assumes the EqnInfos to be already present.
|
||||
-/
|
||||
def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
markAsRecursive preDef.declName
|
||||
generateEagerEqns preDef.declName
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
-- Unless the user asks for something else, mark the definition as irreducible
|
||||
unless preDef.modifiers.attrs.any fun a =>
|
||||
a.name = `reducible || a.name = `semireducible do
|
||||
setIrreducibleAttribute preDef.declName
|
||||
|
||||
end Lean.Elab.Mutual
|
||||
9
src/Lean/Elab/PreDefinition/PartialFixpoint.lean
Normal file
9
src/Lean/Elab/PreDefinition/PartialFixpoint.lean
Normal file
@@ -0,0 +1,9 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint.Eqns
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint.Main
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint.Induction
|
||||
117
src/Lean/Elab/PreDefinition/PartialFixpoint/Eqns.lean
Normal file
117
src/Lean/Elab/PreDefinition/PartialFixpoint/Eqns.lean
Normal file
@@ -0,0 +1,117 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.Conv
|
||||
import Lean.Meta.Tactic.Rewrite
|
||||
import Lean.Meta.Tactic.Split
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Eqns
|
||||
import Lean.Meta.ArgsPacker.Basic
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Internal.Order.Basic
|
||||
|
||||
namespace Lean.Elab.PartialFixpoint
|
||||
open Meta
|
||||
open Eqns
|
||||
|
||||
structure EqnInfo extends EqnInfoCore where
|
||||
declNames : Array Name
|
||||
declNameNonRec : Name
|
||||
fixedPrefixSize : Nat
|
||||
deriving Inhabited
|
||||
|
||||
private def deltaLHSUntilFix (declName declNameNonRec : Name) (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
let target ← mvarId.getType'
|
||||
let some (_, lhs, rhs) := target.eq? | throwTacticEx `deltaLHSUntilFix mvarId "equality expected"
|
||||
let lhs' ← deltaExpand lhs fun n => n == declName || n == declNameNonRec
|
||||
mvarId.replaceTargetDefEq (← mkEq lhs' rhs)
|
||||
|
||||
partial def rwFixUnder (lhs : Expr) : MetaM Expr := do
|
||||
if lhs.isAppOfArity ``Order.fix 4 then
|
||||
return mkAppN (mkConst ``Order.fix_eq lhs.getAppFn.constLevels!) lhs.getAppArgs
|
||||
else if lhs.isApp then
|
||||
let h ← rwFixUnder lhs.appFn!
|
||||
mkAppM ``congrFun #[h, lhs.appArg!]
|
||||
else if lhs.isProj then
|
||||
let f := mkLambda `p .default (← inferType lhs.projExpr!) (lhs.updateProj! (.bvar 0))
|
||||
let h ← rwFixUnder lhs.projExpr!
|
||||
mkAppM ``congrArg #[f, h]
|
||||
else
|
||||
throwError "rwFixUnder: unexpected expression {lhs}"
|
||||
|
||||
private def rwFixEq (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
||||
let mut mvarId := mvarId
|
||||
let target ← mvarId.getType'
|
||||
let some (_, lhs, rhs) := target.eq? | unreachable!
|
||||
let h ← rwFixUnder lhs
|
||||
let some (_, _, lhsNew) := (← inferType h).eq? | unreachable!
|
||||
let targetNew ← mkEq lhsNew rhs
|
||||
let mvarNew ← mkFreshExprSyntheticOpaqueMVar targetNew
|
||||
mvarId.assign (← mkEqTrans h mvarNew)
|
||||
return mvarNew.mvarId!
|
||||
|
||||
private partial def mkProof (declName : Name) (declNameNonRec : Name) (type : Expr) : MetaM Expr := do
|
||||
trace[Elab.definition.partialFixpoint] "proving: {type}"
|
||||
withNewMCtxDepth do
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar type
|
||||
let (_, mvarId) ← main.mvarId!.intros
|
||||
let mvarId ← deltaLHSUntilFix declName declNameNonRec mvarId
|
||||
let mvarId ← rwFixEq mvarId
|
||||
if ← withAtLeastTransparency .all (tryURefl mvarId) then
|
||||
instantiateMVars main
|
||||
else
|
||||
throwError "failed to generate equational theorem for '{declName}'\n{MessageData.ofGoal mvarId}"
|
||||
|
||||
def mkEqns (declName : Name) (info : EqnInfo) : MetaM (Array Name) :=
|
||||
withOptions (tactic.hygienic.set · false) do
|
||||
let baseName := declName
|
||||
let eqnTypes ← withNewMCtxDepth <| lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
|
||||
let us := info.levelParams.map mkLevelParam
|
||||
let target ← mkEq (mkAppN (Lean.mkConst declName us) xs) body
|
||||
let goal ← mkFreshExprSyntheticOpaqueMVar target
|
||||
withReducible do
|
||||
mkEqnTypes info.declNames goal.mvarId!
|
||||
let mut thmNames := #[]
|
||||
for h : i in [: eqnTypes.size] do
|
||||
let type := eqnTypes[i]
|
||||
trace[Elab.definition.partialFixpoint] "{eqnTypes[i]}"
|
||||
let name := (Name.str baseName eqnThmSuffixBase).appendIndexAfter (i+1)
|
||||
thmNames := thmNames.push name
|
||||
let value ← mkProof declName info.declNameNonRec type
|
||||
let (type, value) ← removeUnusedEqnHypotheses type value
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name, type, value
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
return thmNames
|
||||
|
||||
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ← mkMapDeclarationExtension
|
||||
|
||||
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fixedPrefixSize : Nat) : MetaM Unit := do
|
||||
preDefs.forM fun preDef => ensureEqnReservedNamesAvailable preDef.declName
|
||||
unless preDefs.all fun p => p.kind.isTheorem do
|
||||
unless (← preDefs.allM fun p => isProp p.type) do
|
||||
let declNames := preDefs.map (·.declName)
|
||||
modifyEnv fun env =>
|
||||
preDefs.foldl (init := env) fun env preDef =>
|
||||
eqnInfoExt.insert env preDef.declName { preDef with
|
||||
declNames, declNameNonRec, fixedPrefixSize }
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if let some info := eqnInfoExt.find? (← getEnv) declName then
|
||||
mkEqns declName info
|
||||
else
|
||||
return none
|
||||
|
||||
def getUnfoldFor? (declName : Name) : MetaM (Option Name) := do
|
||||
let env ← getEnv
|
||||
Eqns.getUnfoldFor? declName fun _ => eqnInfoExt.find? env declName |>.map (·.toEqnInfoCore)
|
||||
|
||||
builtin_initialize
|
||||
registerGetEqnsFn getEqnsFor?
|
||||
registerGetUnfoldEqnFn getUnfoldFor?
|
||||
|
||||
end Lean.Elab.PartialFixpoint
|
||||
292
src/Lean/Elab/PreDefinition/PartialFixpoint/Induction.lean
Normal file
292
src/Lean/Elab/PreDefinition/PartialFixpoint/Induction.lean
Normal file
@@ -0,0 +1,292 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Lean.Meta.Basic
|
||||
import Lean.Meta.Match.MatcherApp.Transform
|
||||
import Lean.Meta.Check
|
||||
import Lean.Meta.Tactic.Subst
|
||||
import Lean.Meta.Injective -- for elimOptParam
|
||||
import Lean.Meta.ArgsPacker
|
||||
import Lean.Meta.PProdN
|
||||
import Lean.Meta.Tactic.Apply
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint.Eqns
|
||||
import Lean.Elab.Command
|
||||
import Lean.Meta.Tactic.ElimInfo
|
||||
|
||||
namespace Lean.Elab.PartialFixpoint
|
||||
|
||||
open Lean Elab Meta
|
||||
|
||||
open Lean.Order
|
||||
|
||||
def mkAdmAnd (α instα adm₁ adm₂ : Expr) : MetaM Expr :=
|
||||
mkAppOptM ``admissible_and #[α, instα, none, none, adm₁, adm₂]
|
||||
|
||||
partial def mkAdmProj (packedInst : Expr) (i : Nat) (e : Expr) : MetaM Expr := do
|
||||
if let some inst ← whnfUntil packedInst ``instCCPOPProd then
|
||||
let_expr instCCPOPProd α β instα instβ := inst | throwError "mkAdmProj: unexpected instance {inst}"
|
||||
if i == 0 then
|
||||
mkAppOptM ``admissible_pprod_fst #[α, β, instα, instβ, none, e]
|
||||
else
|
||||
let e ← mkAdmProj instβ (i - 1) e
|
||||
mkAppOptM ``admissible_pprod_snd #[α, β, instα, instβ, none, e]
|
||||
else
|
||||
assert! i == 0
|
||||
return e
|
||||
|
||||
def CCPOProdProjs (n : Nat) (inst : Expr) : Array Expr := Id.run do
|
||||
let mut insts := #[inst]
|
||||
while insts.size < n do
|
||||
let inst := insts.back!
|
||||
let_expr Lean.Order.instCCPOPProd _ _ inst₁ inst₂ := inst
|
||||
| panic! s!"isOptionFixpoint: unexpected CCPO instance {inst}"
|
||||
insts := insts.pop
|
||||
insts := insts.push inst₁
|
||||
insts := insts.push inst₂
|
||||
return insts
|
||||
|
||||
|
||||
/-- `maskArray mask xs` keeps those `x` where the corresponding entry in `mask` is `true` -/
|
||||
-- Worth having in the standard libray?
|
||||
private def maskArray {α} (mask : Array Bool) (xs : Array α) : Array α := Id.run do
|
||||
let mut ys := #[]
|
||||
for b in mask, x in xs do
|
||||
if b then ys := ys.push x
|
||||
return ys
|
||||
|
||||
/-- Appends `_1` etc to `base` unless `n == 1` -/
|
||||
private def numberNames (n : Nat) (base : String) : Array Name :=
|
||||
.ofFn (n := n) fun ⟨i, _⟩ =>
|
||||
if n == 1 then .mkSimple base else .mkSimple s!"{base}_{i+1}"
|
||||
|
||||
def deriveInduction (name : Name) : MetaM Unit := do
|
||||
mapError (f := (m!"Cannot derive fixpoint induction principle (please report this issue)\n{indentD ·}")) do
|
||||
let some eqnInfo := eqnInfoExt.find? (← getEnv) name |
|
||||
throwError "{name} is not defined by partial_fixpoint"
|
||||
|
||||
let infos ← eqnInfo.declNames.mapM getConstInfoDefn
|
||||
-- First open up the fixed parameters everywhere
|
||||
let e' ← lambdaBoundedTelescope infos[0]!.value eqnInfo.fixedPrefixSize fun xs _ => do
|
||||
-- Now look at the body of an arbitrary of the functions (they are essentially the same
|
||||
-- up to the final projections)
|
||||
let body ← instantiateLambda infos[0]!.value xs
|
||||
|
||||
-- The body should now be of the form of the form (fix … ).2.2.1
|
||||
-- We strip the projections (if present)
|
||||
let body' := PProdN.stripProjs body
|
||||
let some fixApp ← whnfUntil body' ``fix
|
||||
| throwError "Unexpected function body {body}"
|
||||
let_expr fix α instCCPOα F hmono := fixApp
|
||||
| throwError "Unexpected function body {body'}"
|
||||
|
||||
let instCCPOs := CCPOProdProjs infos.size instCCPOα
|
||||
let types ← infos.mapM (instantiateForall ·.type xs)
|
||||
let packedType ← PProdN.pack 0 types
|
||||
let motiveTypes ← types.mapM (mkArrow · (.sort 0))
|
||||
let motiveNames := numberNames motiveTypes.size "motive"
|
||||
withLocalDeclsDND (motiveNames.zip motiveTypes) fun motives => do
|
||||
let packedMotive ←
|
||||
withLocalDeclD (← mkFreshUserName `x) packedType fun x => do
|
||||
mkLambdaFVars #[x] <| ← PProdN.pack 0 <|
|
||||
motives.mapIdx fun idx motive =>
|
||||
mkApp motive (PProdN.proj motives.size idx packedType x)
|
||||
|
||||
let admTypes ← motives.mapIdxM fun i motive => do
|
||||
mkAppOptM ``admissible #[types[i]!, instCCPOs[i]!, some motive]
|
||||
let admNames := numberNames admTypes.size "adm"
|
||||
withLocalDeclsDND (admNames.zip admTypes) fun adms => do
|
||||
let adms' ← adms.mapIdxM fun i adm => mkAdmProj instCCPOα i adm
|
||||
let packedAdm ← PProdN.genMk (mkAdmAnd α instCCPOα) adms'
|
||||
let hNames := numberNames infos.size "h"
|
||||
let hTypes_hmask : Array (Expr × Array Bool) ← infos.mapIdxM fun i _info => do
|
||||
let approxNames := infos.map fun info =>
|
||||
match info.name with
|
||||
| .str _ n => .mkSimple n
|
||||
| _ => `f
|
||||
withLocalDeclsDND (approxNames.zip types) fun approxs => do
|
||||
let ihTypes := approxs.mapIdx fun j approx => mkApp motives[j]! approx
|
||||
withLocalDeclsDND (ihTypes.map (⟨`ih, ·⟩)) fun ihs => do
|
||||
let f ← PProdN.mk 0 approxs
|
||||
let Ff := F.beta #[f]
|
||||
let Ffi := PProdN.proj motives.size i packedType Ff
|
||||
let t := mkApp motives[i]! Ffi
|
||||
let t ← PProdN.reduceProjs t
|
||||
let mask := approxs.map fun approx => t.containsFVar approx.fvarId!
|
||||
let t ← mkForallFVars (maskArray mask approxs ++ maskArray mask ihs) t
|
||||
pure (t, mask)
|
||||
let (hTypes, masks) := hTypes_hmask.unzip
|
||||
withLocalDeclsDND (hNames.zip hTypes) fun hs => do
|
||||
let packedH ←
|
||||
withLocalDeclD `approx packedType fun approx =>
|
||||
let packedIHType := packedMotive.beta #[approx]
|
||||
withLocalDeclD `ih packedIHType fun ih => do
|
||||
let approxs := PProdN.projs motives.size packedType approx
|
||||
let ihs := PProdN.projs motives.size packedIHType ih
|
||||
let e ← PProdN.mk 0 <| hs.mapIdx fun i h =>
|
||||
let mask := masks[i]!
|
||||
mkAppN h (maskArray mask approxs ++ maskArray mask ihs)
|
||||
mkLambdaFVars #[approx, ih] e
|
||||
let e' ← mkAppOptM ``fix_induct #[α, instCCPOα, F, hmono, packedMotive, packedAdm, packedH]
|
||||
-- Should be the type of e', but with the function definitions folded
|
||||
let packedConclusion ← PProdN.pack 0 <| ←
|
||||
motives.mapIdxM fun i motive => do
|
||||
let f ← mkConstWithLevelParams infos[i]!.name
|
||||
return mkApp motive (mkAppN f xs)
|
||||
let e' ← mkExpectedTypeHint e' packedConclusion
|
||||
let e' ← mkLambdaFVars hs e'
|
||||
let e' ← mkLambdaFVars adms e'
|
||||
let e' ← mkLambdaFVars motives e'
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) xs e'
|
||||
let e' ← instantiateMVars e'
|
||||
trace[Elab.definition.partialFixpoint.induction] "complete body of fixpoint induction principle:{indentExpr e'}"
|
||||
pure e'
|
||||
|
||||
let eTyp ← inferType e'
|
||||
let eTyp ← elimOptParam eTyp
|
||||
-- logInfo m!"eTyp: {eTyp}"
|
||||
let params := (collectLevelParams {} eTyp).params
|
||||
-- Prune unused level parameters, preserving the original order
|
||||
let us := infos[0]!.levelParams.filter (params.contains ·)
|
||||
|
||||
let inductName := name ++ `fixpoint_induct
|
||||
addDecl <| Declaration.thmDecl
|
||||
{ name := inductName, levelParams := us, type := eTyp, value := e' }
|
||||
|
||||
def isInductName (env : Environment) (name : Name) : Bool := Id.run do
|
||||
let .str p s := name | return false
|
||||
match s with
|
||||
| "fixpoint_induct" =>
|
||||
if let some eqnInfo := eqnInfoExt.find? env p then
|
||||
return p == eqnInfo.declNames[0]!
|
||||
return false
|
||||
| _ => return false
|
||||
|
||||
builtin_initialize
|
||||
registerReservedNamePredicate isInductName
|
||||
|
||||
registerReservedNameAction fun name => do
|
||||
if isInductName (← getEnv) name then
|
||||
let .str p _ := name | return false
|
||||
MetaM.run' <| deriveInduction p
|
||||
return true
|
||||
return false
|
||||
|
||||
/--
|
||||
Returns true if `name` defined by `partial_fixpoint`, the first in its mutual group,
|
||||
and all functions are defined using the `CCPO` instance for `Option`.
|
||||
-/
|
||||
def isOptionFixpoint (env : Environment) (name : Name) : Bool := Option.isSome do
|
||||
let eqnInfo ← eqnInfoExt.find? env name
|
||||
guard <| name == eqnInfo.declNames[0]!
|
||||
let defnInfo ← env.find? eqnInfo.declNameNonRec
|
||||
assert! defnInfo.hasValue
|
||||
let mut value := defnInfo.value!
|
||||
while value.isLambda do value := value.bindingBody!
|
||||
let_expr Lean.Order.fix _ inst _ _ := value | panic! s!"isOptionFixpoint: unexpected value {value}"
|
||||
let insts := CCPOProdProjs eqnInfo.declNames.size inst
|
||||
insts.forM fun inst => do
|
||||
let mut inst := inst
|
||||
while inst.isAppOfArity ``instCCPOPi 3 do
|
||||
guard inst.appArg!.isLambda
|
||||
inst := inst.appArg!.bindingBody!
|
||||
guard <| inst.isAppOfArity ``instCCPOOption 1
|
||||
|
||||
def isPartialCorrectnessName (env : Environment) (name : Name) : Bool := Id.run do
|
||||
let .str p s := name | return false
|
||||
unless s == "partial_correctness" do return false
|
||||
return isOptionFixpoint env p
|
||||
|
||||
/--
|
||||
Given `motive : α → β → γ → Prop`, construct a proof of
|
||||
`admissible (fun f => ∀ x y r, f x y = r → motive x y r)`
|
||||
-/
|
||||
def mkOptionAdm (motive : Expr) : MetaM Expr := do
|
||||
let type ← inferType motive
|
||||
forallTelescope type fun ysr _ => do
|
||||
let P := mkAppN motive ysr
|
||||
let ys := ysr.pop
|
||||
let r := ysr.back!
|
||||
let mut inst ← mkAppM ``Option.admissible_eq_some #[P, r]
|
||||
inst ← mkLambdaFVars #[r] inst
|
||||
inst ← mkAppOptM ``admissible_pi #[none, none, none, none, inst]
|
||||
for y in ys.reverse do
|
||||
inst ← mkLambdaFVars #[y] inst
|
||||
inst ← mkAppOptM ``admissible_pi_apply #[none, none, none, none, inst]
|
||||
pure inst
|
||||
|
||||
def derivePartialCorrectness (name : Name) : MetaM Unit := do
|
||||
let fixpointInductThm := name ++ `fixpoint_induct
|
||||
unless (← getEnv).contains fixpointInductThm do
|
||||
deriveInduction name
|
||||
|
||||
mapError (f := (m!"Cannot derive partial correctness theorem (please report this issue)\n{indentD ·}")) do
|
||||
let some eqnInfo := eqnInfoExt.find? (← getEnv) name |
|
||||
throwError "{name} is not defined by partial_fixpoint"
|
||||
|
||||
let infos ← eqnInfo.declNames.mapM getConstInfoDefn
|
||||
-- First open up the fixed parameters everywhere
|
||||
let e' ← lambdaBoundedTelescope infos[0]!.value eqnInfo.fixedPrefixSize fun xs _ => do
|
||||
let types ← infos.mapM (instantiateForall ·.type xs)
|
||||
|
||||
-- for `f : α → β → Option γ`, we expect a `motive : α → β → γ → Prop`
|
||||
let motiveTypes ← types.mapM fun type =>
|
||||
forallTelescopeReducing type fun ys type => do
|
||||
let type ← whnf type
|
||||
let_expr Option γ := type | throwError "Expected `Option`, got:{indentExpr type}"
|
||||
withLocalDeclD (← mkFreshUserName `r) γ fun r =>
|
||||
mkForallFVars (ys.push r) (.sort 0)
|
||||
let motiveDecls ← motiveTypes.mapIdxM fun i motiveType => do
|
||||
let n := if infos.size = 1 then .mkSimple "motive"
|
||||
else .mkSimple s!"motive_{i+1}"
|
||||
pure (n, fun _ => pure motiveType)
|
||||
withLocalDeclsD motiveDecls fun motives => do
|
||||
-- the motives, as expected by `f.fixpoint_induct`:
|
||||
-- fun f => ∀ x y r, f x y = some r → motive x y r
|
||||
let motives' ← motives.mapIdxM fun i motive => do
|
||||
withLocalDeclD (← mkFreshUserName `f) types[i]! fun f => do
|
||||
forallTelescope (← inferType motive) fun ysr _ => do
|
||||
let ys := ysr.pop
|
||||
let r := ysr.back!
|
||||
let heq ← mkEq (mkAppN f ys) (← mkAppM ``some #[r])
|
||||
let motive' ← mkArrow heq (mkAppN motive ysr)
|
||||
let motive' ← mkForallFVars ysr motive'
|
||||
mkLambdaFVars #[f] motive'
|
||||
|
||||
let e' ← mkAppOptM fixpointInductThm <| (xs ++ motives').map some
|
||||
let adms ← motives.mapM mkOptionAdm
|
||||
let e' := mkAppN e' adms
|
||||
let e' ← mkLambdaFVars motives e'
|
||||
let e' ← mkLambdaFVars (binderInfoForMVars := .default) (usedOnly := true) xs e'
|
||||
let e' ← instantiateMVars e'
|
||||
trace[Elab.definition.partialFixpoint.induction] "complete body of partial correctness principle:{indentExpr e'}"
|
||||
pure e'
|
||||
|
||||
let eTyp ← inferType e'
|
||||
let eTyp ← elimOptParam eTyp
|
||||
let eTyp ← Core.betaReduce eTyp
|
||||
-- logInfo m!"eTyp: {eTyp}"
|
||||
let params := (collectLevelParams {} eTyp).params
|
||||
-- Prune unused level parameters, preserving the original order
|
||||
let us := infos[0]!.levelParams.filter (params.contains ·)
|
||||
|
||||
let inductName := name ++ `partial_correctness
|
||||
addDecl <| Declaration.thmDecl
|
||||
{ name := inductName, levelParams := us, type := eTyp, value := e' }
|
||||
|
||||
builtin_initialize
|
||||
registerReservedNamePredicate isPartialCorrectnessName
|
||||
|
||||
registerReservedNameAction fun name => do
|
||||
let .str p s := name | return false
|
||||
unless s == "partial_correctness" do return false
|
||||
unless isOptionFixpoint (← getEnv) p do return false
|
||||
MetaM.run' <| derivePartialCorrectness p
|
||||
return false
|
||||
|
||||
end Lean.Elab.PartialFixpoint
|
||||
|
||||
builtin_initialize Lean.registerTraceClass `Elab.definition.partialFixpoint.induction
|
||||
188
src/Lean/Elab/PreDefinition/PartialFixpoint/Main.lean
Normal file
188
src/Lean/Elab/PreDefinition/PartialFixpoint/Main.lean
Normal file
@@ -0,0 +1,188 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.MkInhabitant
|
||||
import Lean.Elab.PreDefinition.Mutual
|
||||
import Lean.Elab.PreDefinition.PartialFixpoint.Eqns
|
||||
import Lean.Elab.Tactic.Monotonicity
|
||||
import Init.Internal.Order.Basic
|
||||
import Lean.Meta.PProdN
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
open Meta
|
||||
open Monotonicity
|
||||
|
||||
open Lean.Order
|
||||
|
||||
private def replaceRecApps (recFnNames : Array Name) (fixedPrefixSize : Nat) (f : Expr) (e : Expr) : MetaM Expr := do
|
||||
let t ← inferType f
|
||||
return e.replace fun e =>
|
||||
if let some idx := recFnNames.findIdx? (e.isAppOfArity · fixedPrefixSize) then
|
||||
some <| PProdN.proj recFnNames.size idx t f
|
||||
else
|
||||
none
|
||||
|
||||
/--
|
||||
For pretty error messages:
|
||||
Takes `F : (fun f => e)`, where `f` is the packed function, and replaces `f` in `e` with the user-visible
|
||||
constants, which are added to the environment temporarily.
|
||||
-/
|
||||
private def unReplaceRecApps {α} (preDefs : Array PreDefinition) (fixedArgs : Array Expr)
|
||||
(F : Expr) (k : Expr → MetaM α) : MetaM α := do
|
||||
unless F.isLambda do throwError "Expected lambda:{indentExpr F}"
|
||||
withoutModifyingEnv do
|
||||
preDefs.forM addAsAxiom
|
||||
let fns := preDefs.map fun d =>
|
||||
mkAppN (.const d.declName (d.levelParams.map mkLevelParam)) fixedArgs
|
||||
let packedFn ← PProdN.mk 0 fns
|
||||
let e ← lambdaBoundedTelescope F 1 fun f e => do
|
||||
let f := f[0]!
|
||||
-- Replace f with calls to the constants
|
||||
let e := e.replace fun e => do if e == f then return packedFn else none
|
||||
-- And reduce projection redexes
|
||||
let e ← PProdN.reduceProjs e
|
||||
pure e
|
||||
k e
|
||||
|
||||
def mkInstCCPOPProd (inst₁ inst₂ : Expr) : MetaM Expr := do
|
||||
mkAppOptM ``instCCPOPProd #[none, none, inst₁, inst₂]
|
||||
|
||||
def mkMonoPProd (hmono₁ hmono₂ : Expr) : MetaM Expr := do
|
||||
-- mkAppM does not support the equivalent of (cfg := { synthAssignedInstances := false}),
|
||||
-- so this is a bit more pedestrian
|
||||
let_expr monotone _ inst _ inst₁ _ := (← inferType hmono₁)
|
||||
| throwError "mkMonoPProd: unexpected type of{indentExpr hmono₁}"
|
||||
let_expr monotone _ _ _ inst₂ _ := (← inferType hmono₂)
|
||||
| throwError "mkMonoPProd: unexpected type of{indentExpr hmono₂}"
|
||||
mkAppOptM ``PProd.monotone_mk #[none, none, none, inst₁, inst₂, inst, none, none, hmono₁, hmono₂]
|
||||
|
||||
def partialFixpoint (preDefs : Array PreDefinition) : TermElabM Unit := do
|
||||
-- We expect all functions in the clique to have `partial_fixpoint` syntax
|
||||
let hints := preDefs.filterMap (·.termination.partialFixpoint?)
|
||||
assert! preDefs.size = hints.size
|
||||
-- For every function of type `∀ x y, r x y`, an CCPO instance
|
||||
-- ∀ x y, CCPO (r x y), but crucially constructed using `instCCPOPi`
|
||||
let ccpoInsts ← preDefs.mapIdxM fun i preDef => withRef hints[i]!.ref do
|
||||
lambdaTelescope preDef.value fun xs _body => do
|
||||
let type ← instantiateForall preDef.type xs
|
||||
let inst ←
|
||||
try
|
||||
synthInstance (← mkAppM ``CCPO #[type])
|
||||
catch _ =>
|
||||
trace[Elab.definition.partialFixpoint] "No CCPO instance found for {preDef.declName}, trying inhabitation"
|
||||
let msg := m!"failed to compile definition '{preDef.declName}' using `partial_fixpoint`"
|
||||
let w ← mkInhabitantFor msg #[] preDef.type
|
||||
let instNonempty ← mkAppM ``Nonempty.intro #[mkAppN w xs]
|
||||
let classicalWitness ← mkAppOptM ``Classical.ofNonempty #[none, instNonempty]
|
||||
mkAppOptM ``FlatOrder.instCCPO #[none, classicalWitness]
|
||||
mkLambdaFVars xs inst
|
||||
|
||||
let fixedPrefixSize ← Mutual.getFixedPrefix preDefs
|
||||
trace[Elab.definition.partialFixpoint] "fixed prefix size: {fixedPrefixSize}"
|
||||
|
||||
let declNames := preDefs.map (·.declName)
|
||||
|
||||
forallBoundedTelescope preDefs[0]!.type fixedPrefixSize fun fixedArgs _ => do
|
||||
-- ∀ x y, CCPO (rᵢ x y)
|
||||
let ccpoInsts := ccpoInsts.map (·.beta fixedArgs)
|
||||
let types ← preDefs.mapM (instantiateForall ·.type fixedArgs)
|
||||
|
||||
-- (∀ x y, r₁ x y) ×' (∀ x y, r₂ x y)
|
||||
let packedType ← PProdN.pack 0 types
|
||||
|
||||
-- CCPO (∀ x y, rᵢ x y)
|
||||
let ccpoInsts' ← ccpoInsts.mapM fun inst =>
|
||||
lambdaTelescope inst fun xs inst => do
|
||||
let mut inst := inst
|
||||
for x in xs.reverse do
|
||||
inst ← mkAppOptM ``instCCPOPi #[(← inferType x), none, (← mkLambdaFVars #[x] inst)]
|
||||
pure inst
|
||||
-- CCPO ((∀ x y, r₁ x y) ×' (∀ x y, r₂ x y))
|
||||
let packedCCPOInst ← PProdN.genMk mkInstCCPOPProd ccpoInsts'
|
||||
-- Order ((∀ x y, r₁ x y) ×' (∀ x y, r₂ x y))
|
||||
let packedPartialOrderInst ← mkAppOptM ``CCPO.toPartialOrder #[none, packedCCPOInst]
|
||||
|
||||
-- Error reporting hook, presenting monotonicity errors in terms of recursive functions
|
||||
let failK {α} f (monoThms : Array Name) : MetaM α := do
|
||||
unReplaceRecApps preDefs fixedArgs f fun t => do
|
||||
let extraMsg := if monoThms.isEmpty then m!"" else
|
||||
m!"Tried to apply {.andList (monoThms.toList.map (m!"'{.ofConstName ·}'"))}, but failed.\n\
|
||||
Possible cause: A missing `{.ofConstName ``MonoBind}` instance.\n\
|
||||
Use `set_option trace.Elab.Tactic.monotonicity true` to debug."
|
||||
if let some recApp := t.find? hasRecAppSyntax then
|
||||
let some syn := getRecAppSyntax? recApp | panic! "getRecAppSyntax? failed"
|
||||
withRef syn <|
|
||||
throwError "Cannot eliminate recursive call `{syn}` enclosed in{indentExpr t}\n{extraMsg}"
|
||||
else
|
||||
throwError "Cannot eliminate recursive call in{indentExpr t}\n{extraMsg}"
|
||||
|
||||
-- Adjust the body of each function to take the other functions as a
|
||||
-- (packed) parameter
|
||||
let Fs ← preDefs.mapM fun preDef => do
|
||||
let body ← instantiateLambda preDef.value fixedArgs
|
||||
withLocalDeclD (← mkFreshUserName `f) packedType fun f => do
|
||||
let body' ← withoutModifyingEnv do
|
||||
-- replaceRecApps needs the constants in the env to typecheck things
|
||||
preDefs.forM (addAsAxiom ·)
|
||||
replaceRecApps declNames fixedPrefixSize f body
|
||||
mkLambdaFVars #[f] body'
|
||||
|
||||
-- Construct and solve monotonicity goals for each function separately
|
||||
-- This way we preserve the user's parameter names as much as possible
|
||||
-- and can (later) use the user-specified per-function tactic
|
||||
let hmonos ← preDefs.mapIdxM fun i preDef => do
|
||||
let type := types[i]!
|
||||
let F := Fs[i]!
|
||||
let inst ← mkAppOptM ``CCPO.toPartialOrder #[type, ccpoInsts'[i]!]
|
||||
let goal ← mkAppOptM ``monotone #[packedType, packedPartialOrderInst, type, inst, F]
|
||||
if let some term := hints[i]!.term? then
|
||||
let hmono ← Term.withSynthesize <| Term.elabTermEnsuringType term goal
|
||||
let hmono ← instantiateMVars hmono
|
||||
let mvars ← getMVars hmono
|
||||
if mvars.isEmpty then
|
||||
pure hmono
|
||||
else
|
||||
discard <| Term.logUnassignedUsingErrorInfos mvars
|
||||
mkSorry goal (synthetic := true)
|
||||
else
|
||||
let hmono ← mkFreshExprSyntheticOpaqueMVar goal
|
||||
mapError (f := (m!"Could not prove '{preDef.declName}' to be monotone in its recursive calls:{indentD ·}")) do
|
||||
solveMono failK hmono.mvarId!
|
||||
trace[Elab.definition.partialFixpoint] "monotonicity proof for {preDef.declName}: {hmono}"
|
||||
instantiateMVars hmono
|
||||
let hmono ← PProdN.genMk mkMonoPProd hmonos
|
||||
|
||||
let packedValue ← mkAppOptM ``fix #[packedType, packedCCPOInst, none, hmono]
|
||||
trace[Elab.definition.partialFixpoint] "packedValue: {packedValue}"
|
||||
|
||||
let declName :=
|
||||
if preDefs.size = 1 then
|
||||
preDefs[0]!.declName
|
||||
else
|
||||
preDefs[0]!.declName ++ `mutual
|
||||
let packedType' ← mkForallFVars fixedArgs packedType
|
||||
let packedValue' ← mkLambdaFVars fixedArgs packedValue
|
||||
let preDefNonRec := { preDefs[0]! with
|
||||
declName := declName
|
||||
type := packedType'
|
||||
value := packedValue'}
|
||||
let preDefsNonrec ← preDefs.mapIdxM fun fidx preDef => do
|
||||
let us := preDefNonRec.levelParams.map mkLevelParam
|
||||
let value := mkConst preDefNonRec.declName us
|
||||
let value := mkAppN value fixedArgs
|
||||
let value := PProdN.proj preDefs.size fidx packedType value
|
||||
let value ← mkLambdaFVars fixedArgs value
|
||||
pure { preDef with value }
|
||||
|
||||
Mutual.addPreDefsFromUnary preDefs preDefsNonrec preDefNonRec
|
||||
let preDefs ← Mutual.cleanPreDefs preDefs
|
||||
PartialFixpoint.registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize
|
||||
Mutual.addPreDefAttributes preDefs
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
builtin_initialize Lean.registerTraceClass `Elab.definition.partialFixpoint
|
||||
@@ -294,7 +294,7 @@ def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Exp
|
||||
let brecOn := mkAppN brecOn packedFArgs
|
||||
let some (size, idx) := positions.findSome? fun pos => (pos.size, ·) <$> pos.indexOf? fnIdx
|
||||
| throwError "mkBrecOnApp: Could not find {fnIdx} in {positions}"
|
||||
let brecOn ← PProdN.proj size idx brecOn
|
||||
let brecOn ← PProdN.projM size idx brecOn
|
||||
mkLambdaFVars ys (mkAppN brecOn otherArgs)
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.TerminationArgument
|
||||
import Lean.Elab.PreDefinition.TerminationMeasure
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
@@ -56,7 +56,7 @@ private def hasBadParamDep? (ys : Array Expr) (indParams : Array Expr) : MetaM (
|
||||
|
||||
/--
|
||||
Assemble the `RecArgInfo` for the `i`th parameter in the parameter list `xs`. This performs
|
||||
various sanity checks on the argument (is it even an inductive type etc).
|
||||
various sanity checks on the parameter (is it even of inductive type etc).
|
||||
-/
|
||||
def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) : MetaM RecArgInfo := do
|
||||
if h : i < xs.size then
|
||||
@@ -112,17 +112,17 @@ considered.
|
||||
|
||||
The `xs` are the fixed parameters, `value` the body with the fixed prefix instantiated.
|
||||
|
||||
Takes the optional user annotations into account (`termArg?`). If this is given and the argument
|
||||
Takes the optional user annotation into account (`termMeasure?`). If this is given and the measure
|
||||
is unsuitable, throw an error.
|
||||
-/
|
||||
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
|
||||
(termArg? : Option TerminationArgument) : MetaM (Array RecArgInfo × MessageData) := do
|
||||
(termMeasure? : Option TerminationMeasure) : MetaM (Array RecArgInfo × MessageData) := do
|
||||
lambdaTelescope value fun ys _ => do
|
||||
if let .some termArg := termArg? then
|
||||
-- User explicitly asked to use a certain argument, so throw errors eagerly
|
||||
let recArgInfo ← withRef termArg.ref do
|
||||
mapError (f := (m!"cannot use specified parameter for structural recursion:{indentD ·}")) do
|
||||
getRecArgInfo fnName xs.size (xs ++ ys) (← termArg.structuralArg)
|
||||
if let .some termMeasure := termMeasure? then
|
||||
-- User explicitly asked to use a certain measure, so throw errors eagerly
|
||||
let recArgInfo ← withRef termMeasure.ref do
|
||||
mapError (f := (m!"cannot use specified measure for structural recursion:{indentD ·}")) do
|
||||
getRecArgInfo fnName xs.size (xs ++ ys) (← termMeasure.structuralArg)
|
||||
return (#[recArgInfo], m!"")
|
||||
else
|
||||
let mut recArgInfos := #[]
|
||||
@@ -233,12 +233,12 @@ def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
|
||||
|
||||
|
||||
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
(termArg?s : Array (Option TerminationArgument)) (k : Array RecArgInfo → M α) : M α := do
|
||||
(termMeasure?s : Array (Option TerminationMeasure)) (k : Array RecArgInfo → M α) : M α := do
|
||||
let mut report := m!""
|
||||
-- Gather information on all possible recursive arguments
|
||||
let mut recArgInfoss := #[]
|
||||
for fnName in fnNames, value in values, termArg? in termArg?s do
|
||||
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termArg?
|
||||
for fnName in fnNames, value in values, termMeasure? in termMeasure?s do
|
||||
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termMeasure?
|
||||
report := report ++ thisReport
|
||||
recArgInfoss := recArgInfoss.push recArgInfos
|
||||
-- Put non-indices first
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.TerminationArgument
|
||||
import Lean.Elab.PreDefinition.TerminationMeasure
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.FindRecArg
|
||||
import Lean.Elab.PreDefinition.Structural.Preprocess
|
||||
@@ -127,7 +127,7 @@ private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr
|
||||
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
|
||||
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
|
||||
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) :
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) :
|
||||
M (Array Nat × (Array PreDefinition) × Nat) := do
|
||||
withoutModifyingEnv do
|
||||
preDefs.forM (addAsAxiom ·)
|
||||
@@ -142,7 +142,7 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (O
|
||||
assert! xs.size = maxNumFixed
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
|
||||
tryAllArgs fnNames xs values termArg?s fun recArgInfos => do
|
||||
tryAllArgs fnNames xs values termMeasure?s fun recArgInfos => do
|
||||
let recArgPoss := recArgInfos.map (·.recArgPos)
|
||||
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
|
||||
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
|
||||
@@ -156,20 +156,20 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (O
|
||||
let preDefs' ← elimMutualRecursion preDefs xs recArgInfos
|
||||
return (recArgPoss, preDefs', numFixed)
|
||||
|
||||
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
def reporttermMeasure (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
if let some ref := preDef.termination.terminationBy?? then
|
||||
let fn ← lambdaTelescope preDef.value fun xs _ => mkLambdaFVars xs xs[recArgPos]!
|
||||
let termArg : TerminationArgument:= {ref := .missing, structural := true, fn}
|
||||
let termMeasure : TerminationMeasure:= {ref := .missing, structural := true, fn}
|
||||
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
|
||||
let stx ← termArg.delab arity (extraParams := preDef.termination.extraParams)
|
||||
let stx ← termMeasure.delab arity (extraParams := preDef.termination.extraParams)
|
||||
Tactic.TryThis.addSuggestion ref stx
|
||||
|
||||
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option TerminationMeasure)) : TermElabM Unit := do
|
||||
let names := preDefs.map (·.declName)
|
||||
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termArg?s
|
||||
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termMeasure?s
|
||||
for recArgPos in recArgPoss, preDef in preDefs do
|
||||
reportTermArg preDef recArgPos
|
||||
reporttermMeasure preDef recArgPos
|
||||
state.addMatchers.forM liftM
|
||||
preDefsNonRec.forM fun preDefNonRec => do
|
||||
let preDefNonRec ← eraseRecAppSyntax preDefNonRec
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Lean.Elab
|
||||
/-- A single `termination_by` clause -/
|
||||
structure TerminationBy where
|
||||
ref : Syntax
|
||||
structural : Bool
|
||||
structural : Bool
|
||||
vars : TSyntaxArray [`ident, ``Lean.Parser.Term.hole]
|
||||
body : Term
|
||||
/--
|
||||
@@ -33,6 +33,12 @@ structure DecreasingBy where
|
||||
tactic : TSyntax ``Lean.Parser.Tactic.tacticSeq
|
||||
deriving Inhabited
|
||||
|
||||
/-- A single `partial_fixpoint` clause -/
|
||||
structure PartialFixpoint where
|
||||
ref : Syntax
|
||||
term? : Option Term
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
The termination annotations for a single function.
|
||||
For `decreasing_by`, we store the whole `decreasing_by tacticSeq` expression, as this
|
||||
@@ -42,12 +48,13 @@ structure TerminationHints where
|
||||
ref : Syntax
|
||||
terminationBy?? : Option Syntax
|
||||
terminationBy? : Option TerminationBy
|
||||
partialFixpoint? : Option PartialFixpoint
|
||||
decreasingBy? : Option DecreasingBy
|
||||
/--
|
||||
Here we record the number of parameters past the `:`. It is set by
|
||||
`TerminationHints.rememberExtraParams` and used as follows:
|
||||
|
||||
* When we guess the termination argument in `GuessLex` and want to print it in surface-syntax
|
||||
* When we guess the termination measure in `GuessLex` and want to print it in surface-syntax
|
||||
compatible form.
|
||||
* If there are fewer variables in the `termination_by` annotation than there are extra
|
||||
parameters, we know which parameters they should apply to (`TerminationBy.checkVars`).
|
||||
@@ -55,26 +62,29 @@ structure TerminationHints where
|
||||
extraParams : Nat
|
||||
deriving Inhabited
|
||||
|
||||
def TerminationHints.none : TerminationHints := ⟨.missing, .none, .none, .none, 0⟩
|
||||
def TerminationHints.none : TerminationHints := ⟨.missing, .none, .none, .none, .none, 0⟩
|
||||
|
||||
/-- Logs warnings when the `TerminationHints` are unexpectedly present. -/
|
||||
def TerminationHints.ensureNone (hints : TerminationHints) (reason : String) : CoreM Unit := do
|
||||
match hints.terminationBy??, hints.terminationBy?, hints.decreasingBy? with
|
||||
| .none, .none, .none => pure ()
|
||||
| .none, .none, .some dec_by =>
|
||||
match hints.terminationBy??, hints.terminationBy?, hints.decreasingBy?, hints.partialFixpoint? with
|
||||
| .none, .none, .none, .none => pure ()
|
||||
| .none, .none, .some dec_by, .none =>
|
||||
logWarningAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
|
||||
| .some term_by?, .none, .none =>
|
||||
| .some term_by?, .none, .none, .none =>
|
||||
logWarningAt term_by? m!"unused `termination_by?`, function is {reason}"
|
||||
| .none, .some term_by, .none =>
|
||||
| .none, .some term_by, .none, .none =>
|
||||
logWarningAt term_by.ref m!"unused `termination_by`, function is {reason}"
|
||||
| _, _, _ =>
|
||||
| .none, .none, .none, .some partialFixpoint =>
|
||||
logWarningAt partialFixpoint.ref m!"unused `partial_fixpoint`, function is {reason}"
|
||||
| _, _, _, _=>
|
||||
logWarningAt hints.ref m!"unused termination hints, function is {reason}"
|
||||
|
||||
/-- True if any form of termination hint is present. -/
|
||||
def TerminationHints.isNotNone (hints : TerminationHints) : Bool :=
|
||||
hints.terminationBy??.isSome ||
|
||||
hints.terminationBy?.isSome ||
|
||||
hints.decreasingBy?.isSome
|
||||
hints.decreasingBy?.isSome ||
|
||||
hints.partialFixpoint?.isSome
|
||||
|
||||
/--
|
||||
Remembers `extraParams` for later use. Needs to happen early enough where we still know
|
||||
@@ -117,6 +127,8 @@ def elabTerminationHints {m} [Monad m] [MonadError m] (stx : TSyntax ``suffix) :
|
||||
| _ => pure none
|
||||
else pure none
|
||||
let terminationBy? : Option TerminationBy ← if let some t := t? then match t with
|
||||
| `(terminationBy|termination_by partialFixpointursion) =>
|
||||
pure (some {ref := t, structural := false, vars := #[], body := ⟨.missing⟩ : TerminationBy})
|
||||
| `(terminationBy|termination_by $[structural%$s]? => $_body) =>
|
||||
throwErrorAt t "no extra parameters bounds, please omit the `=>`"
|
||||
| `(terminationBy|termination_by $[structural%$s]? $vars* => $body) =>
|
||||
@@ -124,12 +136,17 @@ def elabTerminationHints {m} [Monad m] [MonadError m] (stx : TSyntax ``suffix) :
|
||||
| `(terminationBy|termination_by $[structural%$s]? $body:term) =>
|
||||
pure (some {ref := t, structural := s.isSome, vars := #[], body})
|
||||
| `(terminationBy?|termination_by?) => pure none
|
||||
| `(partialFixpoint|partial_fixpoint $[monotonicity $_]?) => pure none
|
||||
| _ => throwErrorAt t "unexpected `termination_by` syntax"
|
||||
else pure none
|
||||
let partialFixpoint? : Option PartialFixpoint ← if let some t := t? then match t with
|
||||
| `(partialFixpoint|partial_fixpoint $[monotonicity $term?]?) => pure (some {ref := t, term?})
|
||||
| _ => pure none
|
||||
else pure none
|
||||
let decreasingBy? ← d?.mapM fun d => match d with
|
||||
| `(decreasingBy|decreasing_by $tactic) => pure {ref := d, tactic}
|
||||
| _ => throwErrorAt d "unexpected `decreasing_by` syntax"
|
||||
return { ref := stx, terminationBy??, terminationBy?, decreasingBy?, extraParams := 0 }
|
||||
return { ref := stx, terminationBy??, terminationBy?, partialFixpoint?, decreasingBy?, extraParams := 0 }
|
||||
| _ => throwErrorAt stx s!"Unexpected Termination.suffix syntax: {stx} of kind {stx.raw.getKind}"
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
@@ -14,8 +14,8 @@ import Lean.PrettyPrinter.Delaborator.Basic
|
||||
|
||||
/-!
|
||||
This module contains
|
||||
* the data type `TerminationArgument`, the elaborated form of a `TerminationBy` clause,
|
||||
* the `TerminationArguments` type for a clique, and
|
||||
* the data type `TerminationMeasure`, the elaborated form of a `TerminationBy` clause,
|
||||
* the `TerminationMeasures` type for a clique, and
|
||||
* elaboration and deelaboration functions.
|
||||
-/
|
||||
|
||||
@@ -29,28 +29,28 @@ open Lean Meta Elab Term
|
||||
Elaborated form for a `termination_by` clause.
|
||||
|
||||
The `fn` has the same (value) arity as the recursive functions (stored in
|
||||
`arity`), and maps its arguments (including fixed prefix, in unpacked form) to
|
||||
the termination argument.
|
||||
`arity`), and maps its measures (including fixed prefix, in unpacked form) to
|
||||
the termination measure.
|
||||
|
||||
If `structural := Bool`, then the `fn` is a lambda picking out exactly one argument.
|
||||
If `structural := Bool`, then the `fn` is a lambda picking out exactly one measure.
|
||||
-/
|
||||
structure TerminationArgument where
|
||||
structure TerminationMeasure where
|
||||
ref : Syntax
|
||||
structural : Bool
|
||||
fn : Expr
|
||||
deriving Inhabited
|
||||
|
||||
/-- A complete set of `TerminationArgument`s, as applicable to a single clique. -/
|
||||
abbrev TerminationArguments := Array TerminationArgument
|
||||
/-- A complete set of `TerminationMeasure`s, as applicable to a single clique. -/
|
||||
abbrev TerminationMeasures := Array TerminationMeasure
|
||||
|
||||
/--
|
||||
Elaborates a `TerminationBy` to an `TerminationArgument`.
|
||||
Elaborates a `TerminationBy` to an `TerminationMeasure`.
|
||||
|
||||
* `type` is the full type of the original recursive function, including fixed prefix.
|
||||
* `hint : TerminationBy` is the syntactic `TerminationBy`.
|
||||
-/
|
||||
def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams : Nat)
|
||||
(hint : TerminationBy) : TermElabM TerminationArgument := withDeclName funName do
|
||||
def TerminationMeasure.elab (funName : Name) (type : Expr) (arity extraParams : Nat)
|
||||
(hint : TerminationBy) : TermElabM TerminationMeasure := withDeclName funName do
|
||||
assert! extraParams ≤ arity
|
||||
|
||||
if h : hint.vars.size > extraParams then
|
||||
@@ -73,7 +73,7 @@ def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams :
|
||||
-- Structural recursion: The body has to be a single parameter, whose index we return
|
||||
if hint.structural then unless (ys ++ xs).contains body do
|
||||
let params := MessageData.andList ((ys ++ xs).toList.map (m!"'{·}'"))
|
||||
throwErrorAt hint.ref m!"The termination argument of a structurally recursive " ++
|
||||
throwErrorAt hint.ref m!"The termination measure of a structurally recursive " ++
|
||||
m!"function must be one of the parameters {params}, but{indentExpr body}\nisn't " ++
|
||||
m!"one of these."
|
||||
|
||||
@@ -87,24 +87,24 @@ def TerminationArgument.elab (funName : Name) (type : Expr) (arity extraParams :
|
||||
| 1 => "one parameter"
|
||||
| n => m!"{n} parameters"
|
||||
|
||||
def TerminationArgument.structuralArg (termArg : TerminationArgument) : MetaM Nat := do
|
||||
assert! termArg.structural
|
||||
lambdaTelescope termArg.fn fun ys e => do
|
||||
def TerminationMeasure.structuralArg (measure : TerminationMeasure) : MetaM Nat := do
|
||||
assert! measure.structural
|
||||
lambdaTelescope measure.fn fun ys e => do
|
||||
let .some idx := ys.indexOf? e
|
||||
| panic! "TerminationArgument.structuralArg: body not one of the parameters"
|
||||
| panic! "TerminationMeasure.structuralArg: body not one of the parameters"
|
||||
return idx
|
||||
|
||||
|
||||
open PrettyPrinter Delaborator SubExpr Parser.Termination Parser.Term in
|
||||
/--
|
||||
Delaborates a `TerminationArgument` back to a `TerminationHint`, e.g. for `termination_by?`.
|
||||
Delaborates a `TerminationMeasure` back to a `TerminationHint`, e.g. for `termination_by?`.
|
||||
|
||||
This needs extra information:
|
||||
* `arity` is the value arity of the recursive function
|
||||
* `extraParams` indicates how many of the functions arguments are bound “after the colon”.
|
||||
* `extraParams` indicates how many of the function's parameters are bound “after the colon”.
|
||||
-/
|
||||
def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : TerminationArgument) : MetaM (TSyntax ``terminationBy) := do
|
||||
lambdaBoundedTelescope termArg.fn (arity - extraParams) fun _ys e => do
|
||||
def TerminationMeasure.delab (arity : Nat) (extraParams : Nat) (measure : TerminationMeasure) : MetaM (TSyntax ``terminationBy) := do
|
||||
lambdaBoundedTelescope measure.fn (arity - extraParams) fun _ys e => do
|
||||
pure (← delabCore e (delab := go extraParams #[])).1
|
||||
where
|
||||
go : Nat → TSyntaxArray `ident → DelabM (TSyntax ``terminationBy)
|
||||
@@ -119,7 +119,7 @@ def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : Termi
|
||||
-- drop trailing underscores
|
||||
let mut vars := vars
|
||||
while ! vars.isEmpty && vars.back!.raw.isOfKind ``hole do vars := vars.pop
|
||||
if termArg.structural then
|
||||
if measure.structural then
|
||||
if vars.isEmpty then
|
||||
`(terminationBy|termination_by structural $stxBody)
|
||||
else
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user