mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 02:44:12 +00:00
Compare commits
207 Commits
array_any2
...
list_array
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b7f5cbef6 | ||
|
|
65ab894ed4 | ||
|
|
d5f01f2db1 | ||
|
|
c8aae00847 | ||
|
|
1bbd2c183b | ||
|
|
b55a5b0826 | ||
|
|
eeca0ce96b | ||
|
|
2cb89823f3 | ||
|
|
297be24c0d | ||
|
|
e59f487bf0 | ||
|
|
e1d15946f7 | ||
|
|
5c333d88c0 | ||
|
|
07ee2eea21 | ||
|
|
af82d75e86 | ||
|
|
25179352b4 | ||
|
|
06c57826ae | ||
|
|
044e3b1b56 | ||
|
|
96f9ee2a41 | ||
|
|
0f3d426591 | ||
|
|
a014ae1001 | ||
|
|
137f559520 | ||
|
|
3d6d51d2c6 | ||
|
|
3786ad6d0c | ||
|
|
b16769f5a0 | ||
|
|
4262ea14d6 | ||
|
|
816da7120e | ||
|
|
38ed354cdb | ||
|
|
56ac94b591 | ||
|
|
b78352ec9d | ||
|
|
1feae7abe1 | ||
|
|
a2cb435aa1 | ||
|
|
c3f61ba3a2 | ||
|
|
8850f9e9aa | ||
|
|
2363d2fa87 | ||
|
|
d1d2f215ad | ||
|
|
eb58f46ce7 | ||
|
|
1a2345b47f | ||
|
|
c1d145e9d7 | ||
|
|
3a308324f6 | ||
|
|
bc2561f538 | ||
|
|
ed89c2611e | ||
|
|
2952cf81e6 | ||
|
|
589eff6187 | ||
|
|
7c5b423659 | ||
|
|
b1bd2c931c | ||
|
|
ce614bd830 | ||
|
|
1731f2f850 | ||
|
|
bfe7b1fb34 | ||
|
|
0a14ec0978 | ||
|
|
bb47469d1a | ||
|
|
e7e57d40c4 | ||
|
|
7c0b72e2c5 | ||
|
|
8fc8e8ed19 | ||
|
|
96947280df | ||
|
|
0af15f9b1d | ||
|
|
dab4908317 | ||
|
|
aca1d54514 | ||
|
|
817772e97b | ||
|
|
af8ec41014 | ||
|
|
51794c384a | ||
|
|
acfc11ae42 | ||
|
|
9d39942189 | ||
|
|
829522ba55 | ||
|
|
d538e1cd90 | ||
|
|
77609dcdc7 | ||
|
|
22b6b49a43 | ||
|
|
f3c507ec57 | ||
|
|
e0fa6a1792 | ||
|
|
9a435b4f4a | ||
|
|
80b1ce8cad | ||
|
|
2ac0e4c061 | ||
|
|
cdfec6971f | ||
|
|
7365600cf8 | ||
|
|
754bab442a | ||
|
|
4593ff50f0 | ||
|
|
6ecce365e9 | ||
|
|
1d17119710 | ||
|
|
9233d7a4d7 | ||
|
|
060e137599 | ||
|
|
7bfa8f6296 | ||
|
|
84c7e5db1f | ||
|
|
c797525d2a | ||
|
|
0714a7150b | ||
|
|
9c36901728 | ||
|
|
da2d877019 | ||
|
|
ffc7ba0829 | ||
|
|
09161f6fdd | ||
|
|
8dc3c53240 | ||
|
|
dd91d7e2e2 | ||
|
|
599444e27e | ||
|
|
1a0d2b6fc1 | ||
|
|
8d0093b43f | ||
|
|
d07897fc36 | ||
|
|
bfe8e5a958 | ||
|
|
b9f8a859e7 | ||
|
|
0d3ae7fde5 | ||
|
|
1bfccf88da | ||
|
|
565c6f3eb2 | ||
|
|
77ae842496 | ||
|
|
250b977616 | ||
|
|
a8a5c6cff1 | ||
|
|
555f3d86fb | ||
|
|
dc5eb40ca3 | ||
|
|
20571a938b | ||
|
|
e9f2e1861e | ||
|
|
905b2eedcd | ||
|
|
00a4503c4f | ||
|
|
11aff52fb1 | ||
|
|
ec127a780e | ||
|
|
b958109d06 | ||
|
|
d0f4e7c590 | ||
|
|
20d191bc8e | ||
|
|
24db5b598b | ||
|
|
141e519009 | ||
|
|
c5cec10788 | ||
|
|
950ab377c6 | ||
|
|
0c898742f6 | ||
|
|
ca0d822619 | ||
|
|
e2a80875c9 | ||
|
|
061ebe1dca | ||
|
|
7a8c8a4fb3 | ||
|
|
3ff10c6cdd | ||
|
|
9ae2ac39c9 | ||
|
|
2c8fb9d3fc | ||
|
|
dc7358b4df | ||
|
|
44a518b331 | ||
|
|
68f3fc6d5d | ||
|
|
72c4630aab | ||
|
|
db0abe89cf | ||
|
|
2b44a4f0d9 | ||
|
|
72f4098156 | ||
|
|
f0f7c3ff01 | ||
|
|
5536281238 | ||
|
|
8de6233326 | ||
|
|
f312170f21 | ||
|
|
6d1bda6ff2 | ||
|
|
f45c19b428 | ||
|
|
e2ee629022 | ||
|
|
64731b71aa | ||
|
|
23b5baa5ec | ||
|
|
f58e893e63 | ||
|
|
a856518265 | ||
|
|
45806017e5 | ||
|
|
058e63a3d6 | ||
|
|
e8e6c4716f | ||
|
|
3ce8c73315 | ||
|
|
88edd13642 | ||
|
|
c70e614a5b | ||
|
|
aa8faae576 | ||
|
|
2f8901d6d0 | ||
|
|
9ff8c5ac2d | ||
|
|
48491e5262 | ||
|
|
9f5cc7262b | ||
|
|
957beb02bc | ||
|
|
017a1f2b94 | ||
|
|
f8f1b2212a | ||
|
|
dab6a161bd | ||
|
|
8e47d29bf9 | ||
|
|
e337129108 | ||
|
|
d3eb2fe13c | ||
|
|
d2239a5770 | ||
|
|
a244b06882 | ||
|
|
0a55f4bf36 | ||
|
|
e7a411a66d | ||
|
|
783671261d | ||
|
|
01d951c3fc | ||
|
|
6cf3402f1c | ||
|
|
e3c6909ad5 | ||
|
|
255810db64 | ||
|
|
f094652481 | ||
|
|
3eb07cac44 | ||
|
|
58034bf237 | ||
|
|
7ba7ea4e16 | ||
|
|
4877e84031 | ||
|
|
9c47f395c8 | ||
|
|
3f98b4835c | ||
|
|
a86145b6bb | ||
|
|
c4d3a74f32 | ||
|
|
c74865fbe2 | ||
|
|
93a908469c | ||
|
|
903fe29863 | ||
|
|
84da113355 | ||
|
|
75df4c0b52 | ||
|
|
ad5a746cdd | ||
|
|
2bd3ce5463 | ||
|
|
2b752ec245 | ||
|
|
909ee719aa | ||
|
|
7dd5e957da | ||
|
|
d67e0eea47 | ||
|
|
10bfeba2d9 | ||
|
|
4285f8ba05 | ||
|
|
d8be3ef7a8 | ||
|
|
c924768879 | ||
|
|
c1e76e8976 | ||
|
|
60a9f8e492 | ||
|
|
604133d189 | ||
|
|
d3781bb787 | ||
|
|
87e8da5230 | ||
|
|
727c696d9f | ||
|
|
cf2b7f4c1b | ||
|
|
cd4383b6f3 | ||
|
|
0d9859370a | ||
|
|
c292ae2e0e | ||
|
|
3113847806 | ||
|
|
d275455674 | ||
|
|
a4d10742d3 | ||
|
|
777fba495a |
20
.github/workflows/awaiting-mathlib.yml
vendored
Normal file
20
.github/workflows/awaiting-mathlib.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Check awaiting-mathlib label
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, labeled]
|
||||
|
||||
jobs:
|
||||
check-awaiting-mathlib:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check awaiting-mathlib label
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { labels } = context.payload.pull_request;
|
||||
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
|
||||
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
|
||||
}
|
||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -204,7 +204,8 @@ jobs:
|
||||
"os": "macos-14",
|
||||
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"release": true,
|
||||
"check-level": 0,
|
||||
// special cased below
|
||||
// "check-level": 0,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
|
||||
@@ -260,8 +261,21 @@ jobs:
|
||||
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
|
||||
// }
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
|
||||
return matrix.filter((job) => level >= job["check-level"])
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
|
||||
const isPr = "${{ github.event_name }}" == "pull_request";
|
||||
const filter = (job) => {
|
||||
if (job["name"] === "macOS aarch64") {
|
||||
// Special handling for MacOS aarch64, we want:
|
||||
// 1. To run it in PRs so Mac devs get PR toolchains
|
||||
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
|
||||
// little value in the merge queue
|
||||
// 3. To run it in release (obviously)
|
||||
return isPr || level >= 2;
|
||||
} else {
|
||||
return level >= job["check-level"];
|
||||
}
|
||||
};
|
||||
return matrix.filter(filter);
|
||||
|
||||
build:
|
||||
needs: [configure]
|
||||
|
||||
27
.github/workflows/pr-release.yml
vendored
27
.github/workflows/pr-release.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v8 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
@@ -155,6 +155,20 @@ jobs:
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
# Check if force-mathlib-ci label is present
|
||||
LABELS="$(curl --retry 3 --location --silent \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
|
||||
| jq -r '.[].name')"
|
||||
|
||||
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
|
||||
echo "force-mathlib-ci label detected, forcing CI despite issues"
|
||||
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
|
||||
FORCE_CI=true
|
||||
else
|
||||
MESSAGE="$MESSAGE You can force Mathlib CI using the \`force-mathlib-ci\` label."
|
||||
fi
|
||||
|
||||
echo "Checking existing messages"
|
||||
|
||||
@@ -201,7 +215,12 @@ jobs:
|
||||
else
|
||||
echo "The message already exists in the comment body."
|
||||
fi
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
if [[ "$FORCE_CI" == "true" ]]; then
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
@@ -252,7 +271,7 @@ jobs:
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
|
||||
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
@@ -316,7 +335,7 @@ jobs:
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
|
||||
@@ -47,10 +47,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
|
||||
endif()
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
|
||||
ExternalProject_add(cadical
|
||||
PREFIX cadical
|
||||
GIT_REPOSITORY https://github.com/arminbiere/cadical
|
||||
GIT_TAG rel-1.9.5
|
||||
GIT_TAG rel-2.1.2
|
||||
CONFIGURE_COMMAND ""
|
||||
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
|
||||
8
flake.lock
generated
8
flake.lock
generated
@@ -36,17 +36,17 @@
|
||||
},
|
||||
"nixpkgs-cadical": {
|
||||
"locked": {
|
||||
"lastModified": 1722221733,
|
||||
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
|
||||
"lastModified": 1740791350,
|
||||
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
# old nixpkgs used for portable release with older glibc (2.26)
|
||||
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
|
||||
inputs.nixpkgs-older.flake = false;
|
||||
# for cadical 1.9.5; sync with CMakeLists.txt
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
|
||||
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
|
||||
1110
releases/v4.17.0.md
Normal file
1110
releases/v4.17.0.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -65,20 +65,21 @@ def format_markdown_description(pr_number, description):
|
||||
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
|
||||
return f"{link} {description}"
|
||||
|
||||
def commit_types():
|
||||
# see doc/dev/commit_convention.md
|
||||
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
|
||||
|
||||
def count_commit_types(commits):
|
||||
counts = {
|
||||
'total': len(commits),
|
||||
'feat': 0,
|
||||
'fix': 0,
|
||||
'refactor': 0,
|
||||
'doc': 0,
|
||||
'chore': 0
|
||||
}
|
||||
for commit_type in commit_types():
|
||||
counts[commit_type] = 0
|
||||
|
||||
for _, first_line, _ in commits:
|
||||
for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
|
||||
if first_line.startswith(commit_type):
|
||||
counts[commit_type.rstrip(':')] += 1
|
||||
for commit_type in commit_types():
|
||||
if first_line.startswith(f'{commit_type}:'):
|
||||
counts[commit_type] += 1
|
||||
break
|
||||
|
||||
return counts
|
||||
@@ -158,8 +159,9 @@ def main():
|
||||
counts = count_commit_types(commits)
|
||||
print(f"For this release, {counts['total']} changes landed. "
|
||||
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements "
|
||||
f"and {counts['chore']} chores.\n")
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
|
||||
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
|
||||
f"and {counts['style'] + counts['chore']} other changes.\n")
|
||||
|
||||
section_order = sort_sections_order()
|
||||
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
|
||||
@@ -168,7 +170,12 @@ def main():
|
||||
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
|
||||
print(f"## {section_title}\n")
|
||||
for _, entry in sorted(entries, key=lambda x: x[0]):
|
||||
print(f"* {entry}\n")
|
||||
# Split entry into lines and indent all lines after the first
|
||||
lines = entry.splitlines()
|
||||
print(f"* {lines[0]}")
|
||||
for line in lines[1:]:
|
||||
print(f" {line}")
|
||||
print() # Empty line after each entry
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 18)
|
||||
set(LEAN_VERSION_MINOR 19)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
|
||||
@@ -40,3 +40,4 @@ import Init.Syntax
|
||||
import Init.Internal
|
||||
import Init.Try
|
||||
import Init.BinderNameHint
|
||||
import Init.Task
|
||||
|
||||
@@ -51,14 +51,28 @@ def ForInStep.value (x : ForInStep α) : α :=
|
||||
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
|
||||
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
|
||||
|
||||
/--
|
||||
Maps a function over a functor, with parameters swapped so that the function comes last.
|
||||
|
||||
This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.
|
||||
-/
|
||||
@[reducible]
|
||||
def Functor.mapRev {f : Type u → Type v} [Functor f] {α β : Type u} : f α → (α → β) → f β :=
|
||||
fun a f => f <$> a
|
||||
|
||||
@[inherit_doc Functor.mapRev]
|
||||
infixr:100 " <&> " => Functor.mapRev
|
||||
|
||||
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»]
|
||||
|
||||
/--
|
||||
Discards the value in a functor, retaining the functor's structure.
|
||||
|
||||
Discarding values is especially useful when using `Applicative` functors or `Monad`s to implement
|
||||
effects, and some operation should be carried out only for its effects. In `do`-notation, statements
|
||||
whose values are discarded must return `Unit`, and `discard` can be used to explicitly discard their
|
||||
values.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
|
||||
Functor.mapConst PUnit.unit x
|
||||
|
||||
@@ -13,10 +13,20 @@ import Init.Coe
|
||||
namespace Except
|
||||
variable {ε : Type u}
|
||||
|
||||
/--
|
||||
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : Except ε α :=
|
||||
Except.ok a
|
||||
|
||||
/--
|
||||
Transforms a successful result with a function, doing nothing when an exception is thrown.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).map toString = pure 2`
|
||||
* `(throw "Error" : Except String Nat).map toString = throw "Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map (f : α → β) : Except ε α → Except ε β
|
||||
| Except.error err => Except.error err
|
||||
@@ -27,36 +37,78 @@ protected def map (f : α → β) : Except ε α → Except ε β
|
||||
intro e
|
||||
simp [Except.map]; cases e <;> rfl
|
||||
|
||||
/--
|
||||
Transforms exceptions with a function, doing nothing on successful results.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).mapError (·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).mapError (·.length) = throw 5`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def mapError (f : ε → ε') : Except ε α → Except ε' α
|
||||
| Except.error err => Except.error <| f err
|
||||
| Except.ok v => Except.ok v
|
||||
|
||||
/--
|
||||
Sequences two operations that may throw exceptions, allowing the second to depend on the value
|
||||
returned by the first.
|
||||
|
||||
If the first operation throws an exception, then it is the result of the computation. If the first
|
||||
succeeds but the second throws an exception, then that exception is the result. If both succeed,
|
||||
then the result is the result of the second computation.
|
||||
|
||||
This is the implementation of the `>>=` operator for `Except ε`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (ma : Except ε α) (f : α → Except ε β) : Except ε β :=
|
||||
match ma with
|
||||
| Except.error err => Except.error err
|
||||
| Except.ok v => f v
|
||||
|
||||
/-- Returns true if the value is `Except.ok`, false otherwise. -/
|
||||
/-- Returns `true` if the value is `Except.ok`, `false` otherwise. -/
|
||||
@[always_inline, inline]
|
||||
protected def toBool : Except ε α → Bool
|
||||
| Except.ok _ => true
|
||||
| Except.error _ => false
|
||||
|
||||
@[inherit_doc Except.toBool]
|
||||
abbrev isOk : Except ε α → Bool := Except.toBool
|
||||
|
||||
/--
|
||||
Returns `none` if an exception was thrown, or `some` around the value on success.
|
||||
|
||||
Examples:
|
||||
* `(pure 10 : Except String Nat).toOption = some 10`
|
||||
* `(throw "Failure" : Except String Nat).toOption = none`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def toOption : Except ε α → Option α
|
||||
| Except.ok a => some a
|
||||
| Except.error _ => none
|
||||
|
||||
/--
|
||||
Handles exceptions thrown in the `Except ε` monad.
|
||||
|
||||
If `ma` is successful, its result is returned. If it throws an exception, then `handle` is invoked
|
||||
on the exception's value.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).tryCatch (pure ·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (pure ·.length) = pure 5`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (fun x => throw ("E: " ++ x)) = throw "E: Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch (ma : Except ε α) (handle : ε → Except ε α) : Except ε α :=
|
||||
match ma with
|
||||
| Except.ok a => Except.ok a
|
||||
| Except.error e => handle e
|
||||
|
||||
/--
|
||||
Recovers from exceptions thrown in the `Except ε` monad. Typically used via the `<|>` operator.
|
||||
|
||||
`Except.tryCatch` is a related operator that allows the recovery procedure to depend on _which_
|
||||
exception was thrown.
|
||||
-/
|
||||
def orElseLazy (x : Except ε α) (y : Unit → Except ε α) : Except ε α :=
|
||||
match x with
|
||||
| Except.ok a => Except.ok a
|
||||
@@ -70,12 +122,26 @@ instance : Monad (Except ε) where
|
||||
|
||||
end Except
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
-/
|
||||
def ExceptT (ε : Type u) (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Except ε α)
|
||||
|
||||
/--
|
||||
Use a monadic action that may return an exception's value as an action in the transformed monad that
|
||||
may throw the corresponding exception.
|
||||
|
||||
This is the inverse of `ExceptT.run`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.mk {ε : Type u} {m : Type u → Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
|
||||
This is the inverse of `ExceptT.mk`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.run {ε : Type u} {m : Type u → Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
|
||||
|
||||
@@ -83,25 +149,41 @@ namespace ExceptT
|
||||
|
||||
variable {ε : Type u} {m : Type u → Type v} [Monad m]
|
||||
|
||||
/--
|
||||
Returns the value `a` without throwing exceptions or having any other effect.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| pure (Except.ok a)
|
||||
|
||||
/--
|
||||
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bindCont {α β : Type u} (f : α → ExceptT ε m β) : Except ε α → m (Except ε β)
|
||||
| Except.ok a => f a
|
||||
| Except.error e => pure (Except.error e)
|
||||
|
||||
/--
|
||||
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
|
||||
operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α → ExceptT ε m β) : ExceptT ε m β :=
|
||||
ExceptT.mk <| ma >>= ExceptT.bindCont f
|
||||
|
||||
/--
|
||||
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map {α β : Type u} (f : α → β) (x : ExceptT ε m α) : ExceptT ε m β :=
|
||||
ExceptT.mk <| x >>= fun a => match a with
|
||||
| (Except.ok a) => pure <| Except.ok (f a)
|
||||
| (Except.error e) => pure <| Except.error e
|
||||
|
||||
/--
|
||||
Runs a computation from an underlying monad in the transformed monad with exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| Except.ok <$> t
|
||||
@@ -110,6 +192,9 @@ protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
instance : MonadLift (Except ε) (ExceptT ε m) := ⟨fun e => ExceptT.mk <| pure e⟩
|
||||
instance : MonadLift m (ExceptT ε m) := ⟨ExceptT.lift⟩
|
||||
|
||||
/--
|
||||
Handles exceptions produced in the `ExceptT ε` transformer.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε → ExceptT ε m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| ma >>= fun res => match res with
|
||||
@@ -124,6 +209,11 @@ instance : Monad (ExceptT ε m) where
|
||||
bind := ExceptT.bind
|
||||
map := ExceptT.map
|
||||
|
||||
/--
|
||||
Transforms exceptions using the function `f`.
|
||||
|
||||
This is the `ExceptT` version of `Except.mapError`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → ExceptT ε' m α := fun x =>
|
||||
ExceptT.mk <| Except.mapError f <$> x
|
||||
@@ -150,8 +240,12 @@ instance (ε) : MonadExceptOf ε (Except ε) where
|
||||
namespace MonadExcept
|
||||
variable {ε : Type u} {m : Type v → Type w}
|
||||
|
||||
/-- Alternative orelse operator that allows to select which exception should be used.
|
||||
The default is to use the first exception since the standard `orelse` uses the second. -/
|
||||
/--
|
||||
An alternative unconditional error recovery operator that allows callers to specify which exception
|
||||
to throw in cases where both operations throw exceptions.
|
||||
|
||||
By default, the first is thrown, because the `<|>` operator throws the second.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
|
||||
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)
|
||||
|
||||
@@ -10,19 +10,37 @@ import Init.Control.Lawful.Basic
|
||||
The Exception monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
|
||||
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
|
||||
style. This has different performance characteristics from `ExceptT ε`.
|
||||
-/
|
||||
def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type u) → (α → m β) → (ε → m β) → m β
|
||||
|
||||
namespace ExceptCpsT
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
|
||||
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
|
||||
|
||||
set_option linter.unusedVariables false in -- `s` unused
|
||||
/--
|
||||
Use a monadic action that may throw an exception by providing explicit success and failure
|
||||
continuations.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α → m β) (error : ε → m β) : m β :=
|
||||
x _ ok error
|
||||
|
||||
/--
|
||||
Returns the value of a computation, forgetting whether it was an exception or a success.
|
||||
|
||||
This corresponds to early return.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
|
||||
x α pure pure
|
||||
@@ -40,6 +58,9 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
|
||||
throw e := fun _ _ k => k e
|
||||
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
|
||||
|
||||
/--
|
||||
Run an action from the transformed monad in the exception monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
|
||||
fun _ k _ => x >>= k
|
||||
|
||||
@@ -10,6 +10,28 @@ import Init.Core
|
||||
|
||||
universe u
|
||||
|
||||
/--
|
||||
The identity function on types, used primarily for its `Monad` instance.
|
||||
|
||||
The identity monad is useful together with monad transformers to construct monads for particular
|
||||
purposes. Additionally, it can be used with `do`-notation in order to use control structures such as
|
||||
local mutability, `for`-loops, and early returns in code that does not otherwise use monads.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def containsFive (xs : List Nat) : Bool := Id.run do
|
||||
for x in xs do
|
||||
if x == 5 then return true
|
||||
return false
|
||||
```
|
||||
|
||||
```lean example
|
||||
#eval containsFive [1, 3, 5, 7]
|
||||
```
|
||||
```output
|
||||
true
|
||||
```
|
||||
-/
|
||||
def Id (type : Type u) : Type u := type
|
||||
|
||||
namespace Id
|
||||
@@ -20,9 +42,18 @@ instance : Monad Id where
|
||||
bind x f := f x
|
||||
map f x := f x
|
||||
|
||||
/--
|
||||
The identity monad has a `bind` operator.
|
||||
-/
|
||||
def hasBind : Bind Id :=
|
||||
inferInstance
|
||||
|
||||
/--
|
||||
Runs a computation in the identity monad.
|
||||
|
||||
This function is the identity function. Because its parameter has type `Id α`, it causes
|
||||
`do`-notation in its arguments to use the `Monad Id` instance.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def run (x : Id α) : α := x
|
||||
|
||||
|
||||
@@ -13,17 +13,26 @@ open Function
|
||||
rfl
|
||||
|
||||
/--
|
||||
The `Functor` typeclass only contains the operations of a functor.
|
||||
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
|
||||
including the preservation of the identity and composition laws:
|
||||
```
|
||||
id <$> x = x
|
||||
(h ∘ g) <$> x = h <$> g <$> x
|
||||
```
|
||||
A functor satisfies the functor laws.
|
||||
|
||||
The `Functor` class contains the operations of a functor, but does not require that instances
|
||||
prove they satisfy the laws of a functor. A `LawfulFunctor` instance includes proofs that the laws
|
||||
are satisfied. Because `Functor` instances may provide optimized implementations of `mapConst`,
|
||||
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulFunctor (f : Type u → Type v) [Functor f] : Prop where
|
||||
/--
|
||||
The `mapConst` implementation is equivalent to the default implementation.
|
||||
-/
|
||||
map_const : (Functor.mapConst : α → f β → f α) = Functor.map ∘ const β
|
||||
/--
|
||||
The `map` implementation preserves identity.
|
||||
-/
|
||||
id_map (x : f α) : id <$> x = x
|
||||
/--
|
||||
The `map` implementation preserves function composition.
|
||||
-/
|
||||
comp_map (g : α → β) (h : β → γ) (x : f α) : (h ∘ g) <$> x = h <$> g <$> x
|
||||
|
||||
export LawfulFunctor (map_const id_map comp_map)
|
||||
@@ -38,21 +47,48 @@ attribute [simp] id_map
|
||||
(comp_map _ _ _).symm
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
```
|
||||
pure id <*> v = v
|
||||
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
|
||||
pure f <*> pure x = pure (f x)
|
||||
u <*> pure y = pure (· y) <*> u
|
||||
```
|
||||
An applicative functor satisfies the laws of an applicative functor.
|
||||
|
||||
The `Applicative` class contains the operations of an applicative functor, but does not require that
|
||||
instances prove they satisfy the laws of an applicative functor. A `LawfulApplicative` instance
|
||||
includes proofs that the laws are satisfied.
|
||||
|
||||
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`,
|
||||
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] : Prop extends LawfulFunctor f where
|
||||
/-- `seqLeft` is equivalent to the default implementation. -/
|
||||
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
|
||||
/-- `seqRight` is equivalent to the default implementation. -/
|
||||
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
|
||||
/--
|
||||
`pure` before `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring immediately prior to `seq`.
|
||||
-/
|
||||
pure_seq (g : α → β) (x : f α) : pure g <*> x = g <$> x
|
||||
|
||||
/--
|
||||
Mapping a function over the result of `pure` is equivalent to applying the function under `pure`.
|
||||
|
||||
This means that `pure` really is pure with respect to `Functor.map`.
|
||||
-/
|
||||
map_pure (g : α → β) (x : α) : g <$> (pure x : f α) = pure (g x)
|
||||
|
||||
/--
|
||||
`pure` after `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring just after `seq`.
|
||||
-/
|
||||
seq_pure {α β : Type u} (g : f (α → β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
|
||||
|
||||
/--
|
||||
`seq` is associative.
|
||||
|
||||
Changing the nesting of `seq` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `seq` is not doing any more than sequencing.
|
||||
-/
|
||||
seq_assoc {α β γ : Type u} (x : f α) (g : f (α → β)) (h : f (β → γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
|
||||
comp_map g h x := (by
|
||||
repeat rw [← pure_seq]
|
||||
@@ -66,21 +102,36 @@ attribute [simp] map_pure seq_pure
|
||||
simp [pure_seq]
|
||||
|
||||
/--
|
||||
The `Monad` typeclass only contains the operations of a monad.
|
||||
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
|
||||
including associativity and identity laws for `bind`:
|
||||
```
|
||||
pure x >>= f = f x
|
||||
x >>= pure = x
|
||||
x >>= f >>= g = x >>= (fun x => f x >>= g)
|
||||
```
|
||||
Lawful monads are those that satisfy a certain behavioral specification. While all instances of
|
||||
`Monad` should satisfy these laws, not all implementations are required to prove this.
|
||||
|
||||
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
|
||||
`LawfulMonad.mk'` is an alternative constructor that contains useful defaults for many fields.
|
||||
-/
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplicative m where
|
||||
/--
|
||||
A `bind` followed by `pure` composed with a function is equivalent to a functorial map.
|
||||
|
||||
This means that `pure` really is pure after a `bind` and has no effects.
|
||||
-/
|
||||
bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
|
||||
/--
|
||||
A `bind` followed by a functorial map is equivalent to `Applicative` sequencing.
|
||||
|
||||
This means that the effect sequencing from `Monad` and `Applicative` are the same.
|
||||
-/
|
||||
bind_map {α β : Type u} (f : m (α → β)) (x : m α) : f >>= (. <$> x) = f <*> x
|
||||
/--
|
||||
`pure` followed by `bind` is equivalent to function application.
|
||||
|
||||
This means that `pure` really is pure before a `bind` and has no effects.
|
||||
-/
|
||||
pure_bind (x : α) (f : α → m β) : pure x >>= f = f x
|
||||
/--
|
||||
`bind` is associative.
|
||||
|
||||
Changing the nesting of `bind` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `bind` is not doing more than data-dependent sequencing.
|
||||
-/
|
||||
bind_assoc (x : m α) (f : α → m β) (g : β → m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
|
||||
map_pure g x := (by rw [← bind_pure_comp, pure_bind])
|
||||
seq_pure g x := (by rw [← bind_map]; simp [map_pure, bind_pure_comp])
|
||||
|
||||
@@ -52,8 +52,6 @@ attribute [simp] namedPattern
|
||||
/--
|
||||
`Empty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`Empty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `Empty.rec`.
|
||||
-/
|
||||
@[macro_inline] def Empty.elim {C : Sort u} : Empty → C := Empty.rec
|
||||
|
||||
@@ -63,8 +61,6 @@ instance : DecidableEq Empty := fun a => a.elim
|
||||
/--
|
||||
`PEmpty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`PEmpty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `PEmpty.rec`.
|
||||
-/
|
||||
@[macro_inline] def PEmpty.elim {C : Sort _} : PEmpty → C := fun a => nomatch a
|
||||
|
||||
@@ -138,46 +134,50 @@ recommended_spelling "iff" for "↔" in [Iff, «term_↔_»]
|
||||
recommended_spelling "iff" for "<->" in [Iff, «term_<->_»]
|
||||
|
||||
/--
|
||||
`Sum α β`, or `α ⊕ β`, is the disjoint union of types `α` and `β`.
|
||||
An element of `α ⊕ β` is either of the form `.inl a` where `a : α`,
|
||||
or `.inr b` where `b : β`.
|
||||
The disjoint union of types `α` and `β`, ordinarily written `α ⊕ β`.
|
||||
|
||||
An element of `α ⊕ β` is either an `a : α` wrapped in `Sum.inl` or a `b : β` wrapped in `Sum.inr`.
|
||||
`α ⊕ β` is not equivalent to the set-theoretic union of `α` and `β` because its values include an
|
||||
indication of which of the two types was chosen. The union of a singleton set with itself contains
|
||||
one element, while `Unit ⊕ Unit` contains distinct values `inl ()` and `inr ()`.
|
||||
-/
|
||||
inductive Sum (α : Type u) (β : Type v) where
|
||||
/-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
|
||||
/-- Left injection into the sum type `α ⊕ β`. -/
|
||||
| inl (val : α) : Sum α β
|
||||
/-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
|
||||
/-- Right injection into the sum type `α ⊕ β`. -/
|
||||
| inr (val : β) : Sum α β
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕ " => Sum
|
||||
|
||||
/--
|
||||
`PSum α β`, or `α ⊕' β`, is the disjoint union of types `α` and `β`.
|
||||
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts
|
||||
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
|
||||
that it can be used in situations where one side is a proposition, like `True ⊕' Nat`.
|
||||
The disjoint union of arbitrary sorts `α` `β`, or `α ⊕' β`.
|
||||
|
||||
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
|
||||
which can cause problems for universe level unification,
|
||||
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
|
||||
`PSum` is usually only used in automation that constructs sums of arbitrary types.
|
||||
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts `Sort u` and `Sort v`,
|
||||
instead of restricting them to `Type u` and `Type v`. This means that it can be used in situations
|
||||
where one side is a proposition, like `True ⊕' Nat`. However, the resulting universe level
|
||||
constraints are often more difficult to solve than those that result from `Sum`.
|
||||
-/
|
||||
inductive PSum (α : Sort u) (β : Sort v) where
|
||||
/-- Left injection into the sum type `α ⊕' β`. If `a : α` then `.inl a : α ⊕' β`. -/
|
||||
/-- Left injection into the sum type `α ⊕' β`.-/
|
||||
| inl (val : α) : PSum α β
|
||||
/-- Right injection into the sum type `α ⊕' β`. If `b : β` then `.inr b : α ⊕' β`. -/
|
||||
/-- Right injection into the sum type `α ⊕' β`. -/
|
||||
| inr (val : β) : PSum α β
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕' " => PSum
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `α` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
If the left type in a sum is inhabited then the sum is inhabited.
|
||||
|
||||
This is not an instance to avoid non-canonical instances when both the left and right types are
|
||||
inhabited.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
|
||||
/--
|
||||
`PSum α β` is inhabited if `β` is inhabited.
|
||||
This is not an instance to avoid non-canonical instances.
|
||||
If the right type in a sum is inhabited then the sum is inhabited.
|
||||
|
||||
This is not an instance to avoid non-canonical instances when both the left and right types are
|
||||
inhabited.
|
||||
-/
|
||||
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := ⟨PSum.inr default⟩
|
||||
|
||||
@@ -188,47 +188,58 @@ instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
|
||||
Nonempty.elim h (fun b => ⟨PSum.inr b⟩)
|
||||
|
||||
/--
|
||||
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
|
||||
whose first component is `a : α` and whose second component is `b : β a`
|
||||
(so the type of the second component can depend on the value of the first component).
|
||||
It is sometimes known as the dependent sum type, since it is the type level version
|
||||
of an indexed summation.
|
||||
Dependent pairs, in which the second element's type depends on the value of the first element. The
|
||||
type `Sigma β` is typically written `Σ a : α, β a` or `(a : α) × β a`.
|
||||
|
||||
Although its values are pairs, `Sigma` is sometimes known as the *dependent sum type*, since it is
|
||||
the type level version of an indexed summation.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure Sigma {α : Type u} (β : α → Type v) where
|
||||
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : Sigma β`.
|
||||
(This will usually require a type ascription to determine `β`
|
||||
since it is not determined from `a` and `b` alone.) -/
|
||||
/--
|
||||
Constructs a dependent pair.
|
||||
|
||||
Using this constructor in a context in which the type is not known usually requires a type
|
||||
ascription to determine `β`. This is because the desired relationship between the two values can't
|
||||
generally be determined automatically.
|
||||
-/
|
||||
mk ::
|
||||
/-- The first component of a dependent pair. If `p : @Sigma α β` then `p.1 : α`. -/
|
||||
/--
|
||||
The first component of a dependent pair.
|
||||
-/
|
||||
fst : α
|
||||
/-- The second component of a dependent pair. If `p : Sigma β` then `p.2 : β p.1`. -/
|
||||
/--
|
||||
The second component of a dependent pair. Its type depends on the first component.
|
||||
-/
|
||||
snd : β fst
|
||||
|
||||
attribute [unbox] Sigma
|
||||
|
||||
/--
|
||||
`PSigma β`, also denoted `Σ' a : α, β a` or `(a : α) ×' β a`, is the type of dependent pairs
|
||||
whose first component is `a : α` and whose second component is `b : β a`
|
||||
(so the type of the second component can depend on the value of the first component).
|
||||
It differs from `Σ a : α, β a` in that it allows `α` and `β` to have arbitrary sorts
|
||||
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
|
||||
that it can be used in situations where one side is a proposition, like `(p : Nat) ×' p = p`.
|
||||
Fully universe-polymorphic dependent pairs, in which the second element's type depends on the value
|
||||
of the first element and both types are allowed to be propositions. The type `PSigma β` is typically
|
||||
written `Σ' a : α, β a` or `(a : α) ×' β a`.
|
||||
|
||||
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
|
||||
which can cause problems for universe level unification,
|
||||
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
|
||||
`PSigma` is usually only used in automation that constructs pairs of arbitrary types.
|
||||
In practice, this generality leads to universe level constraints that are difficult to solve, so
|
||||
`PSigma` is rarely used in manually-written code. It is usually only used in automation that
|
||||
constructs pairs of arbitrary types.
|
||||
|
||||
To pair a value with a proof that a predicate holds for it, use `Subtype`. To demonstrate that a
|
||||
value exists that satisfies a predicate, use `Exists`. A dependent pair with a proposition as its
|
||||
first component is not typically useful due to proof irrelevance: there's no point in depending on a
|
||||
specific proof because all proofs are equal anyway.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure PSigma {α : Sort u} (β : α → Sort v) where
|
||||
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : PSigma β`.
|
||||
(This will usually require a type ascription to determine `β`
|
||||
since it is not determined from `a` and `b` alone.) -/
|
||||
/-- Constructs a fully universe-polymorphic dependent pair. -/
|
||||
mk ::
|
||||
/-- The first component of a dependent pair. If `p : @Sigma α β` then `p.1 : α`. -/
|
||||
/--
|
||||
The first component of a dependent pair.
|
||||
-/
|
||||
fst : α
|
||||
/-- The second component of a dependent pair. If `p : Sigma β` then `p.2 : β p.1`. -/
|
||||
/--
|
||||
The second component of a dependent pair. Its type depends on the first component.
|
||||
-/
|
||||
snd : β fst
|
||||
|
||||
/--
|
||||
@@ -514,10 +525,21 @@ export Singleton (singleton)
|
||||
class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] :
|
||||
Prop where
|
||||
/-- `insert x ∅ = {x}` -/
|
||||
insert_emptyc_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export LawfulSingleton (insert_emptyc_eq)
|
||||
insert_empty_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export LawfulSingleton (insert_empty_eq)
|
||||
|
||||
attribute [simp] insert_empty_eq
|
||||
|
||||
@[deprecated insert_empty_eq (since := "2025-03-12")]
|
||||
theorem insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
|
||||
[LawfulSingleton α β] (x : α) : (insert x ∅ : β) = singleton x :=
|
||||
insert_empty_eq _
|
||||
|
||||
@[deprecated insert_empty_eq (since := "2025-03-12")]
|
||||
theorem LawfulSingleton.insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
|
||||
[LawfulSingleton α β] (x : α) : (insert x ∅ : β) = singleton x :=
|
||||
insert_empty_eq _
|
||||
|
||||
attribute [simp] insert_emptyc_eq
|
||||
|
||||
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
|
||||
class Sep (α : outParam <| Type u) (γ : Type v) where
|
||||
@@ -676,9 +698,11 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
|
||||
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
|
||||
|
||||
/--
|
||||
`LawfulBEq α` is a typeclass which asserts that the `BEq α` implementation
|
||||
(which supplies the `a == b` notation) coincides with logical equality `a = b`.
|
||||
In other words, `a == b` implies `a = b`, and `a == a` is true.
|
||||
A Boolean equality test coincides with propositional equality.
|
||||
|
||||
In other words:
|
||||
* `a == b` implies `a = b`.
|
||||
* `a == a` is true.
|
||||
-/
|
||||
class LawfulBEq (α : Type u) [BEq α] : Prop where
|
||||
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
|
||||
@@ -935,9 +959,10 @@ namespace Decidable
|
||||
variable {p q : Prop}
|
||||
|
||||
/--
|
||||
Synonym for `dite` (dependent if-then-else). We can construct an element `q`
|
||||
(of any sort, not just a proposition) by cases on whether `p` is true or false,
|
||||
provided `p` is decidable.
|
||||
Construct a `q` if some proposition `p` is decidable, and both the truth and falsity of `p` are
|
||||
sufficient to construct a `q`.
|
||||
|
||||
This is a synonym for `dite`, the dependent if-then-else operator.
|
||||
-/
|
||||
@[macro_inline] def byCases {q : Sort u} [dec : Decidable p] (h1 : p → q) (h2 : ¬p → q) : q :=
|
||||
match dec with
|
||||
@@ -1070,22 +1095,28 @@ theorem nonempty_of_exists {α : Sort u} {p : α → Prop} : Exists (fun x => p
|
||||
/-! # Subsingleton -/
|
||||
|
||||
/--
|
||||
A "subsingleton" is a type with at most one element.
|
||||
In other words, it is either empty, or has a unique element.
|
||||
All propositions are subsingletons because of proof irrelevance, but some other types
|
||||
are subsingletons as well and they inherit many of the same properties as propositions.
|
||||
`Subsingleton α` is a typeclass, so it is usually used as an implicit argument and
|
||||
inferred by typeclass inference.
|
||||
A _subsingleton_ is a type with at most one element. It is either empty or has a unique element.
|
||||
|
||||
All propositions are subsingletons because of proof irrelevance: false propositions are empty, and
|
||||
all proofs of a true proposition are equal to one another. Some non-propositional types are also
|
||||
subsingletons.
|
||||
-/
|
||||
class Subsingleton (α : Sort u) : Prop where
|
||||
/-- Construct a proof that `α` is a subsingleton by showing that any two elements are equal. -/
|
||||
/-- Prove that `α` is a subsingleton by showing that any two elements are equal. -/
|
||||
intro ::
|
||||
/-- Any two elements of a subsingleton are equal. -/
|
||||
allEq : (a b : α) → a = b
|
||||
|
||||
/--
|
||||
If a type is a subsingleton, then all of its elements are equal.
|
||||
-/
|
||||
protected theorem Subsingleton.elim {α : Sort u} [h : Subsingleton α] : (a b : α) → a = b :=
|
||||
h.allEq
|
||||
|
||||
/--
|
||||
If two types are equal and one of them is a subsingleton, then all of their elements are
|
||||
[heterogeneously equal](lean-manual://section/HEq).
|
||||
-/
|
||||
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : HEq a b := by
|
||||
subst h₂
|
||||
apply heq_of_eq
|
||||
@@ -1123,22 +1154,21 @@ theorem recSubsingleton
|
||||
| isFalse h => h₄ h
|
||||
|
||||
/--
|
||||
An equivalence relation `~ : α → α → Prop` is a relation that is:
|
||||
An equivalence relation `r : α → α → Prop` is a relation that is
|
||||
|
||||
* reflexive: `x ~ x`
|
||||
* symmetric: `x ~ y` implies `y ~ x`
|
||||
* transitive: `x ~ y` and `y ~ z` implies `x ~ z`
|
||||
* reflexive: `r x x`,
|
||||
* symmetric: `r x y` implies `r y x`, and
|
||||
* transitive: `r x y` and `r y z` implies `r x z`.
|
||||
|
||||
Equality is an equivalence relation, and equivalence relations share many of
|
||||
the properties of equality. In particular, `Quot α r` is most well behaved
|
||||
when `r` is an equivalence relation, and in this case we use `Quotient` instead.
|
||||
Equality is an equivalence relation, and equivalence relations share many of the properties of
|
||||
equality.
|
||||
-/
|
||||
structure Equivalence {α : Sort u} (r : α → α → Prop) : Prop where
|
||||
/-- An equivalence relation is reflexive: `x ~ x` -/
|
||||
/-- An equivalence relation is reflexive: `r x x` -/
|
||||
refl : ∀ x, r x x
|
||||
/-- An equivalence relation is symmetric: `x ~ y` implies `y ~ x` -/
|
||||
/-- An equivalence relation is symmetric: `r x y` implies `r y x` -/
|
||||
symm : ∀ {x y}, r x y → r y x
|
||||
/-- An equivalence relation is transitive: `x ~ y` and `y ~ z` implies `x ~ z` -/
|
||||
/-- An equivalence relation is transitive: `r x y` and `r y z` implies `r x z` -/
|
||||
trans : ∀ {x y z}, r x y → r y z → r x z
|
||||
|
||||
/-- The empty relation is the relation on `α` which is always `False`. -/
|
||||
@@ -1210,12 +1240,12 @@ end Subtype
|
||||
section
|
||||
variable {α : Type u} {β : Type v}
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
@[reducible, inherit_doc PSum.inhabitedLeft]
|
||||
def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
|
||||
default := Sum.inl default
|
||||
|
||||
/-- This is not an instance to avoid non-canonical instances. -/
|
||||
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
@[reducible, inherit_doc PSum.inhabitedRight]
|
||||
def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
|
||||
default := Sum.inr default
|
||||
|
||||
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
|
||||
@@ -1275,7 +1305,12 @@ instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
|
||||
instance [BEq α] [BEq β] : BEq (α × β) where
|
||||
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂
|
||||
|
||||
/-- Lexicographical order for products -/
|
||||
/--
|
||||
Lexicographical order for products.
|
||||
|
||||
Two pairs are lexicographically ordered if their first elements are ordered or if their first
|
||||
elements are equal and their second elements are ordered.
|
||||
-/
|
||||
def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
|
||||
s.1 < t.1 ∨ (s.1 = t.1 ∧ s.2 < t.2)
|
||||
|
||||
@@ -1291,8 +1326,11 @@ theorem Prod.lexLt_def [LT α] [LT β] (s t : α × β) : (Prod.lexLt s t) = (s.
|
||||
theorem Prod.eta (p : α × β) : (p.1, p.2) = p := rfl
|
||||
|
||||
/--
|
||||
`Prod.map f g : α₁ × β₁ → α₂ × β₂` maps across a pair
|
||||
by applying `f` to the first component and `g` to the second.
|
||||
Transforms a pair by applying functions to both elements.
|
||||
|
||||
Examples:
|
||||
* `(1, 2).map (· + 1) (· * 3) = (2, 6)`
|
||||
* `(1, 2).map toString (· * 3) = ("1", 6)`
|
||||
-/
|
||||
def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂ : Type v₂}
|
||||
(f : α₁ → α₂) (g : β₁ → β₂) : α₁ × β₁ → α₂ × β₂
|
||||
@@ -1339,7 +1377,8 @@ instance : DecidableEq PUnit :=
|
||||
|
||||
/--
|
||||
A setoid is a type with a distinguished equivalence relation, denoted `≈`.
|
||||
This is mainly used as input to the `Quotient` type constructor.
|
||||
|
||||
The `Quotient` type constructor requires a `Setoid` instance.
|
||||
-/
|
||||
class Setoid (α : Sort u) where
|
||||
/-- `x ≈ y` is the distinguished equivalence relation of a setoid. -/
|
||||
@@ -1354,12 +1393,15 @@ namespace Setoid
|
||||
|
||||
variable {α : Sort u} [Setoid α]
|
||||
|
||||
/-- A setoid's equivalence relation is reflexive. -/
|
||||
theorem refl (a : α) : a ≈ a :=
|
||||
iseqv.refl a
|
||||
|
||||
/-- A setoid's equivalence relation is symmetric. -/
|
||||
theorem symm {a b : α} (hab : a ≈ b) : b ≈ a :=
|
||||
iseqv.symm hab
|
||||
|
||||
/-- A setoid's equivalence relation is transitive. -/
|
||||
theorem trans {a b c : α} (hab : a ≈ b) (hbc : b ≈ c) : a ≈ c :=
|
||||
iseqv.trans hab hbc
|
||||
|
||||
@@ -1576,34 +1618,21 @@ theorem imp_iff_not (hb : ¬b) : a → b ↔ ¬a := imp_congr_right fun _ => iff
|
||||
|
||||
namespace Quot
|
||||
/--
|
||||
The **quotient axiom**, or at least the nontrivial part of the quotient
|
||||
axiomatization. Quotient types are introduced by the `init_quot` command
|
||||
in `Init.Prelude` which introduces the axioms:
|
||||
The **quotient axiom**, which asserts the equality of elements related by the quotient's relation.
|
||||
|
||||
```
|
||||
opaque Quot {α : Sort u} (r : α → α → Prop) : Sort u
|
||||
The relation `r` does not need to be an equivalence relation to use this axiom. When `r` is not an
|
||||
equivalence relation, the quotient is with respect to the equivalence relation generated by `r`.
|
||||
|
||||
opaque Quot.mk {α : Sort u} (r : α → α → Prop) (a : α) : Quot r
|
||||
`Quot.sound` is part of the built-in primitive quotient type:
|
||||
* `Quot` is the built-in quotient type.
|
||||
* `Quot.mk` places elements of the underlying type `α` into the quotient.
|
||||
* `Quot.lift` allows the definition of functions from the quotient to some other type.
|
||||
* `Quot.ind` is used to write proofs about quotients by assuming that all elements are constructed
|
||||
with `Quot.mk`; it is analogous to the [recursor](lean-manual://section/recursors) for a
|
||||
structure.
|
||||
|
||||
opaque Quot.lift {α : Sort u} {r : α → α → Prop} {β : Sort v} (f : α → β) :
|
||||
(∀ a b : α, r a b → f a = f b) → Quot r → β
|
||||
|
||||
opaque Quot.ind {α : Sort u} {r : α → α → Prop} {β : Quot r → Prop} :
|
||||
(∀ a : α, β (Quot.mk r a)) → ∀ q : Quot r, β q
|
||||
```
|
||||
All of these axioms are true if we assume `Quot α r = α` and `Quot.mk` and
|
||||
`Quot.lift` are identity functions, so they do not add much. However this axiom
|
||||
cannot be explained in that way (it is false for that interpretation), so the
|
||||
real power of quotient types come from this axiom.
|
||||
|
||||
It says that the quotient by `r` maps elements which are related by `r` to equal
|
||||
values in the quotient. Together with `Quot.lift` which says that functions
|
||||
which respect `r` can be lifted to functions on the quotient, we can deduce that
|
||||
`Quot α r` exactly consists of the equivalence classes with respect to `r`.
|
||||
|
||||
It is important to note that `r` need not be an equivalence relation in this axiom.
|
||||
When `r` is not an equivalence relation, we are actually taking a quotient with
|
||||
respect to the equivalence relation generated by `r`.
|
||||
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
|
||||
Reference.
|
||||
-/
|
||||
axiom sound : ∀ {α : Sort u} {r : α → α → Prop} {a b : α}, r a b → Quot.mk r a = Quot.mk r b
|
||||
|
||||
@@ -1621,8 +1650,17 @@ protected theorem indBeta {α : Sort u} {r : α → α → Prop} {motive : Quot
|
||||
rfl
|
||||
|
||||
/--
|
||||
`Quot.liftOn q f h` is the same as `Quot.lift f h q`. It just reorders
|
||||
the argument `q : Quot r` to be first.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's relation.
|
||||
|
||||
Given a relation `r : α → α → Prop` and a quotient's value `q : Quot r`, applying a `f : α → β`
|
||||
requires a proof `c` that `f` respects `r`. In this case, `Quot.liftOn q f h : β` evaluates
|
||||
to the result of applying `f` to the underlying value in `α` from `q`.
|
||||
|
||||
`Quot.liftOn` is a version of the built-in primitive `Quot.lift` with its parameters re-ordered.
|
||||
|
||||
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
|
||||
Reference.
|
||||
-/
|
||||
protected abbrev liftOn {α : Sort u} {β : Sort v} {r : α → α → Prop}
|
||||
(q : Quot r) (f : α → β) (c : (a b : α) → r a b → f a = f b) : β :=
|
||||
@@ -1663,12 +1701,19 @@ protected theorem liftIndepPr1
|
||||
exact rfl
|
||||
|
||||
/--
|
||||
Dependent recursion principle for `Quot`. This constructor can be tricky to use,
|
||||
so you should consider the simpler versions if they apply:
|
||||
* `Quot.lift`, for nondependent functions
|
||||
* `Quot.ind`, for theorems / proofs of propositions about quotients
|
||||
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
|
||||
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
|
||||
A dependent recursion principle for `Quot`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
* `Quot.lift` is useful for defining non-dependent functions.
|
||||
* `Quot.ind` is useful for proving theorems about quotients.
|
||||
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
|
||||
`Quot.sound`.
|
||||
|
||||
`Quot.recOn` is a version of this recursor that takes the quotient parameter first.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev rec
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
@@ -1676,7 +1721,22 @@ so you should consider the simpler versions if they apply:
|
||||
(q : Quot r) : motive q :=
|
||||
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
|
||||
|
||||
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn
|
||||
/--
|
||||
A dependent recursion principle for `Quot` that takes the quotient first. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
* `Quot.lift` is useful for defining non-dependent functions.
|
||||
* `Quot.ind` is useful for proving theorems about quotients.
|
||||
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
|
||||
`Quot.sound`.
|
||||
|
||||
`Quot.rec` is a version of this recursor that takes the quotient parameter last.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev recOn
|
||||
(q : Quot r)
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
(h : (a b : α) → (p : r a b) → Eq.ndrec (f a) (sound p) = f b)
|
||||
@@ -1684,8 +1744,13 @@ so you should consider the simpler versions if they apply:
|
||||
q.rec f h
|
||||
|
||||
/--
|
||||
Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
|
||||
In this case the quotient's side condition is trivial so any function can be lifted.
|
||||
An alternative induction principle for quotients that can be used when the target type is a
|
||||
subsingleton, in which all elements are equal.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's relation is trivial, so any
|
||||
function can be lifted.
|
||||
|
||||
`Quot.rec` does not assume that the type is a subsingleton.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quot.mk r a))]
|
||||
@@ -1697,9 +1762,11 @@ In this case the quotient's side condition is trivial so any function can be lif
|
||||
apply Subsingleton.elim
|
||||
|
||||
/--
|
||||
Heterogeneous dependent recursion principle for a quotient.
|
||||
This may be easier to work with since it uses `HEq` instead of
|
||||
an `Eq.ndrec` in the hypothesis.
|
||||
A dependent recursion principle for `Quot` that uses [heterogeneous
|
||||
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
|
||||
a structure.
|
||||
|
||||
`Quot.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
|
||||
-/
|
||||
protected abbrev hrecOn
|
||||
(q : Quot r)
|
||||
@@ -1715,48 +1782,101 @@ end Quot
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
`Quotient α s` is the same as `Quot α r`, but it is specialized to a setoid `s`
|
||||
(that is, an equivalence relation) instead of an arbitrary relation.
|
||||
Prefer `Quotient` over `Quot` if your relation is actually an equivalence relation.
|
||||
Quotient types coarsen the propositional equality for a type so that terms related by some
|
||||
equivalence relation are considered equal. The equivalence relation is given by an instance of
|
||||
`Setoid`.
|
||||
|
||||
Set-theoretically, `Quotient s` can seen as the set of equivalence classes of `α` modulo the
|
||||
`Setoid` instance's relation `s.r`. Functions from `Quotient s` must prove that they respect `s.r`:
|
||||
to define a function `f : Quotient s → β`, it is necessary to provide `f' : α → β` and prove that
|
||||
for all `x : α` and `y : α`, `s.r x y → f' x = f' y`. `Quotient.lift` implements this operation.
|
||||
|
||||
The key quotient operators are:
|
||||
* `Quotient.mk` places elements of the underlying type `α` into the quotient.
|
||||
* `Quotient.lift` allows the definition of functions from the quotient to some other type.
|
||||
* `Quotient.sound` asserts the equality of elements related by `r`
|
||||
* `Quotient.ind` is used to write proofs about quotients by assuming that all elements are
|
||||
constructed with `Quotient.mk`.
|
||||
|
||||
`Quotient` is built on top of the primitive quotient type `Quot`, which does not require a proof
|
||||
that the relation is an equivalence relation. `Quotient` should be used instead of `Quot` for
|
||||
relations that actually are equivalence relations.
|
||||
-/
|
||||
def Quotient {α : Sort u} (s : Setoid α) :=
|
||||
@Quot α Setoid.r
|
||||
|
||||
namespace Quotient
|
||||
|
||||
/-- The canonical quotient map into a `Quotient`. -/
|
||||
/--
|
||||
Places an element of a type into the quotient that equates terms according to an equivalence
|
||||
relation.
|
||||
|
||||
The setoid instance is provided explicitly. `Quotient.mk'` uses instance synthesis instead.
|
||||
|
||||
Given `v : α`, `Quotient.mk s v : Quotient s` is like `v`, except all observations of `v`'s value
|
||||
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
|
||||
as the mapping respects `s.r`.
|
||||
-/
|
||||
@[inline]
|
||||
protected def mk {α : Sort u} (s : Setoid α) (a : α) : Quotient s :=
|
||||
Quot.mk Setoid.r a
|
||||
|
||||
/--
|
||||
The canonical quotient map into a `Quotient`.
|
||||
(This synthesizes the setoid by typeclass inference.)
|
||||
Places an element of a type into the quotient that equates terms according to an equivalence
|
||||
relation.
|
||||
|
||||
The equivalence relation is found by synthesizing a `Setoid` instance. `Quotient.mk` instead expects
|
||||
the instance to be provided explicitly.
|
||||
|
||||
Given `v : α`, `Quotient.mk' v : Quotient s` is like `v`, except all observations of `v`'s value
|
||||
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
|
||||
as the mapping respects `s.r`.
|
||||
|
||||
-/
|
||||
protected def mk' {α : Sort u} [s : Setoid α] (a : α) : Quotient s :=
|
||||
Quotient.mk s a
|
||||
|
||||
/--
|
||||
The analogue of `Quot.sound`: If `a` and `b` are related by the equivalence relation,
|
||||
then they have equal equivalence classes.
|
||||
The **quotient axiom**, which asserts the equality of elements related in the setoid.
|
||||
|
||||
Because `Quotient` is built on a lower-level type `Quot`, `Quotient.sound` is implemented as a
|
||||
theorem. It is derived from `Quot.sound`, the soundness axiom for the lower-level quotient type
|
||||
`Quot`.
|
||||
-/
|
||||
theorem sound {α : Sort u} {s : Setoid α} {a b : α} : a ≈ b → Quotient.mk s a = Quotient.mk s b :=
|
||||
Quot.sound
|
||||
|
||||
/--
|
||||
The analogue of `Quot.lift`: if `f : α → β` respects the equivalence relation `≈`,
|
||||
then it lifts to a function on `Quotient s` such that `lift f h (mk a) = f a`.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's equivalence relation.
|
||||
|
||||
Given `s : Setoid α` and a quotient `Quotient s`, applying a function `f : α → β` requires a proof
|
||||
`h` that `f` respects the equivalence relation `s.r`. In this case, the function
|
||||
`Quotient.lift f h : Quotient s → β` computes the same values as `f`.
|
||||
|
||||
`Quotient.liftOn` is a version of this operation that takes the quotient value as its first explicit
|
||||
parameter.
|
||||
-/
|
||||
protected abbrev lift {α : Sort u} {β : Sort v} {s : Setoid α} (f : α → β) : ((a b : α) → a ≈ b → f a = f b) → Quotient s → β :=
|
||||
Quot.lift f
|
||||
|
||||
/-- The analogue of `Quot.ind`: every element of `Quotient s` is of the form `Quotient.mk s a`. -/
|
||||
/--
|
||||
A reasoning principle for quotients that allows proofs about quotients to assume that all values are
|
||||
constructed with `Quotient.mk`.
|
||||
-/
|
||||
protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Prop} : ((a : α) → motive (Quotient.mk s a)) → (q : Quotient s) → motive q :=
|
||||
Quot.ind
|
||||
|
||||
/--
|
||||
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
|
||||
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
|
||||
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
|
||||
quotient's equivalence relation.
|
||||
|
||||
Given `s : Setoid α` and a quotient value `q : Quotient s`, applying a function `f : α → β` requires
|
||||
a proof `c` that `f` respects the equivalence relation `s.r`. In this case, the term
|
||||
`Quotient.liftOn q f h : β` reduces to the result of applying `f` to the underlying `α` value.
|
||||
|
||||
`Quotient.lift` is a version of this operation that takes the quotient value last, rather than
|
||||
first.
|
||||
-/
|
||||
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α → β) (c : (a b : α) → a ≈ b → f a = f b) : β :=
|
||||
Quot.liftOn q f c
|
||||
@@ -1777,7 +1897,21 @@ variable {α : Sort u}
|
||||
variable {s : Setoid α}
|
||||
variable {motive : Quotient s → Sort v}
|
||||
|
||||
/-- The analogue of `Quot.rec` for `Quotient`. See `Quot.rec`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
|
||||
* `Quotient.lift` is useful for defining non-dependent functions.
|
||||
* `Quotient.ind` is useful for proving theorems about quotients.
|
||||
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
|
||||
|
||||
`Quotient.recOn` is a version of this recursor that takes the quotient parameter first.
|
||||
-/
|
||||
@[inline, elab_as_elim]
|
||||
protected def rec
|
||||
(f : (a : α) → motive (Quotient.mk s a))
|
||||
@@ -1786,7 +1920,21 @@ protected def rec
|
||||
: motive q :=
|
||||
Quot.rec f h q
|
||||
|
||||
/-- The analogue of `Quot.recOn` for `Quotient`. See `Quot.recOn`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient`. It is analogous to the
|
||||
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
|
||||
is not necessarily a proposition.
|
||||
|
||||
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
|
||||
be easier to use:
|
||||
|
||||
* `Quotient.lift` is useful for defining non-dependent functions.
|
||||
* `Quotient.ind` is useful for proving theorems about quotients.
|
||||
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
|
||||
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
|
||||
|
||||
`Quotient.rec` is a version of this recursor that takes the quotient parameter last.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOn
|
||||
(q : Quotient s)
|
||||
@@ -1795,7 +1943,15 @@ protected abbrev recOn
|
||||
: motive q :=
|
||||
Quot.recOn q f h
|
||||
|
||||
/-- The analogue of `Quot.recOnSubsingleton` for `Quotient`. See `Quot.recOnSubsingleton`. -/
|
||||
/--
|
||||
An alternative recursion or induction principle for quotients that can be used when the target type
|
||||
is a subsingleton, in which all elements are equal.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
|
||||
so any function can be lifted.
|
||||
|
||||
`Quotient.rec` does not assume that the target type is a subsingleton.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quotient.mk s a))]
|
||||
@@ -1804,7 +1960,13 @@ protected abbrev recOnSubsingleton
|
||||
: motive q :=
|
||||
Quot.recOnSubsingleton (h := h) q f
|
||||
|
||||
/-- The analogue of `Quot.hrecOn` for `Quotient`. See `Quot.hrecOn`. -/
|
||||
/--
|
||||
A dependent recursion principle for `Quotient` that uses [heterogeneous
|
||||
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
|
||||
a structure.
|
||||
|
||||
`Quotient.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev hrecOn
|
||||
(q : Quotient s)
|
||||
@@ -1819,7 +1981,13 @@ universe uA uB uC
|
||||
variable {α : Sort uA} {β : Sort uB} {φ : Sort uC}
|
||||
variable {s₁ : Setoid α} {s₂ : Setoid β}
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
Lifts a binary function from the underlying types to a binary function on quotients. The function
|
||||
must respect both quotients' equivalence relations.
|
||||
|
||||
`Quotient.lift` is a version of this operation for unary functions. `Quotient.liftOn₂` is a version
|
||||
that take the quotient parameters first.
|
||||
-/
|
||||
protected abbrev lift₂
|
||||
(f : α → β → φ)
|
||||
(c : (a₁ : α) → (b₁ : β) → (a₂ : α) → (b₂ : β) → a₁ ≈ a₂ → b₁ ≈ b₂ → f a₁ b₁ = f a₂ b₂)
|
||||
@@ -1830,7 +1998,13 @@ protected abbrev lift₂
|
||||
induction q₂ using Quotient.ind
|
||||
apply c; assumption; apply Setoid.refl
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
Lifts a binary function from the underlying types to a binary function on quotients. The function
|
||||
must respect both quotients' equivalence relations.
|
||||
|
||||
`Quotient.liftOn` is a version of this operation for unary functions. `Quotient.lift₂` is a version
|
||||
that take the quotient parameters last.
|
||||
-/
|
||||
protected abbrev liftOn₂
|
||||
(q₁ : Quotient s₁)
|
||||
(q₂ : Quotient s₂)
|
||||
@@ -1895,6 +2069,9 @@ private theorem rel.refl {s : Setoid α} (q : Quotient s) : rel q q :=
|
||||
private theorem rel_of_eq {s : Setoid α} {q₁ q₂ : Quotient s} : q₁ = q₂ → rel q₁ q₂ :=
|
||||
fun h => Eq.ndrecOn h (rel.refl q₁)
|
||||
|
||||
/--
|
||||
If two values are equal in a quotient, then they are related by its equivalence relation.
|
||||
-/
|
||||
theorem exact {s : Setoid α} {a b : α} : Quotient.mk s a = Quotient.mk s b → a ≈ b :=
|
||||
fun h => rel_of_eq h
|
||||
|
||||
@@ -1905,7 +2082,13 @@ universe uA uB uC
|
||||
variable {α : Sort uA} {β : Sort uB}
|
||||
variable {s₁ : Setoid α} {s₂ : Setoid β}
|
||||
|
||||
/-- Lift a binary function to a quotient on both arguments. -/
|
||||
/--
|
||||
An alternative induction or recursion operator for defining binary operations on quotients that can
|
||||
be used when the target type is a subsingleton.
|
||||
|
||||
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
|
||||
so any function can be lifted.
|
||||
-/
|
||||
@[elab_as_elim]
|
||||
protected abbrev recOnSubsingleton₂
|
||||
{motive : Quotient s₁ → Quotient s₂ → Sort uC}
|
||||
@@ -1925,10 +2108,6 @@ protected abbrev recOnSubsingleton₂
|
||||
end
|
||||
end Quotient
|
||||
|
||||
section
|
||||
variable {α : Type u}
|
||||
variable (r : α → α → Prop)
|
||||
|
||||
instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : ∀ (a b : α), Decidable (a ≈ b)]
|
||||
: DecidableEq (Quotient s) :=
|
||||
fun (q₁ q₂ : Quotient s) =>
|
||||
@@ -1969,28 +2148,39 @@ instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingl
|
||||
/-! # Squash -/
|
||||
|
||||
/--
|
||||
`Squash α` is the quotient of `α` by the always true relation.
|
||||
It is empty if `α` is empty, otherwise it is a singleton.
|
||||
(Thus it is unconditionally a `Subsingleton`.)
|
||||
It is the "universal `Subsingleton`" mapped from `α`.
|
||||
The quotient of `α` by the universal relation. The elements of `Squash α` are those of `α`, but all
|
||||
of them are equal and cannot be distinguished.
|
||||
|
||||
It is similar to `Nonempty α`, which has the same properties, but unlike
|
||||
`Nonempty` this is a `Type u`, that is, it is "data", and the compiler
|
||||
represents an element of `Squash α` the same as `α` itself
|
||||
(as compared to `Nonempty α`, whose elements are represented by a dummy value).
|
||||
`Squash α` is a `Subsingleton`: it is empty if `α` is empty, otherwise it has just one element. It
|
||||
is the “universal `Subsingleton`” mapped from `α`.
|
||||
|
||||
`Squash.lift` will extract a value in any subsingleton `β` from a function on `α`,
|
||||
while `Nonempty.rec` can only do the same when `β` is a proposition.
|
||||
`Nonempty α` also has these properties. It is a proposition, which means that its elements (i.e.
|
||||
proofs) are erased from compiled code and represented by a dummy value. `Squash α` is a `Type u`,
|
||||
and its representation in compiled code is identical to that of `α`.
|
||||
|
||||
Consequently, `Squash.lift` may extract an `α` value into any subsingleton type `β`, while
|
||||
`Nonempty.rec` can only do the same when `β` is a proposition.
|
||||
-/
|
||||
def Squash (α : Sort u) := Quot (fun (_ _ : α) => True)
|
||||
|
||||
/-- The canonical quotient map into `Squash α`. -/
|
||||
/--
|
||||
Places a value into its squash type, in which it cannot be distinguished from any other.
|
||||
-/
|
||||
def Squash.mk {α : Sort u} (x : α) : Squash α := Quot.mk _ x
|
||||
|
||||
/--
|
||||
A reasoning principle that allows proofs about squashed types to assume that all values are
|
||||
constructed with `Squash.mk`.
|
||||
-/
|
||||
theorem Squash.ind {α : Sort u} {motive : Squash α → Prop} (h : ∀ (a : α), motive (Squash.mk a)) : ∀ (q : Squash α), motive q :=
|
||||
Quot.ind h
|
||||
|
||||
/-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/
|
||||
/--
|
||||
Extracts a squashed value into any subsingleton type.
|
||||
|
||||
If `β` is a subsingleton, a function `α → β` cannot distinguish between elements of `α` and thus
|
||||
automatically respects the universal relation that `Squash` quotients with.
|
||||
-/
|
||||
@[inline] def Squash.lift {α β} [Subsingleton β] (s : Squash α) (f : α → β) : β :=
|
||||
Quot.lift f (fun _ _ _ => Subsingleton.elim _ _) s
|
||||
|
||||
|
||||
@@ -555,6 +555,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Array.map_push]
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Array { x // p x }} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem size_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.unattach.size = xs.size := by
|
||||
unfold unattach
|
||||
@@ -676,6 +680,20 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [List.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.all f 0 stop = xs.unattach.all g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.any f 0 stop = xs.unattach.any g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {xs : Array { x // p x }}
|
||||
|
||||
@@ -34,7 +34,7 @@ variable {α : Type u}
|
||||
|
||||
namespace Array
|
||||
|
||||
@[deprecated toList (since := "2024-10-13")] abbrev data := @toList
|
||||
@[deprecated toList (since := "2024-09-10")] abbrev data := @toList
|
||||
|
||||
/-! ### Preliminary theorems -/
|
||||
|
||||
@@ -144,9 +144,9 @@ end List
|
||||
|
||||
namespace Array
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
|
||||
theorem size_eq_length_toList (xs : Array α) : xs.size = xs.toList.length := rfl
|
||||
|
||||
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
|
||||
|
||||
/-! ### Externs -/
|
||||
|
||||
@@ -252,7 +252,7 @@ instance [BEq α] : BEq (Array α) :=
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (emptyWithCapacity n) where
|
||||
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
@@ -503,7 +503,7 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
else
|
||||
pure bs
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
map 0 (mkEmpty as.size)
|
||||
map 0 (emptyWithCapacity as.size)
|
||||
|
||||
@[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM
|
||||
|
||||
@@ -520,7 +520,7 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
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 as[j] j_lt))
|
||||
map as.size 0 rfl (mkEmpty as.size)
|
||||
map as.size 0 rfl (emptyWithCapacity as.size)
|
||||
|
||||
@[inline]
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : Nat → α → m β) (as : Array α) : m (Array β) :=
|
||||
@@ -551,7 +551,7 @@ Note that the universe level is contrained to `Type` here,
|
||||
to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
|
||||
-/
|
||||
@[inline]
|
||||
def findM? {α : Type} {m : Type → Type} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := do
|
||||
def findM? {α : Type} [Monad m] (p : α → m Bool) (as : Array α) : m (Option α) := do
|
||||
for a in as do
|
||||
if (← p a) then
|
||||
return a
|
||||
|
||||
@@ -23,6 +23,18 @@ section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem _root_.List.countP_toArray (l : List α) : countP p l.toArray = l.countP p := by
|
||||
simp [countP]
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons hd tl ih =>
|
||||
simp only [List.foldr_cons, ih, List.countP_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem countP_toList (xs : Array α) : xs.toList.countP p = countP p xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos (xs) (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
|
||||
@@ -150,6 +162,13 @@ section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem _root_.List.count_toArray (l : List α) (a : α) : count a l.toArray = l.count a := by
|
||||
simp [count, List.count_eq_countP]
|
||||
|
||||
@[simp] theorem count_toList (xs : Array α) (a : α) : xs.toList.count a = xs.count a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
|
||||
|
||||
theorem count_push (a b : α) (xs : Array α) :
|
||||
|
||||
@@ -282,6 +282,10 @@ end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
|
||||
simp [eraseIdxIfInBounds, h]
|
||||
|
||||
theorem eraseIdx_eq_take_drop_succ (xs : Array α) (i : Nat) (h) : xs.eraseIdx i = xs.take i ++ xs.drop (i + 1) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.size_toArray] at h
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
|
||||
@@ -23,6 +23,13 @@ open Nat
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] (xs : Array α) (f : α → β) :
|
||||
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
|
||||
induction xs; simp_all
|
||||
|
||||
@[simp] theorem mapM_id {xs : Array α} {f : α → Id β} : xs.mapM f = xs.map f :=
|
||||
mapM_pure _ _
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {xs ys : Array α} :
|
||||
(xs ++ ys).mapM f = (return (← xs.mapM f) ++ (← ys.mapM f)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -162,7 +169,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(xs : Array α) (f : (a : α) → a ∈ xs → β → β) (init : β) :
|
||||
forIn' xs init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (xs.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
@@ -204,7 +211,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(xs : Array α) (f : α → β → β) (init : β) :
|
||||
forIn xs init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (xs.foldl (fun b a => f a b) init) := by
|
||||
@@ -224,6 +231,32 @@ theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
/-! ### allM and anyM -/
|
||||
|
||||
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] (p : α → Bool) (xs : Array α) :
|
||||
xs.anyM (m := m) (pure <| p ·) = pure (xs.any p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] (p : α → Bool) (xs : Array α) :
|
||||
xs.allM (m := m) (pure <| p ·) = pure (xs.all p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### findM? and findSomeM? -/
|
||||
|
||||
@[simp]
|
||||
theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α → Bool) (xs : Array α) :
|
||||
findM? (m := m) (pure <| p ·) xs = pure (xs.find? p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem findSomeM?_pure [Monad m] [LawfulMonad m] (f : α → Option β) (xs : Array α) :
|
||||
findSomeM? (m := m) (pure <| f ·) xs = pure (xs.findSome? f) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
@@ -354,12 +387,12 @@ and simplifies these to the function directly taking the value.
|
||||
simp
|
||||
rw [List.foldlM_subtype hf]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β → α → m β) :
|
||||
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
|
||||
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β → α → m β) (init : β) :
|
||||
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α → Prop) (xs : Array (Subtype P)) (f : β → α → m β) :
|
||||
xs.unattach.foldlM f = xs.foldlM fun b ⟨x, h⟩ =>
|
||||
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α → Prop) (xs : Array (Subtype P)) (f : β → α → m β) (init : β) :
|
||||
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b ⟨x, h⟩ =>
|
||||
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
|
||||
f b (wfParam x) := by
|
||||
simp [wfParam]
|
||||
@@ -378,12 +411,12 @@ and simplifies these to the function directly taking the value.
|
||||
rw [List.foldrM_subtype hf]
|
||||
|
||||
|
||||
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α → β → m β) :
|
||||
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
|
||||
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α → β → m β) (init : β) :
|
||||
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α → Prop) (xs : Array (Subtype P)) (f : α → β → m β) :
|
||||
xs.unattach.foldrM f = xs.foldrM fun ⟨x, h⟩ b =>
|
||||
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α → Prop) (xs : Array (Subtype P)) (f : α → β → m β) (init : β):
|
||||
xs.unattach.foldrM f init = xs.foldrM (init := init) fun ⟨x, h⟩ b =>
|
||||
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
|
||||
f (wfParam x) b := by
|
||||
simp [wfParam]
|
||||
|
||||
@@ -16,6 +16,25 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem ofFn_zero (f : Fin 0 → α) : ofFn f = #[] := rfl
|
||||
|
||||
theorem ofFn_succ (f : Fin (n+1) → α) :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split <;> rename_i h₃
|
||||
· rfl
|
||||
· congr
|
||||
simp at h₁ h₂
|
||||
omega
|
||||
|
||||
@[simp] theorem _rooy_.List.toArray_ofFn (f : Fin n → α) : (List.ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem toList_ofFn (f : Fin n → α) : (Array.ofFn f).toList = List.ofFn f := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
|
||||
rw [← Array.toList_inj]
|
||||
|
||||
@@ -10,15 +10,29 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
universe u v w
|
||||
|
||||
structure Subarray (α : Type u) where
|
||||
/--
|
||||
A region of some underlying array.
|
||||
|
||||
A subarray contains an array together with the start and end indices of a region of interest.
|
||||
Subarrays can be used to avoid copying or allocating space, while being more convenient than
|
||||
tracking the bounds by hand. The region of interest consists of every index that is both greater
|
||||
than or equal to `start` and strictly less than `stop`.
|
||||
-/
|
||||
structure Subarray (α : Type u) where
|
||||
/-- The underlying array. -/
|
||||
array : Array α
|
||||
/-- The starting index of the region of interest (inclusive). -/
|
||||
start : Nat
|
||||
/-- The ending index of the region of interest (exclusive). -/
|
||||
stop : Nat
|
||||
start_le_stop : start ≤ stop
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
/--
|
||||
Computes the size of the subarray.
|
||||
-/
|
||||
def size (s : Subarray α) : Nat :=
|
||||
s.stop - s.start
|
||||
|
||||
@@ -28,6 +42,11 @@ theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
|
||||
apply Nat.le_trans (Nat.sub_le stop start)
|
||||
assumption
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray.
|
||||
|
||||
The index is relative to the start of the subarray, rather than the underlying array.
|
||||
-/
|
||||
def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
have : s.start + i.val < s.array.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
|
||||
@@ -40,12 +59,33 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value `v₀` when the index is out of
|
||||
bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array.
|
||||
-/
|
||||
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
|
||||
if h : i < s.size then s[i] else v₀
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value when the index is out of bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array. The
|
||||
default value is that provided by the `Inhabited α` instance.
|
||||
-/
|
||||
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
getD s i default
|
||||
|
||||
/--
|
||||
Shrinks the subarray by incrementing its starting index if possible, returning it unchanged if not.
|
||||
|
||||
Examples:
|
||||
* `#[1,2,3].toSubarray.popFront.toArray = #[2, 3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.toArray = #[3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.toArray = #[]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.popFront.toArray = #[]`
|
||||
-/
|
||||
def popFront (s : Subarray α) : Subarray α :=
|
||||
if h : s.start < s.stop then
|
||||
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
@@ -54,6 +94,8 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
|
||||
/--
|
||||
The empty subarray.
|
||||
|
||||
This empty subarray is backed by an empty array.
|
||||
-/
|
||||
protected def empty : Subarray α where
|
||||
array := #[]
|
||||
@@ -88,46 +130,197 @@ protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Mona
|
||||
instance : ForIn m (Subarray α) α where
|
||||
forIn := Subarray.forIn
|
||||
|
||||
/--
|
||||
Folds a monadic operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn. The monad in question may permit
|
||||
early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := "") fun acc x => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(3)red (5)green (4)blue "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := 0) fun acc x => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Folds a monadic operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn, moving from the end to the
|
||||
start. The monad in question may permit early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := "") fun x acc => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(4)blue (5)green (3)red "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := 0) fun x acc => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.anyM fun x => do
|
||||
IO.println x
|
||||
pure (x == "blue")
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
true
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.anyM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.allM fun x => do
|
||||
IO.println x
|
||||
pure (x.length == 5)
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
false
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.allM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray.
|
||||
|
||||
The elements are processed starting at the lowest index and moving up.
|
||||
-/
|
||||
@[inline]
|
||||
def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forM f (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray, in reverse order.
|
||||
|
||||
The elements are processed starting at the highest index and moving down.
|
||||
-/
|
||||
@[inline]
|
||||
def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forRevM f (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Folds an operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each
|
||||
element of the subarray with the current accumulator value in turn.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.foldl (· + ·.length) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldlM f (init := init)
|
||||
|
||||
/--
|
||||
Folds an operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each element of the
|
||||
subarray with the current accumulator value in turn, moving from the end to the start.
|
||||
|
||||
Examples:
|
||||
* `#eval #["red", "green", "blue"].toSubarray.foldr (·.length + ·) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldlr (·.length + ·) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldrM f (init := init)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.anyM p
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def all {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.allM p
|
||||
|
||||
/--
|
||||
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
|
||||
element for which the function succeeds by returning a value other than `none`. The succeeding value
|
||||
is returned, or `none` if there is no success.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findSomeRevM? fun x => do
|
||||
IO.println x
|
||||
return Option.guard (· = 5) x.length
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Subarray α) (f : α → m (Option β)) : m (Option β) :=
|
||||
let rec @[specialize] find : (i : Nat) → i ≤ as.size → m (Option β)
|
||||
@@ -142,10 +335,39 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
find i this
|
||||
find as.size (Nat.le_refl _)
|
||||
|
||||
/--
|
||||
Applies a monadic Boolean predicate to each element in a subarray in reverse order, stopping at the
|
||||
first element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies it.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findRevM? fun x => do
|
||||
IO.println x
|
||||
return (x.length = 5)
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findRevM? {α : Type} {m : Type → Type w} [Monad m] (as : Subarray α) (p : α → m Bool) : m (Option α) :=
|
||||
as.findSomeRevM? fun a => return if (← p a) then some a else none
|
||||
|
||||
/--
|
||||
Tests each element in a subarray with a Boolean predicate in reverse order, stopping at the first
|
||||
element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies the predicate.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (·.length ≠ 4) = some "green"`
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (fun _ => true) = some "blue"`
|
||||
* `#["red", "green", "blue"].toSubarray 0 0 |>.findRev? (fun _ => true) = none`
|
||||
-/
|
||||
@[inline]
|
||||
def findRev? {α : Type} (as : Subarray α) (p : α → Bool) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
@@ -155,6 +377,12 @@ end Subarray
|
||||
namespace Array
|
||||
variable {α : Type u}
|
||||
|
||||
/--
|
||||
Returns a subarray of an array, with the given bounds.
|
||||
|
||||
If `start` or `stop` are not valid bounds for a subarray, then they are clamped to array's size.
|
||||
Additionally, the starting index is clamped to the ending index.
|
||||
-/
|
||||
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
|
||||
if h₂ : stop ≤ as.size then
|
||||
if h₁ : start ≤ stop then
|
||||
@@ -177,6 +405,9 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
|
||||
start_le_stop := Nat.le_refl _,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
|
||||
/--
|
||||
Allocates a new array that contains the contents of the subarray.
|
||||
-/
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
let mut as := mkEmpty (s.stop - s.start)
|
||||
@@ -186,8 +417,11 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
|
||||
instance : Coe (Subarray α) (Array α) := ⟨ofSubarray⟩
|
||||
|
||||
/-- A subarray with the provided bounds.-/
|
||||
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
|
||||
/-- A subarray with the provided lower bound that extends to the rest of the array. -/
|
||||
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
|
||||
/-- A subarray with the provided upper bound, starting at the index 0. -/
|
||||
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
|
||||
|
||||
macro_rules
|
||||
@@ -197,6 +431,7 @@ macro_rules
|
||||
|
||||
end Array
|
||||
|
||||
@[inherit_doc Array.ofSubarray]
|
||||
def Subarray.toArray (s : Subarray α) : Array α :=
|
||||
Array.ofSubarray s
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Subarray
|
||||
/--
|
||||
Splits a subarray into two parts.
|
||||
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
|
||||
of which contains the remainder.
|
||||
-/
|
||||
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
|
||||
let ⟨i', isLt⟩ := i
|
||||
|
||||
@@ -49,6 +49,14 @@ theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = fal
|
||||
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b → b == c → a == c :=
|
||||
PartialEquivBEq.trans
|
||||
|
||||
theorem BEq.congr_left [BEq α] [PartialEquivBEq α] {a b c : α} (h : a == b) :
|
||||
(a == c) = (b == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨BEq.trans (BEq.symm h), BEq.trans h⟩
|
||||
|
||||
theorem BEq.congr_right [BEq α] [PartialEquivBEq α] {a b c : α} (h : b == c) :
|
||||
(a == b) = (a == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨fun h' => BEq.trans h' h, fun h' => BEq.trans h' (BEq.symm h)⟩
|
||||
|
||||
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
|
||||
(a == b) = false → b == c → (a == c) = false :=
|
||||
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
|
||||
|
||||
@@ -109,7 +109,12 @@ open Nat Bool
|
||||
|
||||
namespace Bool
|
||||
|
||||
/-- At least two out of three booleans are true. -/
|
||||
/--
|
||||
At least two out of three Booleans are true.
|
||||
|
||||
This function is typically used to model addition of binary numbers, to combine a carry bit with two
|
||||
addend bits.
|
||||
-/
|
||||
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
|
||||
|
||||
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
|
||||
@@ -478,6 +483,36 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
|
||||
case zero => exact hmsb
|
||||
case succ => exact getMsbD_x _ hi (by omega)
|
||||
|
||||
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_ne_intMin`. -/
|
||||
@[simp] theorem signExtend_neg_of_le {v w : Nat} (h : w ≤ v) (b : BitVec v) :
|
||||
(-b).signExtend w = -b.signExtend w := by
|
||||
apply BitVec.eq_of_getElem_eq
|
||||
intro i hi
|
||||
simp only [getElem_signExtend, getElem_neg]
|
||||
rw [dif_pos (by omega), dif_pos (by omega)]
|
||||
simp only [getLsbD_signExtend, Bool.and_eq_true, decide_eq_true_eq, Bool.ite_eq_true_distrib,
|
||||
Bool.bne_right_inj, decide_eq_decide]
|
||||
exact ⟨fun ⟨j, hj₁, hj₂⟩ => ⟨j, ⟨hj₁, ⟨by omega, by rwa [if_pos (by omega)]⟩⟩⟩,
|
||||
fun ⟨j, hj₁, hj₂, hj₃⟩ => ⟨j, hj₁, by rwa [if_pos (by omega)] at hj₃⟩⟩
|
||||
|
||||
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_le`. -/
|
||||
@[simp] theorem signExtend_neg_of_ne_intMin {v w : Nat} (b : BitVec v) (hb : b ≠ intMin v) :
|
||||
(-b).signExtend w = -b.signExtend w := by
|
||||
refine (by omega : w ≤ v ∨ v < w).elim (fun h => signExtend_neg_of_le h b) (fun h => ?_)
|
||||
apply BitVec.eq_of_toInt_eq
|
||||
rw [toInt_signExtend_of_le (by omega), toInt_neg_of_ne_intMin hb, toInt_neg_of_ne_intMin,
|
||||
toInt_signExtend_of_le (by omega)]
|
||||
apply ne_of_apply_ne BitVec.toInt
|
||||
rw [toInt_signExtend_of_le (by omega), toInt_intMin_of_pos (by omega)]
|
||||
have := b.le_two_mul_toInt
|
||||
have : -2 ^ w < -2 ^ v := by
|
||||
apply Int.neg_lt_neg
|
||||
norm_cast
|
||||
rwa [Nat.pow_lt_pow_iff_right (by omega)]
|
||||
have : 2 * b.toInt ≠ -2 ^ w := by omega
|
||||
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
|
||||
omega
|
||||
|
||||
/-! ### abs -/
|
||||
|
||||
theorem msb_abs {w : Nat} {x : BitVec w} :
|
||||
@@ -544,6 +579,15 @@ theorem slt_eq_not_carry (x y : BitVec w) :
|
||||
theorem sle_eq_not_slt (x y : BitVec w) : x.sle y = !y.slt x := by
|
||||
simp only [BitVec.sle, BitVec.slt, ← decide_not, decide_eq_decide]; omega
|
||||
|
||||
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
|
||||
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
|
||||
|
||||
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x ↔ x.msb = false := by
|
||||
simp [zero_sle_eq_not_msb]
|
||||
|
||||
theorem toNat_toInt_of_sle {w : Nat} (b : BitVec w) (hb : BitVec.sle 0#w b) : b.toInt.toNat = b.toNat :=
|
||||
toNat_toInt_of_msb b (zero_sle_iff_msb_eq_false.1 hb)
|
||||
|
||||
theorem sle_eq_carry (x y : BitVec w) :
|
||||
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
|
||||
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
|
||||
@@ -1244,8 +1288,8 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
simp only [saddOverflow]
|
||||
rcases w with _|w
|
||||
· revert x y; decide
|
||||
· have := le_toInt (x := x); have := toInt_lt (x := x)
|
||||
have := le_toInt (x := y); have := toInt_lt (x := y)
|
||||
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x)
|
||||
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
|
||||
simp only [← decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, ← decide_not, ← decide_and,
|
||||
decide_eq_decide]
|
||||
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
|
||||
|
||||
@@ -13,7 +13,9 @@ import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Nat.Mod
|
||||
import Init.Data.Nat.Div.Lemmas
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.Pow
|
||||
import Init.Data.Int.LemmasAux
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
@@ -239,12 +241,16 @@ theorem eq_of_getMsbD_eq {x y : BitVec w}
|
||||
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp [← getLsbD_eq_getElem]
|
||||
|
||||
theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
|
||||
theorem toInt_zero_length (x : BitVec 0) : x.toInt = 0 := by simp [of_length_zero]
|
||||
|
||||
theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by simp
|
||||
theorem getMsbD_zero_length (x : BitVec 0) : x.getMsbD i = false := by simp
|
||||
theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
|
||||
|
||||
theorem toNat_of_zero_length (h : w = 0) (x : BitVec w) : x.toNat = 0 := by
|
||||
subst h; simp [toNat_zero_length]
|
||||
theorem toInt_of_zero_length (h : w = 0) (x : BitVec w) : x.toInt = 0 := by
|
||||
subst h; simp [toInt_zero_length]
|
||||
theorem getLsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getLsbD i = false := by
|
||||
subst h; simp [getLsbD_zero_length]
|
||||
theorem getMsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getMsbD i = false := by
|
||||
@@ -323,8 +329,25 @@ theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
|
||||
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
|
||||
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
|
||||
|
||||
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
|
||||
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
|
||||
|
||||
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl
|
||||
|
||||
@[simp] theorem finMk_toNat (x : BitVec w) : Fin.mk x.toNat x.isLt = x.toFin := rfl
|
||||
|
||||
@[simp] theorem toFin_ofNatLT {n : Nat} (h : n < 2 ^ w) : (BitVec.ofNatLT n h).toFin = Fin.mk n h := rfl
|
||||
|
||||
@[simp] theorem toFin_ofFin (n : Fin (2 ^ w)) : (BitVec.ofFin n).toFin = n := rfl
|
||||
@[simp] theorem ofFin_toFin (x : BitVec w) : BitVec.ofFin x.toFin = x := rfl
|
||||
|
||||
@[simp] theorem ofNatLT_finVal (n : Fin (2 ^ w)) : BitVec.ofNatLT n.val n.isLt = BitVec.ofFin n := rfl
|
||||
|
||||
@[simp] theorem ofNatLT_toNat (x : BitVec w) : BitVec.ofNatLT x.toNat x.isLt = x := rfl
|
||||
|
||||
@[simp] theorem ofNat_finVal (n : Fin (2 ^ w)) : BitVec.ofNat w n.val = BitVec.ofFin n := by
|
||||
rw [← BitVec.ofNatLT_eq_ofNat n.isLt, ofNatLT_finVal]
|
||||
|
||||
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
|
||||
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
|
||||
theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
|
||||
@@ -528,6 +551,9 @@ theorem toInt_eq_toNat_of_msb {x : BitVec w} (h : x.msb = false) :
|
||||
x.toInt = x.toNat := by
|
||||
simp [toInt_eq_msb_cond, h]
|
||||
|
||||
theorem toNat_toInt_of_msb {w : Nat} (b : BitVec w) (hb : b.msb = false) : b.toInt.toNat = b.toNat := by
|
||||
simp [b.toInt_eq_toNat_of_msb hb]
|
||||
|
||||
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
|
||||
simp only [toInt_eq_toNat_cond]
|
||||
split
|
||||
@@ -538,6 +564,16 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
|
||||
rw [Int.bmod_neg] <;> simp only [←Int.ofNat_emod, toNat_mod_cancel]
|
||||
omega
|
||||
|
||||
theorem toInt_neg_of_msb_true {x : BitVec w} (h : x.msb = true) : x.toInt < 0 := by
|
||||
simp only [BitVec.toInt]
|
||||
have : 2 * x.toNat ≥ 2 ^ w := msb_eq_true_iff_two_mul_ge.mp h
|
||||
omega
|
||||
|
||||
theorem toInt_nonneg_of_msb_false {x : BitVec w} (h : x.msb = false) : 0 ≤ x.toInt := by
|
||||
simp only [BitVec.toInt]
|
||||
have : 2 * x.toNat < 2 ^ w := msb_eq_false_iff_two_mul_lt.mp h
|
||||
omega
|
||||
|
||||
/-- Prove equality of bitvectors in terms of nat operations. -/
|
||||
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt → x = y := by
|
||||
intro eq
|
||||
@@ -569,6 +605,11 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||||
have p : 0 ≤ i % (2^n : Nat) := by omega
|
||||
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
|
||||
|
||||
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
|
||||
(h : -2 ^ (w - 1) ≤ n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
|
||||
have hw : w = (w - 1) + 1 := by omega
|
||||
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
|
||||
|
||||
@[simp] theorem ofInt_natCast (w n : Nat) :
|
||||
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
|
||||
|
||||
@@ -604,26 +645,50 @@ theorem toInt_zero {w : Nat} : (0#w).toInt = 0 := by
|
||||
`x.toInt` is less than `2^(w-1)`.
|
||||
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
|
||||
-/
|
||||
theorem toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
|
||||
theorem two_mul_toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
|
||||
simp only [BitVec.toInt]
|
||||
rcases w with _|w'
|
||||
· omega
|
||||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||||
norm_cast; omega
|
||||
|
||||
theorem two_mul_toInt_le {w : Nat} {x : BitVec w} : 2 * x.toInt ≤ 2 ^ w - 1 :=
|
||||
Int.le_sub_one_of_lt two_mul_toInt_lt
|
||||
|
||||
theorem toInt_lt {w : Nat} {x : BitVec w} : x.toInt < 2 ^ (w - 1) := by
|
||||
by_cases h : w = 0
|
||||
· subst h
|
||||
simp [eq_nil x]
|
||||
· have := @two_mul_toInt_lt w x
|
||||
rw_mod_cast [← Nat.two_pow_pred_add_two_pow_pred (by omega), Int.mul_comm, Int.natCast_add] at this
|
||||
omega
|
||||
|
||||
theorem toInt_le {w : Nat} {x : BitVec w} : x.toInt ≤ 2 ^ (w - 1) - 1 :=
|
||||
Int.le_sub_one_of_lt toInt_lt
|
||||
|
||||
/--
|
||||
`x.toInt` is greater than or equal to `-2^(w-1)`.
|
||||
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
|
||||
-/
|
||||
theorem le_toInt {w : Nat} {x : BitVec w} : -2 ^ w ≤ 2 * x.toInt := by
|
||||
theorem le_two_mul_toInt {w : Nat} {x : BitVec w} : -2 ^ w ≤ 2 * x.toInt := by
|
||||
simp only [BitVec.toInt]
|
||||
rcases w with _|w'
|
||||
· omega
|
||||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
|
||||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||||
norm_cast; omega
|
||||
|
||||
|
||||
theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) ≤ x.toInt := by
|
||||
by_cases h : w = 0
|
||||
· subst h
|
||||
simp [BitVec.eq_nil x]
|
||||
· have := le_two_mul_toInt (w := w) (x := x)
|
||||
generalize x.toInt = x at *
|
||||
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
|
||||
omega
|
||||
|
||||
/-! ### slt -/
|
||||
|
||||
/--
|
||||
@@ -645,6 +710,12 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
|
||||
simp [BitVec.slt, this]
|
||||
omega
|
||||
|
||||
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
|
||||
rw [Bool.eq_iff_iff, BitVec.slt_zero_iff_msb_cond]
|
||||
|
||||
theorem sle_iff_toInt_le {w : Nat} {b b' : BitVec w} : b.sle b' ↔ b.toInt ≤ b'.toInt :=
|
||||
decide_eq_true_iff
|
||||
|
||||
/-! ### setWidth, zeroExtend and truncate -/
|
||||
|
||||
@[simp]
|
||||
@@ -996,6 +1067,11 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
|
||||
apply eq_of_toNat_eq
|
||||
simp [extractLsb, show len - 1 + 1 = len by omega]
|
||||
|
||||
/-- Extracting all the bits of a bitvector is an identity operation. -/
|
||||
@[simp] theorem extractLsb'_eq_self {x : BitVec w} : x.extractLsb' 0 w = x := by
|
||||
apply eq_of_toNat_eq
|
||||
simp [extractLsb']
|
||||
|
||||
/-! ### allOnes -/
|
||||
|
||||
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||||
@@ -1467,6 +1543,16 @@ theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi
|
||||
simp [hk, show k ≤ hi - lo by omega]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem ne_not_self {a : BitVec w} (h : 0 < w) : a ≠ ~~~a := by
|
||||
have : ∃ x, x < w := ⟨w - 1, by omega⟩
|
||||
simp [BitVec.eq_of_getElem_eq_iff, this]
|
||||
|
||||
@[simp]
|
||||
theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a ≠ a := by
|
||||
rw [ne_comm]
|
||||
simp [h]
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
|
||||
@@ -1735,7 +1821,7 @@ theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
simp [hn]
|
||||
|
||||
@[simp]
|
||||
theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
|
||||
theorem toFin_ushiftRight {x : BitVec w} {n : Nat} :
|
||||
(x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
by_cases hn : n < w
|
||||
@@ -1941,11 +2027,118 @@ theorem getMsbD_sshiftRight {x : BitVec w} {i n : Nat} :
|
||||
by_cases h₄ : n + (w - 1 - i) < w <;> (simp only [h₄, ↓reduceIte]; congr; omega)
|
||||
· simp [h]
|
||||
|
||||
theorem toInt_shiftRight_lt {x : BitVec w} {n : Nat} :
|
||||
x.toInt >>> n < 2 ^ (w - 1) := by
|
||||
have := @Int.shiftRight_le_of_nonneg x.toInt n
|
||||
have := @Int.shiftRight_le_of_nonpos x.toInt n
|
||||
have := @BitVec.toInt_lt w x
|
||||
have := @Nat.one_le_two_pow (w-1)
|
||||
norm_cast at *
|
||||
omega
|
||||
|
||||
theorem le_toInt_shiftRight {x : BitVec w} {n : Nat} :
|
||||
-(2 ^ (w - 1)) ≤ x.toInt >>> n := by
|
||||
have := @Int.le_shiftRight_of_nonpos x.toInt n
|
||||
have := @Int.le_shiftRight_of_nonneg x.toInt n
|
||||
have := @BitVec.le_toInt w x
|
||||
have := @Nat.one_le_two_pow (w-1)
|
||||
norm_cast at *
|
||||
omega
|
||||
|
||||
theorem toNat_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
|
||||
(x.sshiftRight n).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n := by
|
||||
simp [sshiftRight_eq_of_msb_true, h]
|
||||
|
||||
theorem toNat_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
|
||||
(x.sshiftRight n).toNat = x.toNat >>> n := by
|
||||
simp [sshiftRight_eq_of_msb_false, h]
|
||||
|
||||
theorem toNat_sshiftRight {x : BitVec w} {n : Nat} :
|
||||
(x.sshiftRight n).toNat =
|
||||
if x.msb
|
||||
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n
|
||||
else x.toNat >>> n := by
|
||||
by_cases h : x.msb
|
||||
· simp [toNat_sshiftRight_of_msb_true, h]
|
||||
· rw [Bool.not_eq_true] at h
|
||||
simp [toNat_sshiftRight_of_msb_false, h]
|
||||
|
||||
theorem toFin_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
|
||||
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp only [val_toFin, toNat_sshiftRight, h, ↓reduceIte, Fin.val_ofNat']
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
have := x.isLt
|
||||
have ineq : ∀ y, 2 ^ w - 1 - y < 2 ^ w := by omega
|
||||
exact ineq ((2 ^ w - 1 - x.toNat) >>> n)
|
||||
|
||||
theorem toFin_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
|
||||
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (x.toNat >>> n) := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp only [val_toFin, toNat_sshiftRight, h, Bool.false_eq_true, ↓reduceIte, Fin.val_ofNat']
|
||||
have := Nat.shiftRight_le x.toNat n
|
||||
rw [Nat.mod_eq_of_lt (by omega)]
|
||||
|
||||
theorem toFin_sshiftRight {x : BitVec w} {n : Nat} :
|
||||
(x.sshiftRight n).toFin =
|
||||
if x.msb
|
||||
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n)
|
||||
else Fin.ofNat' (2^w) (x.toNat >>> n) := by
|
||||
by_cases h : x.msb
|
||||
· simp [toFin_sshiftRight_of_msb_true, h]
|
||||
· simp [toFin_sshiftRight_of_msb_false, h]
|
||||
|
||||
@[simp]
|
||||
theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
|
||||
(x.sshiftRight n).toInt = x.toInt >>> n := by
|
||||
by_cases h : w = 0
|
||||
· subst h
|
||||
simp [BitVec.eq_nil x]
|
||||
· rw [sshiftRight, toInt_ofInt, ←Nat.two_pow_pred_add_two_pow_pred (by omega)]
|
||||
have := @toInt_shiftRight_lt w x n
|
||||
have := @le_toInt_shiftRight w x n
|
||||
norm_cast at *
|
||||
exact Int.bmod_eq_self_of_le (by omega) (by omega)
|
||||
|
||||
/-! ### sshiftRight reductions from BitVec to Nat -/
|
||||
|
||||
@[simp]
|
||||
theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl
|
||||
|
||||
theorem toNat_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
|
||||
(x.sshiftRight' y).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat := by
|
||||
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_true h]
|
||||
|
||||
theorem toNat_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
|
||||
(x.sshiftRight' y).toNat = x.toNat >>> y.toNat := by
|
||||
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_false h]
|
||||
|
||||
theorem toNat_sshiftRight' {x y : BitVec w} :
|
||||
(x.sshiftRight' y).toNat =
|
||||
if x.msb
|
||||
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat
|
||||
else x.toNat >>> y.toNat := by
|
||||
rw [sshiftRight_eq', toNat_sshiftRight]
|
||||
|
||||
theorem toFin_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
|
||||
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat) := by
|
||||
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_true h]
|
||||
|
||||
theorem toFin_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
|
||||
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
|
||||
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_false h]
|
||||
|
||||
theorem toFin_sshiftRight' {x y : BitVec w} :
|
||||
(x.sshiftRight' y).toFin =
|
||||
if x.msb
|
||||
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat)
|
||||
else Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
|
||||
rw [sshiftRight_eq', toFin_sshiftRight]
|
||||
|
||||
theorem toInt_sshiftRight' {x y : BitVec w} :
|
||||
(x.sshiftRight' y).toInt = x.toInt >>> y.toNat := by
|
||||
rw [sshiftRight_eq', toInt_sshiftRight]
|
||||
|
||||
-- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form.
|
||||
theorem getLsbD_sshiftRight' {x y : BitVec w} {i : Nat} :
|
||||
getLsbD (x.sshiftRight' y) i =
|
||||
@@ -2036,14 +2229,18 @@ theorem msb_signExtend {x : BitVec w} :
|
||||
· simp [h, BitVec.msb, getMsbD_signExtend, show ¬ (v - w = 0) by omega]
|
||||
|
||||
/-- Sign extending to a width smaller than the starting width is a truncation. -/
|
||||
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v ≤ w):
|
||||
theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v ≤ w) :
|
||||
x.signExtend v = x.setWidth v := by
|
||||
ext i h
|
||||
simp [getElem_signExtend, show i < w by omega]
|
||||
|
||||
@[deprecated signExtend_eq_setWidth_of_le (since := "2025-03-07")]
|
||||
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v ≤ w) :
|
||||
x.signExtend v = x.setWidth v := signExtend_eq_setWidth_of_le x hv
|
||||
|
||||
/-- Sign extending to the same bitwidth is a no op. -/
|
||||
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
|
||||
rw [signExtend_eq_setWidth_of_lt _ (Nat.le_refl _), setWidth_eq]
|
||||
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
|
||||
rw [signExtend_eq_setWidth_of_le _ (Nat.le_refl _), setWidth_eq]
|
||||
|
||||
/-- Sign extending to a larger bitwidth depends on the msb.
|
||||
If the msb is false, then the result equals the original value.
|
||||
@@ -2080,47 +2277,65 @@ theorem toNat_signExtend (x : BitVec w) {v : Nat} :
|
||||
(x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0 := by
|
||||
by_cases h : v ≤ w
|
||||
· have : 2^v ≤ 2^w := Nat.pow_le_pow_right Nat.two_pos h
|
||||
simp [signExtend_eq_setWidth_of_lt x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
|
||||
simp [signExtend_eq_setWidth_of_le x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
|
||||
· have : 2^w ≤ 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega)
|
||||
rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)]
|
||||
|
||||
/-
|
||||
/--
|
||||
If the current width `w` is smaller than the extended width `v`,
|
||||
then the value when interpreted as an integer does not change.
|
||||
-/
|
||||
theorem toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
|
||||
theorem toInt_signExtend_of_le {x : BitVec w} (h : w ≤ v) :
|
||||
(x.signExtend v).toInt = x.toInt := by
|
||||
simp only [toInt_eq_msb_cond, toNat_signExtend]
|
||||
have : (x.signExtend v).msb = x.msb := by
|
||||
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
|
||||
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
|
||||
by_cases hlt : w < v
|
||||
· rw [toInt_signExtend_of_lt hlt]
|
||||
· obtain rfl : w = v := by omega
|
||||
simp
|
||||
where
|
||||
toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
|
||||
(x.signExtend v).toInt = x.toInt := by
|
||||
simp only [toInt_eq_msb_cond, toNat_signExtend]
|
||||
have : (x.signExtend v).msb = x.msb := by
|
||||
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
|
||||
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
|
||||
omega
|
||||
have H : 2^w ≤ 2^v := Nat.pow_le_pow_right (by omega) (by omega)
|
||||
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
|
||||
by_cases h : x.msb
|
||||
<;> norm_cast
|
||||
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
|
||||
omega
|
||||
have H : 2^w ≤ 2^v := Nat.pow_le_pow_right (by omega) (by omega)
|
||||
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
|
||||
by_cases h : x.msb
|
||||
<;> norm_cast
|
||||
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
|
||||
omega
|
||||
|
||||
/-
|
||||
/--
|
||||
If the current width `w` is larger than the extended width `v`,
|
||||
then the value when interpreted as an integer is truncated,
|
||||
and we compute a modulo by `2^v`.
|
||||
-/
|
||||
theorem toInt_signExtend_of_le {x : BitVec w} (hv : v ≤ w) :
|
||||
theorem toInt_signExtend_eq_toNat_bmod_of_le {x : BitVec w} (hv : v ≤ w) :
|
||||
(x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by
|
||||
simp [signExtend_eq_setWidth_of_lt _ hv]
|
||||
simp [signExtend_eq_setWidth_of_le _ hv]
|
||||
|
||||
/-
|
||||
/--
|
||||
Interpreting the sign extension of `(x : BitVec w)` to width `v`
|
||||
computes `x % 2^v` (where `%` is the balanced mod).
|
||||
computes `x % 2^v` (where `%` is the balanced mod). See `toInt_signExtend` for a version stated
|
||||
in terms of `toInt` instead of `toNat`.
|
||||
-/
|
||||
theorem toInt_signExtend (x : BitVec w) :
|
||||
(x.signExtend v).toInt = Int.bmod x.toNat (2^(min v w)) := by
|
||||
theorem toInt_signExtend_eq_toNat_bmod (x : BitVec w) :
|
||||
(x.signExtend v).toInt = Int.bmod x.toNat (2 ^ min v w) := by
|
||||
by_cases hv : v ≤ w
|
||||
· simp [toInt_signExtend_of_le hv, Nat.min_eq_left hv]
|
||||
· simp [toInt_signExtend_eq_toNat_bmod_of_le hv, Nat.min_eq_left hv]
|
||||
· simp only [Nat.not_le] at hv
|
||||
rw [toInt_signExtend_of_lt hv, Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
|
||||
rw [toInt_signExtend_of_le (Nat.le_of_lt hv),
|
||||
Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
|
||||
|
||||
theorem toInt_signExtend (x : BitVec w) :
|
||||
(x.signExtend v).toInt = x.toInt.bmod (2 ^ min v w) := by
|
||||
rw [toInt_signExtend_eq_toNat_bmod, BitVec.toInt_eq_toNat_bmod, Int.bmod_bmod_of_dvd]
|
||||
exact Nat.pow_dvd_pow _ (Nat.min_le_right v w)
|
||||
|
||||
theorem toInt_signExtend_eq_toInt_bmod_of_le (x : BitVec w) (h : v ≤ w) :
|
||||
(x.signExtend v).toInt = x.toInt.bmod (2 ^ v) := by
|
||||
rw [BitVec.toInt_signExtend, Nat.min_eq_left h]
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
@@ -2265,6 +2480,42 @@ theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
|
||||
(x <<< n).msb = x.getMsbD n := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
/--
|
||||
A `(x : BitVec v)` set to width `w` equals `(v - w)` zeros,
|
||||
followed by the low `(min v w) bits of `x`
|
||||
-/
|
||||
theorem setWidth_eq_append_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} :
|
||||
x.setWidth w = ((0#(w - v)) ++ x.extractLsb' 0 (min v w)).cast (by omega) := by
|
||||
ext i hi
|
||||
simp only [getElem_cast, getElem_append]
|
||||
by_cases hiv : i < v
|
||||
· simp [hi]
|
||||
omega
|
||||
· simp [getLsbD_ge x i (by omega)]
|
||||
|
||||
/--
|
||||
A `(x : BitVec v)` set to a width `w ≥ v` equals `(w - v)` zeros, followed by `x`.
|
||||
-/
|
||||
theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
|
||||
x.setWidth w = ((0#(w - v)) ++ x).cast (by omega) := by
|
||||
rw [setWidth_eq_append_extractLsb']
|
||||
ext i hi
|
||||
simp only [getElem_cast, getElem_append]
|
||||
by_cases hiv : i < v
|
||||
· simp [hiv]
|
||||
omega
|
||||
· simp [hiv, getLsbD_ge x i (by omega)]
|
||||
|
||||
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w ≤ v) :
|
||||
x.setWidth w = x.extractLsb' 0 w := by
|
||||
rw [setWidth_eq_append_extractLsb']
|
||||
ext i hi
|
||||
simp only [getElem_cast, getElem_append]
|
||||
by_cases hiv : i < v
|
||||
· simp [hi]
|
||||
omega
|
||||
· simp [getLsbD_ge x i (by omega)]
|
||||
|
||||
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
|
||||
@@ -2282,6 +2533,120 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||||
· simp [hi']
|
||||
· simp [hi', show i - n < w by omega]
|
||||
|
||||
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
|
||||
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||||
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
|
||||
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||||
ext i h
|
||||
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
|
||||
Nat.not_lt]
|
||||
intros hi
|
||||
congr 1
|
||||
omega
|
||||
|
||||
/-- Combine adjacent `~~~ (extractLsb _)'` operations into a single `~~~ (extractLsb _)'`. -/
|
||||
theorem not_extractLsb'_append_not_extractLsb'_eq_not_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||||
(~~~ (x.extractLsb' start₂ len₂) ++ ~~~ (x.extractLsb' start₁ len₁)) =
|
||||
(~~~ x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||||
ext i h
|
||||
simp only [getElem_cast, getElem_not, getElem_extractLsb', getElem_append]
|
||||
by_cases hi : i < len₁
|
||||
· simp [hi]
|
||||
· simp only [hi, ↓reduceDIte, Bool.not_eq_eq_eq_not, Bool.not_not]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
/-- A sign extension of `x : BitVec w` equals high bits of either `0` or `1` depending on `x.msb`,
|
||||
followed by the low bits of `x`. -/
|
||||
theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
|
||||
x.signExtend v =
|
||||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x.extractLsb' 0 (min w v)).cast (by omega) := by
|
||||
ext i hi
|
||||
simp only [getElem_cast]
|
||||
cases hx : x.msb
|
||||
· simp only [hx, signExtend_eq_setWidth_of_msb_false, getElem_setWidth, Bool.false_eq_true,
|
||||
↓reduceIte, getElem_append, getElem_extractLsb', Nat.zero_add, getElem_zero, dite_eq_ite,
|
||||
Bool.if_false_right, Bool.iff_and_self, decide_eq_true_eq]
|
||||
intros hi
|
||||
have hw : i < w := lt_of_getLsbD hi
|
||||
omega
|
||||
· simp [signExtend_eq_not_setWidth_not_of_msb_true hx, getElem_append, Nat.lt_min, hi]
|
||||
|
||||
/-- A sign extension of `x : BitVec w` to a larger bitwidth `v ≥ w`
|
||||
equals high bits of either `0` or `1` depending on `x.msb`, followed by `x`. -/
|
||||
theorem signExtend_eq_append_of_le {w v : Nat} {x : BitVec w} (h : w ≤ v) :
|
||||
x.signExtend v =
|
||||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x).cast (by omega) := by
|
||||
ext i hi
|
||||
cases hx : x.msb <;>
|
||||
simp [getElem_cast, hx, getElem_append, getElem_signExtend]
|
||||
|
||||
/--
|
||||
The 'master theorem' for extracting bits from `(xhi ++ xlo)`,
|
||||
which performs a case analysis on the start index, length, and the lengths of `xlo, xhi`.
|
||||
· If the start index is entirely out of the `xlo` bitvector, then grab the bits from `xhi`.
|
||||
· If the start index is entirely contained in the `xlo` bitvector, then grab the bits from `xlo`.
|
||||
· If the start index is split between the two bitvectors,
|
||||
then append `(w - start)` bits from `xlo` with `(len - (w - start))` bits from xhi.
|
||||
Diagramatically:
|
||||
```
|
||||
xhi xlo
|
||||
(<---------------------](<-------w--------]
|
||||
start+len..start: (<-----len---*------]
|
||||
w - start: *------*
|
||||
len - (w -start): *------------*
|
||||
```
|
||||
-/
|
||||
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
|
||||
extractLsb' start len (xhi ++ xlo) =
|
||||
if hstart : start < w
|
||||
then
|
||||
if hlen : start + len < w
|
||||
then extractLsb' start len xlo
|
||||
else
|
||||
(((extractLsb' (start - w) (len - (w - start)) xhi) ++
|
||||
extractLsb' start (w - start) xlo)).cast (by omega)
|
||||
else
|
||||
extractLsb' (start - w) len xhi := by
|
||||
by_cases hstart : start < w
|
||||
· simp only [hstart, ↓reduceDIte]
|
||||
by_cases hlen : start + len < w
|
||||
· simp only [hlen, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp only [getElem_extractLsb', getLsbD_append, ite_eq_left_iff, Nat.not_lt]
|
||||
intros hcontra
|
||||
omega
|
||||
· simp only [hlen, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp only [getElem_extractLsb', getLsbD_append, getElem_cast,
|
||||
getElem_append, dite_eq_ite]
|
||||
by_cases hi₂ : start + i < w
|
||||
· simp [hi₂, show i < min len w by omega, show i < w - start by omega]
|
||||
· simp [hi₂, ↓reduceIte, show ¬i < w - start by omega,
|
||||
show start + i - w = start - w + (i - (w - start)) by omega]
|
||||
· simp only [hstart, ↓reduceDIte]
|
||||
ext i hi
|
||||
simp [getElem_extractLsb', getLsbD_append,
|
||||
show ¬start + i < w by omega, ↓reduceIte,
|
||||
show start + i - w = start - w + i by omega]
|
||||
|
||||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||||
the bits from `xlo` when `start + len` is within `xlo`.
|
||||
-/
|
||||
theorem extractLsb'_append_eq_of_lt {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
{start len : Nat} (h : start + len < w) :
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo := by
|
||||
simp [extractLsb'_append_eq_ite, h]
|
||||
omega
|
||||
|
||||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||||
the bits from `xhi` when `start` is outside `xlo`.
|
||||
-/
|
||||
theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||||
{start len : Nat} (h : w ≤ start) :
|
||||
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
|
||||
simp [extractLsb'_append_eq_ite, h, show ¬ start < w by omega]
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
|
||||
@@ -2693,6 +3058,9 @@ theorem toInt_neg {x : BitVec w} :
|
||||
rw [← BitVec.zero_sub, toInt_sub]
|
||||
simp [BitVec.toInt_ofNat]
|
||||
|
||||
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
|
||||
eq_of_toInt_eq (by simp [toInt_neg])
|
||||
|
||||
@[simp] theorem toFin_neg (x : BitVec n) :
|
||||
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
|
||||
rfl
|
||||
@@ -2792,6 +3160,14 @@ theorem not_neg (x : BitVec w) : ~~~(-x) = x + -1#w := by
|
||||
show (_ - x.toNat) % _ = _ by rw [Nat.mod_eq_of_lt (by omega)]]
|
||||
omega
|
||||
|
||||
theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
|
||||
|
||||
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
|
||||
apply eq_of_toInt_eq
|
||||
simp [toInt_neg, Int.sub_eq_add_neg]
|
||||
|
||||
/- ### add/sub injectivity -/
|
||||
|
||||
@[simp]
|
||||
@@ -2960,6 +3336,12 @@ protected theorem neg_mul_neg (x y : BitVec w) : -x * -y = x * y := by simp
|
||||
|
||||
protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp
|
||||
|
||||
theorem neg_add_mul_eq_mul_not {x y : BitVec w} :
|
||||
- (x + x * y) = x * ~~~ y := by
|
||||
rw [neg_add, sub_toAdd, ← BitVec.mul_neg, neg_eq_not_add y, mul_add,
|
||||
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc, BitVec.add_right_eq_self,
|
||||
add_neg_eq_sub, BitVec.sub_self]
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :
|
||||
@@ -3064,6 +3446,12 @@ theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w := by
|
||||
exact Eq.symm (BitVec.le_antisymm h this)
|
||||
· simp_all
|
||||
|
||||
@[simp]
|
||||
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w ↔ x ≠ allOnes w := by
|
||||
have := not_congr (@allOnes_le_iff w x)
|
||||
rw [BitVec.not_le] at this
|
||||
exact this
|
||||
|
||||
/-! ### udiv -/
|
||||
|
||||
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
|
||||
@@ -3151,6 +3539,7 @@ then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
|
||||
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
|
||||
(x / y).toInt = x.toNat / y.toNat := by
|
||||
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
|
||||
norm_cast
|
||||
|
||||
/-! ### umod -/
|
||||
|
||||
@@ -3950,7 +4339,6 @@ theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
|
||||
/--
|
||||
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
|
||||
-/
|
||||
@[simp]
|
||||
theorem toInt_intMin {w : Nat} :
|
||||
(intMin w).toInt = -((2 ^ (w - 1) % 2 ^ w) : Nat) := by
|
||||
by_cases h : w = 0
|
||||
@@ -3962,10 +4350,16 @@ theorem toInt_intMin {w : Nat} :
|
||||
rw [Nat.mul_comm]
|
||||
simp [w_pos]
|
||||
|
||||
theorem toInt_intMin_of_pos {v : Nat} (hv : 0 < v) : (intMin v).toInt = -2 ^ (v - 1) := by
|
||||
rw [toInt_intMin, Nat.mod_eq_of_lt]
|
||||
· simp [Int.natCast_pow]
|
||||
· rw [Nat.pow_lt_pow_iff_right (by omega)]
|
||||
omega
|
||||
|
||||
theorem toInt_intMin_le (x : BitVec w) :
|
||||
(intMin w).toInt ≤ x.toInt := by
|
||||
cases w
|
||||
case zero => simp [@of_length_zero x]
|
||||
case zero => simp [toInt_intMin, @of_length_zero x]
|
||||
case succ w =>
|
||||
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod]
|
||||
have : 0 < 2 ^ w := Nat.two_pow_pos w
|
||||
@@ -4109,9 +4503,7 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
|
||||
|
||||
theorem msb_eq_toInt {x : BitVec w}:
|
||||
x.msb = decide (x.toInt < 0) := by
|
||||
by_cases h : x.msb <;>
|
||||
· simp [h, toInt_eq_msb_cond]
|
||||
omega
|
||||
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
|
||||
|
||||
theorem msb_eq_toNat {x : BitVec w}:
|
||||
x.msb = decide (x.toNat ≥ 2 ^ (w - 1)) := by
|
||||
@@ -4346,6 +4738,9 @@ instance instDecidableExistsBitVec :
|
||||
|
||||
set_option linter.missingDocs false
|
||||
|
||||
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
|
||||
abbrev toFin_uShiftRight := @toFin_ushiftRight
|
||||
|
||||
@[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")]
|
||||
abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false
|
||||
|
||||
@@ -4458,7 +4853,7 @@ abbrev signExtend_eq_not_zeroExtend_not_of_msb_false := @signExtend_eq_setWidth
|
||||
abbrev signExtend_eq_not_zeroExtend_not_of_msb_true := @signExtend_eq_not_setWidth_not_of_msb_true
|
||||
|
||||
@[deprecated signExtend_eq_setWidth_of_lt (since := "2024-09-18")]
|
||||
abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_lt
|
||||
abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_le
|
||||
|
||||
@[deprecated truncate_append (since := "2024-09-18")]
|
||||
abbrev truncate_append := @setWidth_append
|
||||
|
||||
@@ -9,7 +9,19 @@ import Init.NotationExtra
|
||||
|
||||
namespace Bool
|
||||
|
||||
/-- Boolean exclusive or -/
|
||||
/--
|
||||
Boolean “exclusive or”. `xor x y` can be written `x ^^ y`.
|
||||
|
||||
`x ^^ y` is `true` when precisely one of `x` or `y` is `true`. Unlike `and` and `or`, it does not
|
||||
have short-circuiting behavior, because one argument's value never determines the final value. Also
|
||||
unlike `and` and `or`, there is no commonly-used corresponding propositional connective.
|
||||
|
||||
Examples:
|
||||
* `false ^^ false = false`
|
||||
* `true ^^ false = true`
|
||||
* `false ^^ true = true`
|
||||
* `true ^^ true = false`
|
||||
-/
|
||||
abbrev xor : Bool → Bool → Bool := bne
|
||||
|
||||
@[inherit_doc] infixl:33 " ^^ " => xor
|
||||
@@ -367,7 +379,9 @@ theorem and_or_inj_left_iff :
|
||||
|
||||
/-! ## toNat -/
|
||||
|
||||
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
def toNat (b : Bool) : Nat := cond b 1 0
|
||||
|
||||
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
|
||||
@@ -388,7 +402,9 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
|
||||
/-! ## toInt -/
|
||||
|
||||
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
def toInt (b : Bool) : Int := cond b 1 0
|
||||
|
||||
@[simp] theorem toInt_false : false.toInt = 0 := rfl
|
||||
@@ -539,8 +555,8 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
|
||||
@[simp] theorem cond_eq_false_distrib : ∀(c t f : Bool),
|
||||
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
|
||||
|
||||
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
|
||||
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
|
||||
protected theorem cond_true {α : Sort u} {a b : α} : cond true a b = a := cond_true a b
|
||||
protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := cond_false a b
|
||||
|
||||
@[simp] theorem cond_true_left : ∀(c f : Bool), cond c true f = ( c || f) := by decide
|
||||
@[simp] theorem cond_false_left : ∀(c f : Bool), cond c false f = (!c && f) := by decide
|
||||
|
||||
@@ -18,10 +18,13 @@ attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
namespace ByteArray
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def mkEmpty (c : @& Nat) : ByteArray :=
|
||||
def emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := #[] }
|
||||
|
||||
def empty : ByteArray := mkEmpty 0
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
@@ -334,6 +337,9 @@ def prevn : Iterator → Nat → Iterator
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
|
||||
@@ -15,7 +15,15 @@ Note that values in `[0xd800, 0xdfff]` are reserved for [UTF-16 surrogate pairs]
|
||||
|
||||
namespace Char
|
||||
|
||||
/--
|
||||
One character is less than another if its code point is strictly less than the other's.
|
||||
-/
|
||||
protected def lt (a b : Char) : Prop := a.val < b.val
|
||||
|
||||
/--
|
||||
One character is less than or equal to another if its code point is less than or equal to the
|
||||
other's.
|
||||
-/
|
||||
protected def le (a b : Char) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : LT Char := ⟨Char.lt⟩
|
||||
@@ -27,7 +35,10 @@ instance (a b : Char) : Decidable (a < b) :=
|
||||
instance (a b : Char) : Decidable (a ≤ b) :=
|
||||
UInt32.decLe _ _
|
||||
|
||||
/-- Determines if the given nat is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).-/
|
||||
/--
|
||||
True for natural numbers that are valid [Unicode scalar
|
||||
values](https://www.unicode.org/glossary/#unicode_scalar_value).
|
||||
-/
|
||||
abbrev isValidCharNat (n : Nat) : Prop :=
|
||||
n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)
|
||||
|
||||
@@ -50,55 +61,93 @@ theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValid
|
||||
theorem isValidChar_zero : isValidChar 0 :=
|
||||
Or.inl (by decide)
|
||||
|
||||
/-- Underlying unicode code point as a `Nat`. -/
|
||||
/--
|
||||
The character's Unicode code point as a `Nat`.
|
||||
-/
|
||||
@[inline] def toNat (c : Char) : Nat :=
|
||||
c.val.toNat
|
||||
|
||||
/-- Convert a character into a `UInt8`, by truncating (reducing modulo 256) if necessary. -/
|
||||
/--
|
||||
Converts a character into a `UInt8` that contains its code point.
|
||||
|
||||
If the code point is larger than 255, it is truncated (reduced modulo 256).
|
||||
-/
|
||||
@[inline] def toUInt8 (c : Char) : UInt8 :=
|
||||
c.val.toUInt8
|
||||
|
||||
/-- The numbers from 0 to 256 are all valid UTF-8 characters, so we can embed one in the other. -/
|
||||
/--
|
||||
Converts an 8-bit unsigned integer into a character.
|
||||
|
||||
The integer's value is interpreted as a Unicode code point.
|
||||
-/
|
||||
def ofUInt8 (n : UInt8) : Char := ⟨n.toUInt32, .inl (Nat.lt_trans n.toBitVec.isLt (by decide))⟩
|
||||
|
||||
instance : Inhabited Char where
|
||||
default := 'A'
|
||||
|
||||
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
|
||||
/--
|
||||
Returns `true` if the character is a space `(' ', U+0020)`, a tab `('\t', U+0009)`, a carriage
|
||||
return `('\r', U+000D)`, or a newline `('\n', U+000A)`.
|
||||
-/
|
||||
@[inline] def isWhitespace (c : Char) : Bool :=
|
||||
c = ' ' || c = '\t' || c = '\r' || c = '\n'
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
|
||||
/--
|
||||
Returns `true` if the character is a uppercase ASCII letter.
|
||||
|
||||
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||
-/
|
||||
@[inline] def isUpper (c : Char) : Bool :=
|
||||
c.val ≥ 65 && c.val ≤ 90
|
||||
|
||||
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
|
||||
/--
|
||||
Returns `true` if the character is a lowercase ASCII letter.
|
||||
|
||||
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
@[inline] def isLower (c : Char) : Bool :=
|
||||
c.val ≥ 97 && c.val ≤ 122
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII letter.
|
||||
|
||||
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
@[inline] def isAlpha (c : Char) : Bool :=
|
||||
c.isUpper || c.isLower
|
||||
|
||||
/-- Is the character in `0123456789`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII digit.
|
||||
|
||||
The ASCII digits are the following: `0123456789`.
|
||||
-/
|
||||
@[inline] def isDigit (c : Char) : Bool :=
|
||||
c.val ≥ 48 && c.val ≤ 57
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII letter or digit.
|
||||
|
||||
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
|
||||
The ASCII digits are the following: `0123456789`.
|
||||
-/
|
||||
@[inline] def isAlphanum (c : Char) : Bool :=
|
||||
c.isAlpha || c.isDigit
|
||||
|
||||
/-- Convert an upper case character to its lower case character.
|
||||
/--
|
||||
Converts an uppercase ASCII letter to the corresponding lowercase letter. Letters outside the ASCII
|
||||
alphabet are returned unchanged.
|
||||
|
||||
Only works on basic latin letters.
|
||||
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||
-/
|
||||
def toLower (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
if n >= 65 ∧ n <= 90 then ofNat (n + 32) else c
|
||||
|
||||
/-- Convert a lower case character to its upper case character.
|
||||
/--
|
||||
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
|
||||
alphabet are returned unchanged.
|
||||
|
||||
Only works on basic latin letters.
|
||||
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
def toUpper (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
|
||||
@@ -14,22 +14,23 @@ instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
|
||||
The type `Fin 0` is uninhabited, so it can be used to derive any result whatsoever.
|
||||
|
||||
This is similar to `Empty.elim`. It can be thought of as a compiler-checked assertion that a code
|
||||
path is unreachable, or a logical contradiction from which `False` and thus anything else could be
|
||||
derived.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
/--
|
||||
Returns the successor of the argument.
|
||||
The successor, with an increased bound.
|
||||
|
||||
The bound in the result type is increased:
|
||||
```
|
||||
(2 : Fin 3).succ = (3 : Fin 4)
|
||||
```
|
||||
This differs from addition, which wraps around:
|
||||
```
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
This differs from adding `1`, which instead wraps around.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 3).succ = (3 : Fin 4)`
|
||||
* `(2 : Fin 3) + 1 = (0 : Fin 3)`
|
||||
-/
|
||||
def succ : Fin n → Fin (n + 1)
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
@@ -53,7 +54,13 @@ protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
|
||||
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
|
||||
-- `i.toNat` to `i.val`.
|
||||
@[inline, inherit_doc val]
|
||||
/--
|
||||
Extracts the underlying `Nat` value.
|
||||
|
||||
This function is a synonym for `Fin.val`, which is the simp normal form. `Fin.val` is also a
|
||||
coercion, so values of type `Fin n` are automatically converted to `Nat`s as needed.
|
||||
-/
|
||||
@[inline]
|
||||
protected def toNat (i : Fin n) : Nat :=
|
||||
i.val
|
||||
|
||||
@@ -65,15 +72,34 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
|
||||
Nat.mod_lt _ this
|
||||
|
||||
/-- Addition modulo `n` -/
|
||||
/--
|
||||
Addition modulo `n`, usually invoked via the `+` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 8) + (2 : Fin 8) = (4 : Fin 8)`
|
||||
* `(2 : Fin 3) + (2 : Fin 3) = (1 : Fin 3)`
|
||||
-/
|
||||
protected def add : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + b) % n, mlt h⟩
|
||||
|
||||
/-- Multiplication modulo `n` -/
|
||||
/--
|
||||
Multiplication modulo `n`, usually invoked via the `*` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 10) * (2 : Fin 10) = (4 : Fin 10)`
|
||||
* `(2 : Fin 10) * (7 : Fin 10) = (4 : Fin 10)`
|
||||
* `(3 : Fin 10) * (7 : Fin 10) = (1 : Fin 10)`
|
||||
-/
|
||||
protected def mul : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a * b) % n, mlt h⟩
|
||||
|
||||
/-- Subtraction modulo `n` -/
|
||||
/--
|
||||
Subtraction modulo `n`, usually invoked via the `-` operator.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 11) - (3 : Fin 11) = (2 : Fin 11)`
|
||||
* `(3 : Fin 11) - (5 : Fin 11) = (9 : Fin 11)`
|
||||
-/
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
/-
|
||||
The definition of `Fin.sub` has been updated to improve performance.
|
||||
@@ -100,27 +126,76 @@ we are trying to minimize the number of Nat theorems
|
||||
needed to bootstrap Lean.
|
||||
-/
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers, usually invoked via the `%` operator.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
protected def mod : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Division of bounded numbers, usually invoked via the `/` operator.
|
||||
|
||||
The resulting value is that computed by the `/` operator on `Nat`. In particular, the result of
|
||||
division by `0` is `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 10) / (2 : Fin 10) = (2 : Fin 10)`
|
||||
* `(5 : Fin 10) / (0 : Fin 10) = (0 : Fin 10)`
|
||||
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
|
||||
-/
|
||||
protected def div : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h⟩
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers with respect to a `Nat`.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
def modn : Fin n → Nat → Fin n
|
||||
| ⟨a, h⟩, m => ⟨a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Bitwise and.
|
||||
-/
|
||||
def land : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.land a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise or.
|
||||
-/
|
||||
def lor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.lor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise xor (“exclusive or”).
|
||||
-/
|
||||
def xor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.xor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise left shift of bounded numbers, with wraparound on overflow.
|
||||
|
||||
Examples:
|
||||
* `(1 : Fin 10) <<< (1 : Fin 10) = (2 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (3 : Fin 10) = (8 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (4 : Fin 10) = (6 : Fin 10)`
|
||||
-/
|
||||
def shiftLeft : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a <<< b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise right shift of bounded numbers.
|
||||
|
||||
This operator corresponds to logical rather than arithmetic bit shifting. The new bits are always
|
||||
`0`.
|
||||
|
||||
Examples:
|
||||
* `(15 : Fin 16) >>> (1 : Fin 16) = (7 : Fin 16)`
|
||||
* `(15 : Fin 16) >>> (2 : Fin 16) = (3 : Fin 16)`
|
||||
* `(15 : Fin 17) >>> (2 : Fin 17) = (3 : Fin 17)`
|
||||
-/
|
||||
def shiftRight : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a >>> b) % n, mlt h⟩
|
||||
|
||||
@@ -174,39 +249,125 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
|
||||
protected theorem pos (i : Fin n) : 0 < n :=
|
||||
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
|
||||
|
||||
/-- The greatest value of `Fin (n+1)`. -/
|
||||
/--
|
||||
The greatest value of `Fin (n+1)`, namely `n`.
|
||||
|
||||
Examples:
|
||||
* `Fin.last 4 = (4 : Fin 5)`
|
||||
* `(Fin.last 0).val = (0 : Nat)`
|
||||
-/
|
||||
@[inline] def last (n : Nat) : Fin (n + 1) := ⟨n, n.lt_succ_self⟩
|
||||
|
||||
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
|
||||
/--
|
||||
Replaces the bound with another that is suitable for the value.
|
||||
|
||||
The proof embedded in `i` can be used to cast to a larger bound even if the concrete value is not
|
||||
known.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
example : Fin 12 := (7 : Fin 10).castLT (by decide : 7 < 12)
|
||||
```
|
||||
```lean example
|
||||
example (i : Fin 10) : Fin 12 :=
|
||||
i.castLT <| by
|
||||
cases i; simp; omega
|
||||
```
|
||||
-/
|
||||
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := ⟨i.1, h⟩
|
||||
|
||||
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.castAdd` for a version that represents the larger bound with addition rather than an
|
||||
explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castLE (h : n ≤ m) (i : Fin n) : Fin m := ⟨i, Nat.lt_of_lt_of_le i.2 h⟩
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
|
||||
/--
|
||||
Uses a proof that two bounds are equal to allow a value bounded by one to be used with the other.
|
||||
|
||||
In other words, when `eq : n = m`, `Fin.cast eq i` converts `i : Fin n` into a `Fin m`.
|
||||
-/
|
||||
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
|
||||
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.natAdd` and `Fin.addNat` for addition functions that increase the bound, and
|
||||
`Fin.castLE` for a version that uses an explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castAdd (m) : Fin n → Fin (n + m) :=
|
||||
castLE <| Nat.le_add_right n m
|
||||
|
||||
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
|
||||
/--
|
||||
Coarsens a bound by one.
|
||||
-/
|
||||
@[inline] def castSucc : Fin n → Fin (n + 1) := castAdd 1
|
||||
|
||||
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.natAdd` is a version of this function that takes its `Nat` parameter first.
|
||||
|
||||
Examples:
|
||||
* `Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)`
|
||||
* `Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)`
|
||||
* `Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)`
|
||||
|
||||
-/
|
||||
def addNat (i : Fin n) (m) : Fin (n + m) := ⟨i + m, Nat.add_lt_add_right i.2 _⟩
|
||||
|
||||
/-- `natAdd n i` adds `n` to `i` "on the left". -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.addNat` is a version of this function that takes its `Nat` parameter second.
|
||||
|
||||
Examples:
|
||||
* `Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)`
|
||||
* `Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)`
|
||||
* `Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)`
|
||||
-/
|
||||
def natAdd (n) (i : Fin m) : Fin (n + m) := ⟨n + i, Nat.add_lt_add_left i.2 _⟩
|
||||
|
||||
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
|
||||
/--
|
||||
Replaces a value with its difference from the largest value in the type.
|
||||
|
||||
Considering the values of `Fin n` as a sequence `0`, `1`, …, `n-2`, `n-1`, `Fin.rev` finds the
|
||||
corresponding element of the reversed sequence. In other words, it maps `0` to `n-1`, `1` to `n-2`,
|
||||
..., and `n-1` to `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 6).rev = (0 : Fin 6)`
|
||||
* `(0 : Fin 6).rev = (5 : Fin 6)`
|
||||
* `(2 : Fin 5).rev = (2 : Fin 5)`
|
||||
-/
|
||||
@[inline] def rev (i : Fin n) : Fin n := ⟨n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)⟩
|
||||
|
||||
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
|
||||
/--
|
||||
Subtraction of a natural number from a `Fin`, with the bound narrowed.
|
||||
|
||||
This is a generalization of `Fin.pred`. It is guaranteed to not underflow or wrap around.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 9).subNat 2 (by decide) = (3 : Fin 7)`
|
||||
* `(5 : Fin 9).subNat 0 (by decide) = (5 : Fin 9)`
|
||||
* `(3 : Fin 9).subNat 3 (by decide) = (0 : Fin 6)`
|
||||
-/
|
||||
@[inline] def subNat (m) (i : Fin (n + m)) (h : m ≤ i) : Fin n :=
|
||||
⟨i - m, Nat.sub_lt_right_of_lt_add h i.2⟩
|
||||
|
||||
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
|
||||
/--
|
||||
The predecessor of a non-zero element of `Fin (n+1)`, with the bound decreased.
|
||||
|
||||
Examples:
|
||||
* `(4 : Fin 8).pred (by decide) = (3 : Fin 7)`
|
||||
* `(1 : Fin 2).pred (by decide) = (0 : Fin 1)`
|
||||
-/
|
||||
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i ≠ 0) : Fin n :=
|
||||
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h
|
||||
|
||||
|
||||
@@ -10,14 +10,26 @@ import Init.Data.Fin.Lemmas
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `0` and
|
||||
nesting to the left.
|
||||
|
||||
Example:
|
||||
* `Fin.foldl 3 (· + ·.val) (0 : Nat) = ((0 + (0 : Fin 3).val) + (1 : Fin 3).val) + (2 : Fin 3).val`
|
||||
-/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `n - 1`
|
||||
and nesting to the right.
|
||||
|
||||
Example:
|
||||
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
|
||||
-/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
@[specialize] loop : (i : _) → i ≤ n → α → α
|
||||
@@ -26,7 +38,9 @@ namespace Fin
|
||||
termination_by structural i => i
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from left to right:
|
||||
Folds a monadic function over all the values in `Fin n` from left to right, starting with `0`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldlM n f x₀ = do
|
||||
let x₁ ← f x₀ 0
|
||||
@@ -53,7 +67,9 @@ Fin.foldlM n f x₀ = do
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from right to left:
|
||||
Folds a monadic function over `Fin n` from right to left, starting with `n-1`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldrM n f xₙ = do
|
||||
let xₙ₋₁ ← f (n-1) xₙ
|
||||
|
||||
@@ -10,10 +10,18 @@ import Init.Data.Fin.Basic
|
||||
namespace Fin
|
||||
|
||||
/--
|
||||
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
|
||||
from `P i`.
|
||||
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an
|
||||
initial accumulator `a`.
|
||||
|
||||
See `hIterate` below for more details.
|
||||
Concretely, `Fin.hIterateFrom P f i a` is equal to
|
||||
```lean
|
||||
a |> f i |> f (i + 1) |> ... |> f (n - 1)
|
||||
```
|
||||
|
||||
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
|
||||
other more specialized theorems.
|
||||
|
||||
`Fin.hIterate` is a variant that always starts at `0`.
|
||||
-/
|
||||
def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.val+1))
|
||||
(i : Nat) (ubnd : i ≤ n) (a : P i) : P n :=
|
||||
@@ -26,20 +34,18 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogeneous iterative operation that applies a
|
||||
index-dependent function `f` to a value `init : P start` a total of
|
||||
`stop - start` times to produce a value of type `P stop`.
|
||||
Applies an index-dependent function to all the values less than the given bound `n`, starting at
|
||||
`0` with an accumulator.
|
||||
|
||||
Concretely, `hIterate start stop f init` is equal to
|
||||
Concretely, `Fin.hIterate P init f` is equal to
|
||||
```lean
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
init |> f 0 |> f 1 |> ... |> f (n-1)
|
||||
```
|
||||
|
||||
Because it is heterogeneous and must return a value of type `P stop`,
|
||||
`hIterate` requires proof that `start ≤ stop`.
|
||||
Theorems about `Fin.hIterate` can be proven using the general theorem `Fin.hIterate_elim` or other more
|
||||
specialized theorems.
|
||||
|
||||
One can prove properties of `hIterate` using the general theorem
|
||||
`hIterate_elim` or other more specialized theorems.
|
||||
`Fin.hIterateFrom` is a variant that takes a custom starting value instead of `0`.
|
||||
-/
|
||||
def hIterate (P : Nat → Sort _) {n : Nat} (init : P 0) (f : ∀(i : Fin n), P i.val → P (i.val+1)) :
|
||||
P n :=
|
||||
|
||||
@@ -45,6 +45,7 @@ theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
|
||||
theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩ :=
|
||||
⟨fun h i hi => h ⟨i, hi⟩, fun h ⟨i, hi⟩ => h i hi⟩
|
||||
|
||||
/-- Restatement of `Fin.mk.injEq` as an `iff`. -/
|
||||
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := Fin.ext_iff
|
||||
|
||||
@@ -55,6 +56,14 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@[simp] theorem mk_eq_zero {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
(⟨a, ha⟩ : Fin n) = 0 ↔ a = 0 :=
|
||||
mk.inj_iff
|
||||
|
||||
@[simp] theorem zero_eq_mk {n a : Nat} {ha : a < n} [NeZero n] :
|
||||
0 = (⟨a, ha⟩ : Fin n) ↔ a = 0 := by
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
|
||||
(Fin.ofNat' n a).val = a % n := rfl
|
||||
|
||||
@@ -661,12 +670,20 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [← cast_addNat]
|
||||
|
||||
/-! ### recursion and induction principles -/
|
||||
/-! ### Recursion and induction principles -/
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
|
||||
`(n+1)`-tuple, and `succ n i` defines `(i+1)`-st element of `(n+1)`-tuple based on `n`, `i`, and
|
||||
`i`-th element of `n`-tuple. -/
|
||||
/--
|
||||
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
|
||||
applications of `Fin.succ`.
|
||||
|
||||
The cases in the induction are:
|
||||
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
|
||||
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
|
||||
bound `n`
|
||||
|
||||
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
|
||||
step. `Fin.succRecOn` is a version of this induction principle that takes the `Fin` argument first.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRec {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive n.succ (0 : Fin (n + 1)))
|
||||
@@ -675,13 +692,18 @@ This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0
|
||||
| Nat.succ n, ⟨0, _⟩ => by rw [mk_zero]; exact zero n
|
||||
| Nat.succ _, ⟨Nat.succ i, h⟩ => succ _ _ (succRec zero succ ⟨i, Nat.lt_of_succ_lt_succ h⟩)
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments:
|
||||
`zero n` defines the `0`-th element `motive (n+1) 0` of an `(n+1)`-tuple, and
|
||||
`succ n i` defines the `(i+1)`-st element of an `(n+1)`-tuple based on `n`, `i`,
|
||||
and the `i`-th element of an `n`-tuple.
|
||||
/--
|
||||
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
|
||||
applications of `Fin.succ`.
|
||||
|
||||
A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
The cases in the induction are:
|
||||
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
|
||||
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
|
||||
bound `n`
|
||||
|
||||
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
|
||||
step. `Fin.succRec` is a version of this induction principle that takes the `Fin` argument last.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive (n + 1) 0) (succ : ∀ n i, motive n i → motive (Nat.succ n) i.succ) :
|
||||
@@ -696,9 +718,17 @@ A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
cases i; rfl
|
||||
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
/--
|
||||
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
For the induction:
|
||||
* `zero` is the base case, demonstrating `motive 0`.
|
||||
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
|
||||
`Fin.castSucc`) and demonstrating it for `i.succ`.
|
||||
|
||||
`Fin.inductionOn` is a version of this induction principle that takes the `Fin` as its first
|
||||
parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.reverseInduction` is a
|
||||
version that starts at the greatest value instead of `0`.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def induction {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
@@ -719,18 +749,30 @@ where
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) (i : Fin n) :
|
||||
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
/--
|
||||
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
|
||||
For the induction:
|
||||
* `zero` is the base case, demonstrating `motive 0`.
|
||||
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
|
||||
`Fin.castSucc`) and demonstrating it for `i.succ`.
|
||||
|
||||
`Fin.induction` is a version of this induction principle that takes the `Fin` as its last
|
||||
parameter.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) : motive i := induction zero succ i
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
|
||||
`i = j.succ`, `j : Fin n`. -/
|
||||
/--
|
||||
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
The two cases are:
|
||||
* `zero`, used when the value is of the form `(0 : Fin (n + 1))`
|
||||
* `succ`, used when the value is of the form `(j : Fin n).succ`
|
||||
|
||||
The corresponding induction principle is `Fin.induction`.
|
||||
-/
|
||||
@[elab_as_elim] def cases {motive : Fin (n + 1) → Sort _}
|
||||
(zero : motive 0) (succ : ∀ i : Fin n, motive i.succ) :
|
||||
∀ i : Fin (n + 1), motive i := induction zero fun i _ => succ i
|
||||
@@ -768,9 +810,14 @@ theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a =
|
||||
simp only [forall_fin_two]; decide
|
||||
|
||||
/--
|
||||
Define `motive i` by reverse induction on `i : Fin (n + 1)` via induction on the underlying `Nat`
|
||||
value. This function has two arguments: `last` handles the base case on `motive (Fin.last n)`,
|
||||
and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
|
||||
Proves a statement by reverse induction on the underlying `Nat` value in a `Fin (n + 1)`.
|
||||
|
||||
For the induction:
|
||||
* `last` is the base case, demonstrating `motive (Fin.last n)`.
|
||||
* `cast` is the inductive step, assuming the motive for `(j : Fin n).succ` and demonstrating it for
|
||||
the predecessor `j.castSucc`.
|
||||
|
||||
`Fin.induction` is the non-reverse induction principle.
|
||||
-/
|
||||
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive i.succ → motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
@@ -793,8 +840,16 @@ decreasing_by decreasing_with
|
||||
succ i (reverseInduction zero succ i.succ) := by
|
||||
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
|
||||
`i = j.castSucc`, `j : Fin n`. -/
|
||||
/--
|
||||
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`, checking whether the
|
||||
value is the greatest representable or a predecessor of some other.
|
||||
|
||||
The two cases are:
|
||||
* `last`, used when the value is `Fin.last n`
|
||||
* `cast`, used when the value is of the form `(j : Fin n).succ`
|
||||
|
||||
The corresponding induction principle is `Fin.reverseInduction`.
|
||||
-/
|
||||
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
reverseInduction last (fun i _ => cast i) i
|
||||
@@ -807,8 +862,16 @@ decreasing_by decreasing_with
|
||||
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
|
||||
reverseInduction_castSucc ..
|
||||
|
||||
/-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
|
||||
`j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
|
||||
/--
|
||||
A case analysis operator for `i : Fin (m + n)` that separately handles the cases where `i < m` and
|
||||
where `m ≤ i < m + n`.
|
||||
|
||||
The first case, where `i < m`, is handled by `left`. In this case, `i` can be represented as
|
||||
`Fin.castAdd n (j : Fin m)`.
|
||||
|
||||
The second case, where `m ≤ i < m + n`, is handled by `right`. In this case, `i` can be represented
|
||||
as `Fin.natAdd m (j : Fin n)`.
|
||||
-/
|
||||
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) → Sort u}
|
||||
(left : ∀ i, motive (castAdd n i)) (right : ∀ i, motive (natAdd m i))
|
||||
(i : Fin (m + n)) : motive i :=
|
||||
|
||||
@@ -6,4 +6,20 @@ Authors: Henrik Böving
|
||||
prelude
|
||||
import Init.Data.Nat.Log2
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
Logarithm base 2 for bounded numbers.
|
||||
|
||||
The resulting value is the same as that computed by `Nat.log2`. In particular, the result for `0` is
|
||||
`0`.
|
||||
|
||||
Examples:
|
||||
* `(8 : Fin 10).log2 = (3 : Fin 10)`
|
||||
* `(7 : Fin 10).log2 = (2 : Fin 10)`
|
||||
* `(4 : Fin 10).log2 = (2 : Fin 10)`
|
||||
* `(3 : Fin 10).log2 = (1 : Fin 10)`
|
||||
* `(1 : Fin 10).log2 = (0 : Fin 10)`
|
||||
* `(0 : Fin 10).log2 = (0 : Fin 10)`
|
||||
-/
|
||||
def Fin.log2 (n : Fin m) : Fin m := ⟨Nat.log2 n.val, Nat.lt_of_le_of_lt (Nat.log2_le_self n.val) n.isLt⟩
|
||||
|
||||
@@ -17,11 +17,14 @@ attribute [extern "lean_float_array_data"] FloatArray.data
|
||||
|
||||
namespace FloatArray
|
||||
@[extern "lean_mk_empty_float_array"]
|
||||
def mkEmpty (c : @& Nat) : FloatArray :=
|
||||
def emptyWithCapacity (c : @& Nat) : FloatArray :=
|
||||
{ data := #[] }
|
||||
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : FloatArray :=
|
||||
mkEmpty 0
|
||||
emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited FloatArray where
|
||||
default := empty
|
||||
@@ -164,6 +167,9 @@ def foldl {β : Type v} (f : β → Float → β) (init : β) (as : FloatArray)
|
||||
|
||||
end FloatArray
|
||||
|
||||
/--
|
||||
Converts a list of floats into a `FloatArray`.
|
||||
-/
|
||||
def List.toFloatArray (ds : List Float) : FloatArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
|
||||
@@ -23,6 +23,12 @@ instance [ToFormat α] : ToFormat (List α) where
|
||||
instance [ToFormat α] : ToFormat (Array α) where
|
||||
format a := "#" ++ format a.toList
|
||||
|
||||
/--
|
||||
Formats an optional value, with no expectation that the Lean parser should be able to parse the
|
||||
result.
|
||||
|
||||
This function is usually accessed through the `ToFormat (Option α)` instance.
|
||||
-/
|
||||
def Option.format {α : Type u} [ToFormat α] : Option α → Format
|
||||
| none => "none"
|
||||
| some a => "some " ++ Std.format a
|
||||
|
||||
@@ -9,10 +9,23 @@ import Init.Core
|
||||
|
||||
namespace Function
|
||||
|
||||
/--
|
||||
Transforms a function from pairs into an equivalent two-parameter function.
|
||||
|
||||
Examples:
|
||||
* `Function.curry (fun (x, y) => x + y) 3 5 = 8`
|
||||
* `Function.curry Prod.swap 3 "five" = ("five", 3)`
|
||||
-/
|
||||
@[inline]
|
||||
def curry : (α × β → φ) → α → β → φ := fun f a b => f (a, b)
|
||||
|
||||
/-- Interpret a function with two arguments as a function on `α × β` -/
|
||||
/--
|
||||
Transforms a two-parameter function into an equivalent function from pairs.
|
||||
|
||||
Examples:
|
||||
* `Function.uncurry List.drop (1, ["a", "b", "c"]) = ["b", "c"]`
|
||||
* `[("orange", 2), ("android", 3) ].map (Function.uncurry String.take) = ["or", "and"]`
|
||||
-/
|
||||
@[inline]
|
||||
def uncurry : (α → β → φ) → α × β → φ := fun f a => f a.1 a.2
|
||||
|
||||
|
||||
@@ -75,13 +75,19 @@ instance (P : Prop) : Hashable P where
|
||||
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
|
||||
mixHash u 11
|
||||
|
||||
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
|
||||
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
|
||||
/--
|
||||
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
|
||||
`hash a = hash b`.
|
||||
|
||||
This is automatic if the `BEq` instance is lawful.
|
||||
-/
|
||||
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
|
||||
/-- If `a == b`, then `hash a = hash b`. -/
|
||||
hash_eq (a b : α) : a == b → hash a = hash b
|
||||
|
||||
/--
|
||||
A lawful hash function respects its Boolean equality test.
|
||||
-/
|
||||
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b → hash a = hash b :=
|
||||
LawfulHashable.hash_eq a b
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@ import Init.Data.Int.Order
|
||||
import Init.Data.Int.Pow
|
||||
import Init.Data.Int.Cooper
|
||||
import Init.Data.Int.Linear
|
||||
import Init.Data.Int.OfNat
|
||||
|
||||
@@ -17,10 +17,12 @@ open Nat
|
||||
This file defines the `Int` type as well as
|
||||
|
||||
* coercions, conversions, and compatibility with numeric literals,
|
||||
* basic arithmetic operations add/sub/mul/div/mod/pow,
|
||||
* basic arithmetic operations add/sub/mul/pow,
|
||||
* a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
|
||||
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
|
||||
* decidability of equality, relations and `NonNeg`.
|
||||
|
||||
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
|
||||
-/
|
||||
|
||||
/--
|
||||
@@ -291,13 +293,16 @@ def toNat : Int → Nat
|
||||
| negSucc _ => 0
|
||||
|
||||
/--
|
||||
* If `n : Nat`, then `int.toNat' n = some n`
|
||||
* If `n : Int` is negative, then `int.toNat' n = none`.
|
||||
* If `n : Nat`, then `Int.toNat? n = some n`
|
||||
* If `n : Int` is negative, then `Int.toNat? n = none`.
|
||||
-/
|
||||
def toNat' : Int → Option Nat
|
||||
def toNat? : Int → Option Nat
|
||||
| (n : Nat) => some n
|
||||
| -[_+1] => none
|
||||
|
||||
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
|
||||
abbrev toNat' := toNat?
|
||||
|
||||
/-! ## divisibility -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -1,50 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement
|
||||
and complements each bit.
|
||||
```
|
||||
~~~(0:Int) = -1
|
||||
~~~(1:Int) = -2
|
||||
~~~(-1:Int) = 0
|
||||
```
|
||||
-/
|
||||
protected def not : Int -> Int
|
||||
| Int.ofNat n => Int.negSucc n
|
||||
| Int.negSucc n => Int.ofNat n
|
||||
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise shift right.
|
||||
|
||||
Conceptually, this treats the integer as an infinite sequence of bits in two's
|
||||
complement and shifts the value to the right.
|
||||
|
||||
```lean
|
||||
( 0b0111:Int) >>> 1 = 0b0011
|
||||
( 0b1000:Int) >>> 1 = 0b0100
|
||||
(-0b1000:Int) >>> 1 = -0b0100
|
||||
(-0b0111:Int) >>> 1 = -0b0100
|
||||
```
|
||||
-/
|
||||
protected def shiftRight : Int → Nat → Int
|
||||
| Int.ofNat n, s => Int.ofNat (n >>> s)
|
||||
| Int.negSucc n, s => Int.negSucc (n >>> s)
|
||||
|
||||
instance : HShiftRight Int Nat Int := ⟨.shiftRight⟩
|
||||
|
||||
end Int
|
||||
import Init.Data.Int.Bitwise.Basic
|
||||
import Init.Data.Int.Bitwise.Lemmas
|
||||
|
||||
50
src/Init/Data/Int/Bitwise/Basic.lean
Normal file
50
src/Init/Data/Int/Bitwise/Basic.lean
Normal file
@@ -0,0 +1,50 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement
|
||||
and complements each bit.
|
||||
```
|
||||
~~~(0:Int) = -1
|
||||
~~~(1:Int) = -2
|
||||
~~~(-1:Int) = 0
|
||||
```
|
||||
-/
|
||||
protected def not : Int → Int
|
||||
| Int.ofNat n => Int.negSucc n
|
||||
| Int.negSucc n => Int.ofNat n
|
||||
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise shift right.
|
||||
|
||||
Conceptually, this treats the integer as an infinite sequence of bits in two's
|
||||
complement and shifts the value to the right.
|
||||
|
||||
```lean
|
||||
( 0b0111:Int) >>> 1 = 0b0011
|
||||
( 0b1000:Int) >>> 1 = 0b0100
|
||||
(-0b1000:Int) >>> 1 = -0b0100
|
||||
(-0b0111:Int) >>> 1 = -0b0100
|
||||
```
|
||||
-/
|
||||
protected def shiftRight : Int → Nat → Int
|
||||
| Int.ofNat n, s => Int.ofNat (n >>> s)
|
||||
| Int.negSucc n, s => Int.negSucc (n >>> s)
|
||||
|
||||
instance : HShiftRight Int Nat Int := ⟨.shiftRight⟩
|
||||
|
||||
end Int
|
||||
@@ -5,12 +5,13 @@ Authors: Siddharth Bhat, Jeremy Avigad
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Bitwise.Lemmas
|
||||
import Init.Data.Int.Bitwise
|
||||
import Init.Data.Int.Bitwise.Basic
|
||||
import Init.Data.Int.DivMod.Lemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
|
||||
|
||||
@[simp]
|
||||
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
|
||||
|
||||
@@ -27,7 +28,7 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
|
||||
m >>> n = m / ((2 ^ n) : Nat) := by
|
||||
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
|
||||
split
|
||||
· simp
|
||||
· simp; norm_cast
|
||||
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
|
||||
rfl
|
||||
|
||||
@@ -39,4 +40,47 @@ theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
|
||||
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
|
||||
simp [Int.shiftRight_eq_div_pow]
|
||||
|
||||
theorem le_shiftRight_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : n ≤ n >>> s := by
|
||||
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
|
||||
split
|
||||
case _ _ _ m =>
|
||||
simp only [ofNat_eq_coe] at h
|
||||
by_cases hm : m = 0
|
||||
· simp [hm]
|
||||
· omega
|
||||
case _ _ _ m =>
|
||||
by_cases hm : m = 0
|
||||
· simp [hm]
|
||||
· have := Nat.shiftRight_le m s
|
||||
omega
|
||||
|
||||
theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 ≤ n) : n >>> s ≤ n := by
|
||||
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
|
||||
split
|
||||
case _ _ _ m =>
|
||||
simp only [Int.ofNat_eq_coe] at h
|
||||
by_cases hm : m = 0
|
||||
· simp [hm]
|
||||
· have := Nat.shiftRight_le m s
|
||||
simp
|
||||
omega
|
||||
case _ _ _ m =>
|
||||
omega
|
||||
|
||||
theorem le_shiftRight_of_nonneg {n : Int} {s : Nat} (h : 0 ≤ n) : 0 ≤ (n >>> s) := by
|
||||
rw [Int.shiftRight_eq_div_pow]
|
||||
by_cases h' : s = 0
|
||||
· simp [h', h]
|
||||
· have := @Nat.pow_pos 2 s (by omega)
|
||||
have := @Int.ediv_nonneg n (2^s) h (by norm_cast at *; omega)
|
||||
norm_cast at *
|
||||
|
||||
theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : (n >>> s) ≤ 0 := by
|
||||
rw [Int.shiftRight_eq_div_pow]
|
||||
by_cases h' : s = 0
|
||||
· simp [h', h]
|
||||
· have : 1 < 2 ^ s := Nat.one_lt_two_pow (by omega)
|
||||
have rl : n / 2 ^ s ≤ 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
|
||||
norm_cast at *
|
||||
|
||||
end Int
|
||||
|
||||
@@ -227,33 +227,4 @@ theorem cooper_resolution_dvd_right
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_le_of_neg_le lower
|
||||
· exact Int.mul_neg _ _ ▸ Int.neg_mul _ _ ▸ dvd
|
||||
|
||||
/--
|
||||
Left Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_left
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < a ∧ b * k + b * p ≤ a * q ∧ a ∣ k + p) := by
|
||||
have h := cooper_resolution_dvd_left
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt a_pos), Int.one_dvd, and_true,
|
||||
and_self] at h
|
||||
exact h
|
||||
|
||||
/--
|
||||
Right Cooper resolution of an upper and lower bound.
|
||||
-/
|
||||
theorem cooper_resolution_right
|
||||
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
|
||||
(∃ x, p ≤ a * x ∧ b * x ≤ q) ↔
|
||||
(∃ k : Int, 0 ≤ k ∧ k < b ∧ a * k + b * p ≤ a * q ∧ b ∣ k - q) := by
|
||||
have h := cooper_resolution_dvd_right
|
||||
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
|
||||
have : ∀ k : Int, (b ∣ -k + q) ↔ (b ∣ k - q) := by
|
||||
intro k
|
||||
rw [← Int.dvd_neg, Int.neg_add, Int.neg_neg, Int.sub_eq_add_neg]
|
||||
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
|
||||
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.one_dvd, and_true,
|
||||
and_self, ← Int.neg_eq_neg_one_mul, this] at h
|
||||
exact h
|
||||
end Int
|
||||
|
||||
@@ -21,26 +21,28 @@ and satisfy `x / 0 = 0` and `x % 0 = x`.
|
||||
In early versions of Lean, the typeclasses provided by `/` and `%`
|
||||
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
|
||||
|
||||
However we decided it was better to use `ediv` and `emod`,
|
||||
However we decided it was better to use `ediv` and `emod` for the default typeclass instances,
|
||||
as they are consistent with the conventions used in SMTLib, and Mathlib,
|
||||
and often mathematical reasoning is easier with these conventions.
|
||||
|
||||
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
|
||||
|
||||
In September 2024, we decided to do this rename (with deprecations in place),
|
||||
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
|
||||
ever need to use these functions and their associated lemmas.
|
||||
|
||||
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
|
||||
In December 2024, we removed `div` and `mod`, but have not yet renamed `ediv` and `emod`.
|
||||
-/
|
||||
|
||||
/-! ### E-rounding division
|
||||
This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
|
||||
This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of `Int.div` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
Integer division. This version of integer division uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x` for `y ≠ 0`.
|
||||
|
||||
This means that `Int.ediv x y = floor (x / y)` when `y > 0` and `Int.ediv x y = ceil (x / y)` when `y < 0`.
|
||||
|
||||
This is the function powering the `/` notation on integers.
|
||||
|
||||
@@ -71,7 +73,7 @@ def ediv : (@& Int) → (@& Int) → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the E-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
|
||||
@@ -109,7 +111,7 @@ instance : Div Int where
|
||||
instance : Mod Int where
|
||||
mod := Int.emod
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : (↑(m / n) : Int) = ↑m / ↑n := rfl
|
||||
@[norm_cast] theorem ofNat_ediv (m n : Nat) : (↑(m / n) : Int) = ↑m / ↑n := rfl
|
||||
|
||||
theorem ofNat_ediv_ofNat {a b : Nat} : (↑a / ↑b : Int) = (a / b : Nat) := rfl
|
||||
@[norm_cast]
|
||||
@@ -165,6 +167,9 @@ def tdiv : (@& Int) → (@& Int) → Int
|
||||
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
|
||||
particular, `a % 0 = a`.
|
||||
|
||||
`tmod` satisfies `natAbs (tmod a b) = natAbs a % natAbs b`,
|
||||
and when `b` does not divide `a`, `tmod a b` has the same sign as `a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
|
||||
|
||||
@@ -229,7 +234,7 @@ def fdiv : Int → Int → Int
|
||||
| -[m+1], -[n+1] => ofNat (succ m / succ n)
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the F-rounding convention
|
||||
Integer modulus. This version of integer modulus uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
|
||||
@@ -268,11 +273,14 @@ Balanced mod (and balanced div) are a division and modulus pair such
|
||||
that `b * (Int.bdiv a b) + Int.bmod a b = a` and
|
||||
`-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
|
||||
|
||||
This is used in Omega as well as signed bitvectors.
|
||||
Note that unlike `emod`, `fmod`, and `tmod`,
|
||||
`bmod` takes a natural number as the second argument, rather than an integer.
|
||||
|
||||
This function is used in `omega` as well as signed bitvectors.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced modulus. This version of Integer modulus uses the
|
||||
Balanced modulus. This version of integer modulus uses the
|
||||
balanced rounding convention, which guarantees that
|
||||
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
|
||||
@@ -18,7 +18,7 @@ open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
|
||||
-- /-! ### dvd -/
|
||||
/-! ### dvd -/
|
||||
|
||||
protected theorem dvd_def (a b : Int) : (a ∣ b) = Exists (fun c => b = a * c) := rfl
|
||||
|
||||
@@ -53,7 +53,7 @@ protected theorem dvd_mul_left (a b : Int) : b ∣ a * b := ⟨_, Int.mul_comm .
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
@[simp] protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [← e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
@@ -67,7 +67,7 @@ protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natAbs := by
|
||||
rw [← natAbs_dvd_natAbs, natAbs_ofNat]
|
||||
|
||||
/-! ### *div zero -/
|
||||
/-! ### ediv zero -/
|
||||
|
||||
@[simp] theorem zero_ediv : ∀ b : Int, 0 / b = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
@@ -77,7 +77,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
/-! ### mod zero -/
|
||||
/-! ### emod zero -/
|
||||
|
||||
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl
|
||||
|
||||
@@ -89,7 +89,6 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natA
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
@@ -106,18 +105,23 @@ where
|
||||
← Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
|
||||
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
|
||||
|
||||
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
|
||||
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
|
||||
rw [Int.mul_comm]; exact emod_add_ediv ..
|
||||
|
||||
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
|
||||
rw [Int.add_comm]; exact emod_add_ediv ..
|
||||
|
||||
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
|
||||
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
|
||||
rw [Int.mul_comm]; exact ediv_add_emod ..
|
||||
|
||||
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
|
||||
rw [← Int.add_sub_cancel (a % b), emod_add_ediv]
|
||||
|
||||
/-! ### `/` ediv -/
|
||||
|
||||
@[simp] protected theorem ediv_neg : ∀ a b : Int, a / (-b) = -(a / b)
|
||||
@[simp] theorem ediv_neg : ∀ a b : Int, a / (-b) = -(a / b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat _, -[_+1] => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], succ _ | -[_+1], -[_+1] => rfl
|
||||
@@ -154,6 +158,10 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
|
||||
apply congrArg negSucc
|
||||
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
|
||||
|
||||
theorem add_mul_ediv_left (a : Int) {b : Int}
|
||||
(c : Int) (H : b ≠ 0) : (a + b * c) / b = a / b + c :=
|
||||
Int.mul_comm .. ▸ Int.add_mul_ediv_right _ _ H
|
||||
|
||||
theorem add_ediv_of_dvd_right {a b c : Int} (H : c ∣ b) : (a + b) / c = a / c + b / c :=
|
||||
if h : c = 0 then by simp [h] else by
|
||||
let ⟨k, hk⟩ := H
|
||||
@@ -170,13 +178,14 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c +
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
| Int.ofNat (b+1), _ =>
|
||||
rcases a with ⟨a⟩ <;> simp [Int.ediv]
|
||||
norm_cast
|
||||
simp
|
||||
|
||||
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
|
||||
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
|
||||
|
||||
/-! ### emod -/
|
||||
|
||||
@@ -189,16 +198,6 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
|
||||
| ofNat _, _, ⟨_, rfl⟩ => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
|
||||
| -[_+1], _, ⟨_, rfl⟩ => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
|
||||
|
||||
theorem mul_ediv_self_le {x k : Int} (h : k ≠ 0) : k * (x / k) ≤ x :=
|
||||
calc k * (x / k)
|
||||
_ ≤ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
|
||||
_ = x := ediv_add_emod _ _
|
||||
|
||||
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
|
||||
calc x
|
||||
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
|
||||
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
|
||||
|
||||
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
|
||||
if cz : c = 0 then by
|
||||
rw [cz, Int.mul_zero, Int.add_zero]
|
||||
@@ -306,6 +305,18 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a ∣ b) : a = 0 ∨ 0 < b % a :
|
||||
· simp_all
|
||||
· exact Or.inr (Int.lt_iff_le_and_ne.mpr ⟨emod_nonneg b w, Ne.symm h⟩)
|
||||
|
||||
/-! ### `/` and ordering -/
|
||||
|
||||
theorem mul_ediv_self_le {x k : Int} (h : k ≠ 0) : k * (x / k) ≤ x :=
|
||||
calc k * (x / k)
|
||||
_ ≤ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
|
||||
_ = x := ediv_add_emod _ _
|
||||
|
||||
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
|
||||
calc x
|
||||
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
|
||||
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
@[simp] theorem bmod_emod : bmod x m % m = x % m := by
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,10 @@ import Init.Data.Int.DivMod.Lemmas
|
||||
|
||||
/-!
|
||||
Definition and lemmas for gcd and lcm over Int
|
||||
|
||||
## Future work
|
||||
Most of the material about `Nat.gcd` and `Nat.lcm` from `Init.Data.Nat.Gcd` and `Init.Data.Nat.Lcm`
|
||||
has analogues for `Int.gcd` and `Int.lcm` that should be added to this file.
|
||||
-/
|
||||
namespace Int
|
||||
|
||||
|
||||
@@ -25,31 +25,32 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
|
||||
|
||||
@[norm_cast] theorem ofNat_add (n m : Nat) : (↑(n + m) : Int) = n + m := rfl
|
||||
@[norm_cast] theorem ofNat_mul (n m : Nat) : (↑(n * m) : Int) = n * m := rfl
|
||||
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
|
||||
@[norm_cast] theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
|
||||
|
||||
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
|
||||
@[local simp] theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
|
||||
@[local simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
|
||||
|
||||
theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
|
||||
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
|
||||
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
|
||||
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
|
||||
|
||||
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
|
||||
|
||||
/-! ## These are only for internal use -/
|
||||
|
||||
@[simp] theorem add_def {a b : Int} : Int.add a b = a + b := rfl
|
||||
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
|
||||
|
||||
/-!
|
||||
## These are only for internal use
|
||||
|
||||
Ideally these could all be made private, but they are used in downstream libraries.
|
||||
-/
|
||||
|
||||
@[local simp] theorem ofNat_add_ofNat (m n : Nat) : (↑m + ↑n : Int) = ↑(m + n) := rfl
|
||||
@[local simp] theorem ofNat_add_negSucc (m n : Nat) : ↑m + -[n+1] = subNatNat m (succ n) := rfl
|
||||
@[local simp] theorem negSucc_add_ofNat (m n : Nat) : -[m+1] + ↑n = subNatNat n (succ m) := rfl
|
||||
@[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
|
||||
|
||||
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
|
||||
|
||||
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (↑m * ↑n : Int) = ↑(m * n) := rfl
|
||||
@[local simp] theorem ofNat_mul_negSucc' (m n : Nat) : ↑m * -[n+1] = negOfNat (m * succ n) := rfl
|
||||
@[local simp] theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * ↑n = negOfNat (succ m * n) := rfl
|
||||
@[local simp] theorem negSucc_mul_negSucc' (m n : Nat) :
|
||||
@[local simp] private theorem ofNat_mul_negSucc' (m n : Nat) : ↑m * -[n+1] = negOfNat (m * succ n) := rfl
|
||||
@[local simp] private theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * ↑n = negOfNat (succ m * n) := rfl
|
||||
@[local simp] private theorem negSucc_mul_negSucc' (m n : Nat) :
|
||||
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
|
||||
|
||||
/- ## some basic functions and properties -/
|
||||
@@ -64,11 +65,14 @@ theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H =
|
||||
|
||||
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
|
||||
|
||||
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] ≠ 0 := nofun
|
||||
|
||||
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 ≠ -[n+1] := nofun
|
||||
|
||||
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
|
||||
@[simp, norm_cast] theorem cast_ofNat_Int :
|
||||
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
|
||||
|
||||
/- ## neg -/
|
||||
@@ -78,7 +82,7 @@ theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
|
||||
| succ _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
protected theorem neg_inj {a b : Int} : -a = -b ↔ a = b :=
|
||||
@[simp] protected theorem neg_inj {a b : Int} : -a = -b ↔ a = b :=
|
||||
⟨fun h => by rw [← Int.neg_neg a, ← Int.neg_neg b, h], congrArg _⟩
|
||||
|
||||
@[simp] protected theorem neg_eq_zero : -a = 0 ↔ a = 0 := Int.neg_inj (b := 0)
|
||||
@@ -86,12 +90,13 @@ protected theorem neg_inj {a b : Int} : -a = -b ↔ a = b :=
|
||||
protected theorem neg_ne_zero : -a ≠ 0 ↔ a ≠ 0 := not_congr Int.neg_eq_zero
|
||||
|
||||
protected theorem sub_eq_add_neg {a b : Int} : a - b = a + -b := rfl
|
||||
protected theorem add_neg_eq_sub {a b : Int} : a + -b = a - b := rfl
|
||||
|
||||
theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
|
||||
|
||||
/- ## basic properties of subNatNat -/
|
||||
|
||||
-- @[elabAsElim] -- TODO(Mario): unexpected eliminator resulting type
|
||||
@[elab_as_elim]
|
||||
theorem subNatNat_elim (m n : Nat) (motive : Nat → Nat → Int → Prop)
|
||||
(hp : ∀ i n, motive (n + i) n i)
|
||||
(hn : ∀ i m, motive m (m + i + 1) -[i+1]) :
|
||||
@@ -140,29 +145,6 @@ theorem subNatNat_of_lt {m n : Nat} (h : m < n) : subNatNat m n = -[pred (n - m)
|
||||
rw [Nat.sub_eq_iff_eq_add' h]
|
||||
simp
|
||||
|
||||
/- # Additive group properties -/
|
||||
|
||||
/- addition -/
|
||||
|
||||
protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat n, ofNat m => by simp [Nat.add_comm]
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
instance : Std.Commutative (α := Int) (· + ·) := ⟨Int.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
|
||||
left_id := Int.zero_add
|
||||
right_id := Int.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
|
||||
theorem subNatNat_sub (h : n ≤ m) (k : Nat) : subNatNat (m - n) k = subNatNat m (k + n) := by
|
||||
rwa [← subNatNat_add_add _ _ n, Nat.sub_add_cancel]
|
||||
|
||||
@@ -191,6 +173,34 @@ theorem subNatNat_add_negSucc (m n k : Nat) :
|
||||
← Nat.add_assoc, succ_sub_succ_eq_sub, Nat.add_comm n,Nat.add_sub_assoc (Nat.le_of_lt h'),
|
||||
Nat.add_comm]
|
||||
|
||||
theorem subNatNat_self : ∀ n, subNatNat n n = 0
|
||||
| 0 => rfl
|
||||
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
|
||||
|
||||
/- # Additive group properties -/
|
||||
|
||||
/- addition -/
|
||||
|
||||
protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat n, ofNat m => by simp [Nat.add_comm]
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
|
||||
instance : Std.Commutative (α := Int) (· + ·) := ⟨Int.add_comm⟩
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
|
||||
left_id := Int.zero_add
|
||||
right_id := Int.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
|
||||
protected theorem add_assoc : ∀ a b c : Int, a + b + c = a + (b + c)
|
||||
| (m:Nat), (n:Nat), _ => aux1 ..
|
||||
| Nat.cast m, b, Nat.cast k => by
|
||||
@@ -222,18 +232,12 @@ protected theorem add_right_comm (a b c : Int) : a + b + c = a + c + b := by
|
||||
|
||||
/- ## negation -/
|
||||
|
||||
theorem subNatNat_self : ∀ n, subNatNat n n = 0
|
||||
protected theorem add_left_neg : ∀ a : Int, -a + a = 0
|
||||
| 0 => rfl
|
||||
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
|
||||
| succ m => by simp [neg_ofNat_succ]
|
||||
| -[m+1] => by simp [neg_negSucc]
|
||||
|
||||
attribute [local simp] subNatNat_self
|
||||
|
||||
@[local simp] protected theorem add_left_neg : ∀ a : Int, -a + a = 0
|
||||
| 0 => rfl
|
||||
| succ m => by simp
|
||||
| -[m+1] => by simp
|
||||
|
||||
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
|
||||
protected theorem add_right_neg (a : Int) : a + -a = 0 := by
|
||||
rw [Int.add_comm, Int.add_left_neg]
|
||||
|
||||
protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
|
||||
@@ -264,11 +268,22 @@ protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := b
|
||||
have h₁ : -a + (a + b) = -a + (a + c) := by rw [h]
|
||||
simp [← Int.add_assoc, Int.add_left_neg, Int.zero_add] at h₁; exact h₁
|
||||
|
||||
@[local simp] protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
|
||||
protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
|
||||
apply Int.add_left_cancel (a := a + b)
|
||||
rw [Int.add_right_neg, Int.add_comm a, ← Int.add_assoc, Int.add_assoc b,
|
||||
Int.add_right_neg, Int.add_zero, Int.add_right_neg]
|
||||
|
||||
/--
|
||||
If a predicate on the integers is invariant under negation,
|
||||
then it is sufficient to prove it for the nonnegative integers.
|
||||
-/
|
||||
theorem wlog_sign {P : Int → Prop} (inv : ∀ i, P i ↔ P (-i)) (w : ∀ n : Nat, P n) (i : Int) : P i := by
|
||||
cases i with
|
||||
| ofNat n => exact w n
|
||||
| negSucc n =>
|
||||
rw [negSucc_eq, ← inv, ← ofNat_succ]
|
||||
apply w
|
||||
|
||||
/- ## subtraction -/
|
||||
|
||||
@[simp] theorem negSucc_sub_one (n : Nat) : -[n+1] - 1 = -[n + 1 +1] := rfl
|
||||
@@ -292,13 +307,13 @@ protected theorem sub_eq_zero {a b : Int} : a - b = 0 ↔ a = b :=
|
||||
⟨Int.eq_of_sub_eq_zero, Int.sub_eq_zero_of_eq⟩
|
||||
|
||||
protected theorem sub_sub (a b c : Int) : a - b - c = a - (b + c) := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add]
|
||||
|
||||
protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_comm]
|
||||
simp [Int.sub_eq_add_neg, Int.add_comm, Int.neg_add]
|
||||
|
||||
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc]
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc, Int.neg_add, Int.add_right_neg]
|
||||
|
||||
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@@ -309,7 +324,7 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
Int.add_neg_cancel_right a b
|
||||
|
||||
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, ← Int.sub_eq_add_neg]
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
|
||||
|
||||
@[norm_cast] theorem ofNat_sub (h : m ≤ n) : ((n - m : Nat) : Int) = n - m := by
|
||||
match m with
|
||||
@@ -318,6 +333,7 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
|
||||
show ofNat (n - succ m) = subNatNat n (succ m)
|
||||
rw [subNatNat, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_coe' (n : Nat) : -[n+1] = -↑n - 1 := by
|
||||
rw [Int.sub_eq_add_neg, ← Int.neg_add]; rfl
|
||||
|
||||
@@ -327,11 +343,11 @@ protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = ↑m - ↑n :=
|
||||
rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
|
||||
Int.add_right_neg, Int.add_zero]
|
||||
· intros i n
|
||||
simp only [negSucc_coe, ofNat_add, Int.sub_eq_add_neg, Int.neg_add, ← Int.add_assoc]
|
||||
rw [← @Int.sub_eq_add_neg n, ← ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
|
||||
simp only [negSucc_eq, ofNat_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, ← Int.add_assoc]
|
||||
rw [Int.add_neg_eq_sub (a := n), ← ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
|
||||
apply Nat.le_refl
|
||||
|
||||
theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
@[simp] theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
rw [← Int.subNatNat_eq_coe]
|
||||
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_)
|
||||
· exact (Nat.add_sub_cancel_left ..).symm
|
||||
@@ -347,6 +363,9 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
|
||||
norm_cast
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem negSucc_eq_neg_ofNat_iff {a b : Nat} : -[a+1] = - (b : Int) ↔ a + 1 = b := by
|
||||
rw [eq_comm, neg_ofNat_eq_negSucc_iff, eq_comm]
|
||||
|
||||
@[simp] theorem neg_ofNat_eq_negSucc_add_one_iff {a b : Nat} : - (a : Int) = -[b+1] + 1 ↔ a = b := by
|
||||
cases b with
|
||||
| zero => simp; norm_cast
|
||||
@@ -355,30 +374,33 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
|
||||
norm_cast
|
||||
simp [eq_comm]
|
||||
|
||||
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) ↔ a = b := by
|
||||
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
|
||||
|
||||
/- ## add/sub injectivity -/
|
||||
|
||||
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
apply Iff.intro
|
||||
· intro p
|
||||
rw [←Int.add_sub_cancel i k, ←Int.add_sub_cancel j k, p]
|
||||
· exact congrArg (· + k)
|
||||
|
||||
protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
@[simp] protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
simp [Int.add_comm k, Int.add_left_inj]
|
||||
|
||||
protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
@[simp] protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.neg_inj, Int.add_right_inj]
|
||||
|
||||
protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
@[simp] protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_left_inj]
|
||||
|
||||
/- ## Ring properties -/
|
||||
|
||||
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -↑(m * succ n) := rfl
|
||||
theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -↑(m * succ n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -↑(succ m * n) := rfl
|
||||
theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -↑(succ m * n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
|
||||
theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
|
||||
|
||||
protected theorem mul_comm (a b : Int) : a * b = b * a := by
|
||||
cases a <;> cases b <;> simp [Nat.mul_comm]
|
||||
@@ -396,11 +418,10 @@ theorem negSucc_mul_negOfNat (m n : Nat) : -[m+1] * negOfNat n = ofNat (succ m *
|
||||
theorem negOfNat_mul_negSucc (m n : Nat) : negOfNat n * -[m+1] = ofNat (n * succ m) := by
|
||||
rw [Int.mul_comm, negSucc_mul_negOfNat, Nat.mul_comm]
|
||||
|
||||
attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
|
||||
negSucc_mul_negOfNat negOfNat_mul_negSucc
|
||||
|
||||
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
|
||||
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
|
||||
cases a <;> cases b <;> cases c <;>
|
||||
simp [Nat.mul_assoc, ofNat_mul_negOfNat, negOfNat_mul_ofNat, negSucc_mul_negOfNat, negOfNat_mul_negSucc]
|
||||
|
||||
instance : Std.Associative (α := Int) (· * ·) := ⟨Int.mul_assoc⟩
|
||||
|
||||
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
|
||||
@@ -418,7 +439,7 @@ theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases
|
||||
theorem ofNat_mul_subNatNat (m n k : Nat) :
|
||||
m * subNatNat n k = subNatNat (m * n) (m * k) := by
|
||||
cases m with
|
||||
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul]
|
||||
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul, subNatNat_self]
|
||||
| succ m => cases n.lt_or_ge k with
|
||||
| inl h =>
|
||||
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
|
||||
@@ -446,8 +467,7 @@ theorem negSucc_mul_subNatNat (m n k : Nat) :
|
||||
Nat.mul_sub_left_distrib, ← succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁)]; rfl
|
||||
| inr h' => rw [Nat.le_antisymm h h', subNatNat_self, subNatNat_self, Int.mul_zero]
|
||||
|
||||
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat
|
||||
|
||||
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat in
|
||||
protected theorem mul_add : ∀ a b c : Int, a * (b + c) = a * b + a * c
|
||||
| (m:Nat), (n:Nat), (k:Nat) => by simp [Nat.left_distrib]
|
||||
| (m:Nat), (n:Nat), -[k+1] => by
|
||||
@@ -495,7 +515,9 @@ instance : Std.LawfulIdentity (α := Int) (· * ·) 1 where
|
||||
left_id := Int.one_mul
|
||||
right_id := Int.mul_one
|
||||
|
||||
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
@[simp] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
|
||||
@[simp] protected theorem neg_one_mul (a : Int) : -1 * a = -a := by rw [Int.neg_mul, Int.one_mul]
|
||||
|
||||
protected theorem neg_eq_neg_one_mul : ∀ a : Int, -a = -1 * a
|
||||
| 0 => rfl
|
||||
@@ -544,16 +566,18 @@ The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul
|
||||
but it is convenient to have these earlier, for users who only need `Nat` and `Int`.
|
||||
-/
|
||||
|
||||
theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
|
||||
protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
|
||||
|
||||
theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
|
||||
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
|
||||
|
||||
@[simp] theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
|
||||
@[simp] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
|
||||
-- Note this only works because of local simp attributes in this file,
|
||||
-- so it still makes sense to tag the lemmas with `@[simp]`.
|
||||
simp
|
||||
|
||||
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
|
||||
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl
|
||||
|
||||
@[simp] protected theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
|
||||
simp
|
||||
|
||||
end Int
|
||||
|
||||
@@ -15,6 +15,32 @@ import Init.Omega
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ### miscellaneous lemmas -/
|
||||
|
||||
@[simp] theorem natCast_le_zero : {n : Nat} → (n : Int) ≤ 0 ↔ n = 0 := by omega
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c ↔ a = c + b := by omega
|
||||
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c ↔ a = b + c := by omega
|
||||
|
||||
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i ≤ 0 ↔ 0 ≤ i := by omega
|
||||
|
||||
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ≤ ((no_index (OfNat.ofNat n)) : Int) :=
|
||||
ofNat_nonneg _
|
||||
|
||||
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (no_index (OfNat.ofNat m)) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) ≤ (m : Int) :=
|
||||
Int.le_trans (by simp) (ofNat_zero_le m)
|
||||
|
||||
/-! ### toNat -/
|
||||
|
||||
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
|
||||
symm
|
||||
simp only [Int.toNat]
|
||||
@@ -39,6 +65,60 @@ namespace Int
|
||||
simp [toNat]
|
||||
split <;> simp_all <;> omega
|
||||
|
||||
@[simp] theorem toNat_eq_zero : ∀ {n : Int}, n.toNat = 0 ↔ n ≤ 0 := by omega
|
||||
|
||||
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat ≤ n ↔ m ≤ n := by omega
|
||||
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n ↔ m < n := by omega
|
||||
|
||||
/-! ### natAbs -/
|
||||
|
||||
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d ∣ n) (h₁ : n.natAbs < d.natAbs) :
|
||||
n = 0 := by
|
||||
obtain ⟨a, rfl⟩ := h
|
||||
rw [natAbs_mul] at h₁
|
||||
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
|
||||
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
@[simp] protected theorem min_assoc : ∀ (a b c : Int), min (min a b) c = min a (min b c) := by omega
|
||||
instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
|
||||
|
||||
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
|
||||
rw [← Int.min_assoc, Int.min_self]
|
||||
|
||||
@[simp] protected theorem min_self_assoc' {m n : Int} : min n (min m n) = min n m := by
|
||||
rw [Int.min_comm m n, ← Int.min_assoc, Int.min_self]
|
||||
|
||||
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
|
||||
instance : Std.Associative (α := Nat) max := ⟨Nat.max_assoc⟩
|
||||
|
||||
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
|
||||
rw [← Int.max_assoc, Int.max_self]
|
||||
|
||||
@[simp] protected theorem max_self_assoc' {m n : Int} : max n (max m n) = max n m := by
|
||||
rw [Int.max_comm m n, ← Int.max_assoc, Int.max_self]
|
||||
|
||||
protected theorem max_min_distrib_left (a b c : Int) : max a (min b c) = min (max a b) (max a c) := by omega
|
||||
|
||||
protected theorem min_max_distrib_left (a b c : Int) : min a (max b c) = max (min a b) (min a c) := by omega
|
||||
|
||||
protected theorem max_min_distrib_right (a b c : Int) :
|
||||
max (min a b) c = min (max a c) (max b c) := by omega
|
||||
|
||||
protected theorem min_max_distrib_right (a b c : Int) :
|
||||
min (max a b) c = max (min a c) (min b c) := by omega
|
||||
|
||||
protected theorem sub_min_sub_right (a b c : Int) : min (a - c) (b - c) = min a b - c := by omega
|
||||
|
||||
protected theorem sub_max_sub_right (a b c : Int) : max (a - c) (b - c) = max a b - c := by omega
|
||||
|
||||
protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max b c := by omega
|
||||
|
||||
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
|
||||
|
||||
/-! ### bmod -/
|
||||
|
||||
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
(x.bmod m) < 0 ↔ (-(m / 2) ≤ x ∧ x < 0) ∨ ((m + 1) / 2 ≤ x) := by
|
||||
simp only [Int.bmod_def]
|
||||
@@ -46,4 +126,18 @@ theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
|
||||
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
|
||||
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
|
||||
|
||||
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m + 1) / 2) :
|
||||
n.bmod m = n := by
|
||||
rw [← Int.sub_eq_zero]
|
||||
have := le_bmod (x := n) (m := m) (by omega)
|
||||
have := bmod_lt (x := n) (m := m) (by omega)
|
||||
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
|
||||
omega
|
||||
|
||||
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n ∣ m) :
|
||||
(a.bmod m).bmod n = a.bmod n := by
|
||||
rw [← Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
|
||||
obtain ⟨k, rfl⟩ := hnm
|
||||
simp [Int.mul_assoc]
|
||||
|
||||
end Int
|
||||
|
||||
@@ -9,6 +9,7 @@ import Init.Data.Prod
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.Int.DivMod.Bootstrap
|
||||
import Init.Data.Int.Cooper
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.RArray
|
||||
import Init.Data.AC
|
||||
@@ -186,14 +187,13 @@ theorem cmod_gt_of_pos (a : Int) {b : Int} (h : 0 < b) : cmod a b > -b :=
|
||||
|
||||
theorem cmod_nonpos (a : Int) {b : Int} (h : b ≠ 0) : cmod a b ≤ 0 := by
|
||||
have := Int.neg_le_neg (Int.emod_nonneg (-a) h)
|
||||
simp at this
|
||||
assumption
|
||||
simpa [cmod] using this
|
||||
|
||||
theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 ↔ a%b = 0 := by
|
||||
unfold cmod
|
||||
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
|
||||
simp at this
|
||||
simp [Int.neg_emod, ← this, Eq.comm]
|
||||
simp [Int.neg_emod_eq_sub_emod, ← this, Eq.comm]
|
||||
|
||||
private abbrev div_mul_cancel_of_mod_zero :=
|
||||
@Int.ediv_mul_cancel_of_emod_eq_zero
|
||||
@@ -250,14 +250,24 @@ def Poly.divCoeffs (k : Int) : Poly → Bool
|
||||
/--
|
||||
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
|
||||
-/
|
||||
def Poly.mul (p : Poly) (k : Int) : Poly :=
|
||||
def Poly.mul' (p : Poly) (k : Int) : Poly :=
|
||||
match p with
|
||||
| .num k' => .num (k*k')
|
||||
| .add k' v p => .add (k*k') v (mul p k)
|
||||
| .add k' v p => .add (k*k') v (mul' p k)
|
||||
|
||||
def Poly.mul (p : Poly) (k : Int) : Poly :=
|
||||
if k == 0 then
|
||||
.num 0
|
||||
else
|
||||
p.mul' k
|
||||
|
||||
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
|
||||
induction p <;> simp [mul, denote, *]
|
||||
rw [Int.mul_assoc, Int.mul_add]
|
||||
simp [mul]
|
||||
split
|
||||
next => simp [*, denote]
|
||||
next =>
|
||||
induction p <;> simp [mul', denote, *]
|
||||
rw [Int.mul_assoc, Int.mul_add]
|
||||
|
||||
attribute [local simp] Int.add_comm Int.add_assoc Int.add_left_comm Int.add_mul Int.mul_add
|
||||
attribute [local simp] Poly.insert Poly.denote Poly.norm Poly.addConst
|
||||
@@ -310,7 +320,6 @@ theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.di
|
||||
replace h₁ := div_mul_cancel_of_mod_zero h₁
|
||||
have ih := ih h₂
|
||||
simp [ih]
|
||||
apply congrArg (denote ctx p + ·)
|
||||
rw [Int.mul_right_comm, h₁]
|
||||
|
||||
attribute [local simp] Poly.divCoeffs Poly.getConst
|
||||
@@ -410,7 +419,7 @@ theorem norm_eq_var_const (ctx : Context) (lhs rhs : Expr) (x : Var) (k : Int) (
|
||||
simp [norm_eq_var_const_cert] at h
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
simp at h
|
||||
rw [←Int.sub_eq_zero, h, Int.add_comm, ← Int.sub_eq_add_neg, Int.sub_eq_zero]
|
||||
rw [←Int.sub_eq_zero, h, Int.add_comm, Int.add_neg_eq_sub, Int.sub_eq_zero]
|
||||
|
||||
private theorem mul_eq_zero_iff (a k : Int) (h₁ : k > 0) : k * a = 0 ↔ a = 0 := by
|
||||
conv => lhs; rw [← Int.mul_zero k]
|
||||
@@ -531,8 +540,9 @@ def Poly.isValidLe (p : Poly) : Bool :=
|
||||
| .num k => k ≤ 0
|
||||
| _ => false
|
||||
|
||||
attribute [-simp] Int.not_le in
|
||||
theorem le_eq_false (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isUnsatLe → (lhs.denote ctx ≤ rhs.denote ctx) = False := by
|
||||
simp [Poly.isUnsatLe] <;> split <;> simp
|
||||
simp only [Poly.isUnsatLe] <;> split <;> simp
|
||||
next p k h =>
|
||||
intro h'
|
||||
replace h := congrArg (Poly.denote ctx) h
|
||||
@@ -791,7 +801,7 @@ theorem dvd_solve_elim (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int) (
|
||||
intro _ hd _; subst x₁ p; simp
|
||||
intro h₁ h₂
|
||||
rw [Int.add_comm] at h₁ h₂
|
||||
rw [← Int.sub_eq_add_neg]
|
||||
rw [Int.add_neg_eq_sub]
|
||||
exact dvd_solve_elim' hd h₁ h₂
|
||||
|
||||
theorem dvd_norm (ctx : Context) (d : Int) (p₁ p₂ : Poly) : p₁.norm == p₂ → d ∣ p₁.denote' ctx → d ∣ p₂.denote' ctx := by
|
||||
@@ -820,7 +830,7 @@ def le_neg_cert (p₁ p₂ : Poly) : Bool :=
|
||||
theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ → ¬ p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 := by
|
||||
simp [le_neg_cert]
|
||||
intro; subst p₂; simp; intro h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg <| Int.lt_of_not_ge h
|
||||
replace h : _ + 1 ≤ -0 := Int.neg_lt_neg h
|
||||
simp at h
|
||||
exact h
|
||||
|
||||
@@ -844,11 +854,28 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
· rw [← Int.zero_mul (Poly.denote ctx p₂)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
|
||||
· rw [← Int.zero_mul (Poly.denote ctx p₁)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
|
||||
|
||||
def le_combine_coeff_cert (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
|
||||
let a₁ := p₁.leadCoeff.natAbs
|
||||
let a₂ := p₂.leadCoeff.natAbs
|
||||
let p := p₁.mul a₂ |>.combine (p₂.mul a₁)
|
||||
k > 0 && (p.divCoeffs k && p₃ == p.div k)
|
||||
|
||||
theorem le_combine_coeff (ctx : Context) (p₁ p₂ p₃ : Poly) (k : Int)
|
||||
: le_combine_coeff_cert p₁ p₂ p₃ k → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp only [le_combine_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
|
||||
let a₁ := p₁.leadCoeff.natAbs
|
||||
let a₂ := p₂.leadCoeff.natAbs
|
||||
generalize h : (p₁.mul a₂ |>.combine (p₂.mul a₁)) = p
|
||||
intro h₁ h₂ h₃ h₄ h₅
|
||||
have := le_combine ctx p₁ p₂ p
|
||||
simp only [le_combine_cert, beq_iff_eq] at this
|
||||
have aux₁ := this h.symm h₄ h₅
|
||||
have := le_coeff ctx p p₃ k
|
||||
simp only [le_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp] at this
|
||||
exact this h₁ h₂ h₃ aux₁
|
||||
|
||||
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe → p.denote' ctx ≤ 0 → False := by
|
||||
simp [Poly.isUnsatLe]; split <;> simp
|
||||
intro h₁ h₂
|
||||
have := Int.lt_of_le_of_lt h₂ h₁
|
||||
simp at this
|
||||
|
||||
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 → p₂.denote' ctx = 0 := by
|
||||
simp at h
|
||||
@@ -990,10 +1017,8 @@ theorem eq_le_subst_nonpos (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
|
||||
intro h
|
||||
intro; subst p₃
|
||||
intro h₁ h₂
|
||||
simp [*]
|
||||
simp [*, -Int.neg_nonpos_iff]
|
||||
replace h₂ := Int.mul_le_mul_of_nonpos_left h₂ h; simp at h₂; clear h
|
||||
rw [← Int.neg_zero]
|
||||
apply Int.neg_le_neg
|
||||
rw [Int.mul_comm]
|
||||
assumption
|
||||
|
||||
@@ -1004,7 +1029,7 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: eq_of_core_cert p₁ p₂ p₃ → p₁.denote' ctx = p₂.denote' ctx → p₃.denote' ctx = 0 := by
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [h, ←Int.sub_eq_add_neg, Int.sub_self]
|
||||
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self]
|
||||
|
||||
def Poly.isUnsatDiseq (p : Poly) : Bool :=
|
||||
match p with
|
||||
@@ -1021,6 +1046,9 @@ theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p'
|
||||
simp [eq_coeff_cert]
|
||||
intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) → p.denote' ctx ≠ 0 → p'.denote' ctx ≠ 0 := by
|
||||
simp; intro _ _; simp [mul_eq_zero_iff, *]
|
||||
|
||||
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq → p.denote' ctx ≠ 0 → False := by
|
||||
simp [Poly.isUnsatDiseq] <;> split <;> simp
|
||||
|
||||
@@ -1041,7 +1069,693 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
simp [eq_of_core_cert]
|
||||
intro; subst p₃; simp
|
||||
intro h; rw [← Int.sub_eq_zero] at h
|
||||
rw [←Int.sub_eq_add_neg]; assumption
|
||||
rw [Int.add_neg_eq_sub]; assumption
|
||||
|
||||
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
|
||||
p₂ == p₁.mul (-1)
|
||||
|
||||
theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
|
||||
: eq_of_le_ge_cert p₁ p₂ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₁.denote' ctx = 0 := by
|
||||
simp [eq_of_le_ge_cert]
|
||||
intro; subst p₂; simp [-Int.neg_nonpos_iff]
|
||||
intro h₁ h₂
|
||||
simp [Int.eq_iff_le_and_ge, *]
|
||||
|
||||
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
|
||||
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
|
||||
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
|
||||
p₃ == p₁.addConst 1
|
||||
|
||||
theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
|
||||
: le_of_le_diseq_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp [le_of_le_diseq_cert]
|
||||
have (a : Int) : a ≤ 0 → ¬ a = 0 → 1 + a ≤ 0 := by
|
||||
intro h₁ h₂; cases (Int.lt_or_gt_of_ne h₂)
|
||||
next => apply Int.le_of_lt_add_one; rw [Int.add_comm, Int.add_lt_add_iff_right]; assumption
|
||||
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
|
||||
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
|
||||
|
||||
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
|
||||
p₂ == p₁.addConst 1 &&
|
||||
p₃ == (p₁.mul (-1)).addConst 1
|
||||
|
||||
theorem diseq_split (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → p₂.denote' ctx ≤ 0 ∨ p₃.denote' ctx ≤ 0 := by
|
||||
simp [diseq_split_cert]
|
||||
intro _ _; subst p₂ p₃; simp
|
||||
generalize p₁.denote ctx = p
|
||||
intro h; cases Int.lt_or_gt_of_ne h
|
||||
next h => have := Int.add_one_le_of_lt h; rw [Int.add_comm]; simp [*]
|
||||
next h => have := Int.add_one_le_of_lt (Int.neg_lt_neg h); simp at this; simp [*]
|
||||
|
||||
theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
|
||||
: diseq_split_cert p₁ p₂ p₃ → p₁.denote' ctx ≠ 0 → ¬p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
intro h₁ h₂ h₃
|
||||
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
|
||||
|
||||
def OrOver (n : Nat) (p : Nat → Prop) : Prop :=
|
||||
match n with
|
||||
| 0 => False
|
||||
| n+1 => p n ∨ OrOver n p
|
||||
|
||||
theorem orOver_one {p} : OrOver 1 p → p 0 := by simp [OrOver]
|
||||
|
||||
theorem orOver_resolve {n p} : OrOver (n+1) p → ¬ p n → OrOver n p := by
|
||||
intro h₁ h₂
|
||||
rw [OrOver] at h₁
|
||||
cases h₁
|
||||
· contradiction
|
||||
· assumption
|
||||
|
||||
def OrOver_cases_type (n : Nat) (p : Nat → Prop) : Prop :=
|
||||
match n with
|
||||
| 0 => p 0
|
||||
| n+1 => ¬ p (n+1) → OrOver_cases_type n p
|
||||
|
||||
theorem orOver_cases {n p} : OrOver (n+1) p → OrOver_cases_type n p := by
|
||||
induction n <;> simp [OrOver_cases_type]
|
||||
next => exact orOver_one
|
||||
next n ih => intro h₁ h₂; exact ih (orOver_resolve h₁ h₂)
|
||||
|
||||
private theorem orOver_of_p {i n p} (h₁ : i < n) (h₂ : p i) : OrOver n p := by
|
||||
induction n
|
||||
next => simp at h₁
|
||||
next n ih =>
|
||||
simp [OrOver]
|
||||
cases Nat.eq_or_lt_of_le <| Nat.le_of_lt_add_one h₁
|
||||
next h => subst i; exact Or.inl h₂
|
||||
next h => exact Or.inr (ih h)
|
||||
|
||||
private theorem orOver_of_exists {n p} : (∃ k, k < n ∧ p k) → OrOver n p := by
|
||||
intro ⟨k, h₁, h₂⟩
|
||||
apply orOver_of_p h₁ h₂
|
||||
|
||||
private theorem ofNat_toNat {a : Int} : a ≥ 0 → Int.ofNat a.toNat = a := by cases a <;> simp
|
||||
private theorem cast_toNat {a : Int} : a ≥ 0 → a.toNat = a := by cases a <;> simp
|
||||
private theorem ofNat_lt {a : Int} {n : Nat} : a ≥ 0 → a < Int.ofNat n → a.toNat < n := by cases a <;> simp
|
||||
private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
|
||||
private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
|
||||
private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
|
||||
private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
|
||||
|
||||
private theorem cooper_dvd_left_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm a (a * d / Int.gcd (a * d) c)) fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k ∧
|
||||
a * d ∣ c * p + (-a) * s + c * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_left a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
rw [Int.neg_mul] at h₂
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
rw [Int.neg_ediv_of_dvd Int.gcd_dvd_left] at h₂
|
||||
simp only [lcm_neg_right] at h₂
|
||||
have : c * k + c * p + -(a * s) = c * p + -(a * s) + c * k := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
rw [← ofNat_toNat h₁] at h₃ h₄ h₅
|
||||
rw [Int.add_comm] at h₄
|
||||
have := ofNat_lt h₁ h₂
|
||||
apply orOver_of_exists
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : b * Int.ofNat k.toNat + b * p + -(a * q) = b * p + -(a * q) + b * Int.ofNat k.toNat := by ac_rfl
|
||||
rw [this] at h₃
|
||||
exists k.toNat
|
||||
|
||||
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
|
||||
|
||||
def Poly.tail (p : Poly) : Poly :=
|
||||
match p with
|
||||
| .add _ _ p => p
|
||||
| _ => p
|
||||
|
||||
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
∧ a*d ∣ (p₂.addConst (c*k)).denote' ctx
|
||||
|
||||
private theorem denote'_mul_combine_mul_addConst_eq (ctx : Context) (p q : Poly) (a b c : Int)
|
||||
: ((p.mul b |>.combine (q.mul a)).addConst c).denote' ctx = b*p.denote ctx + a*q.denote ctx + c := by
|
||||
simp
|
||||
|
||||
private theorem denote'_addConst_eq (ctx : Context) (p : Poly) (a : Int)
|
||||
: (p.addConst a).denote' ctx = p.denote ctx + a := by
|
||||
simp
|
||||
|
||||
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_left_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_left_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' → p'.denote' ctx ≤ 0 := by
|
||||
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd1_cert p₁ p' a k → a ∣ p'.denote' ctx := by
|
||||
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let p := p₁.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := p.mul c |>.combine (s.mul (-a))
|
||||
d' == a*d && p' == p₂.addConst (c*k)
|
||||
|
||||
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k → cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' → d' ∣ p'.denote' ctx := by
|
||||
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_left_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver a.natAbs fun k =>
|
||||
b * p + (-a) * q + b * k ≤ 0 ∧
|
||||
a ∣ p + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true] at h
|
||||
assumption
|
||||
|
||||
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
|
||||
n == a.natAbs
|
||||
|
||||
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst (b*k)).denote' ctx ≤ 0
|
||||
∧ a ∣ (p.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_left_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_left_split ctx p₁ p₂) := by
|
||||
unfold cooper_left_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_left_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_left_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq]
|
||||
assumption
|
||||
|
||||
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
|
||||
|
||||
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_ineq_cert p₁ p₂ k b p' → p'.denote' ctx ≤ 0 := by
|
||||
simp [cooper_left_split_ineq_cert, cooper_left_split]
|
||||
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
|
||||
a == p₁.leadCoeff && p' == p₁.tail.addConst k
|
||||
|
||||
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_left_split ctx p₁ p₂ k → cooper_left_split_dvd_cert p₁ p' a k → a ∣ p'.denote' ctx := by
|
||||
simp [cooper_left_split_dvd_cert, cooper_left_split]
|
||||
intros; subst a p'; simp; assumption
|
||||
|
||||
private theorem cooper_dvd_right_core
|
||||
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
(h₃ : d ∣ c * x + s)
|
||||
: OrOver (Int.lcm b (b * d / Int.gcd (b * d) c)) fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k ∧
|
||||
b * d ∣ (-c) * q + b * s + (-c) * k := by
|
||||
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
|
||||
have h₁' : p ≤ (-a)*x := by rw [Int.neg_mul, ← Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
|
||||
have h₂' : b * x ≤ -q := by rw [← Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
|
||||
have ⟨k, h₁, h₂, h₃, h₄, h₅⟩ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp ⟨x, h₁', h₂', h₃⟩
|
||||
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
|
||||
apply orOver_of_exists
|
||||
have hlt := ofNat_lt h₁ h₂
|
||||
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
|
||||
have : -(a * k) + b * p + -(a * q) = b * p + -(a * q) + -(a * k) := by ac_rfl
|
||||
rw [this] at h₃; clear this
|
||||
rw [Int.sub_neg, Int.add_comm] at h₄
|
||||
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
|
||||
rw [this] at h₅; clear this
|
||||
exists k.toNat
|
||||
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
|
||||
|
||||
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
p₃.casesOn (fun _ => false) fun c z _ =>
|
||||
.and (x == y) <| .and (x == z) <|
|
||||
.and (a < 0) <| .and (b > 0) <|
|
||||
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
|
||||
|
||||
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
∧ b*d ∣ (p₂.addConst ((-c)*k)).denote' ctx
|
||||
|
||||
theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
|
||||
: cooper_dvd_right_cert p₁ p₂ p₃ d n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ d ∣ p₃.denote' ctx
|
||||
→ OrOver n (cooper_dvd_right_split ctx p₁ p₂ p₃ d) := by
|
||||
unfold cooper_dvd_right_split
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q c z s =>
|
||||
intro _ _; subst y z
|
||||
intro ha hb hd
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂ h₃
|
||||
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
|
||||
|
||||
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' → p'.denote' ctx ≤ 0 := by
|
||||
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd1_cert p₂ p' b k → b ∣ p'.denote' ctx := by
|
||||
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
|
||||
let q := p₂.tail
|
||||
let s := p₃.tail
|
||||
let b := p₂.leadCoeff
|
||||
let c := p₃.leadCoeff
|
||||
let p₂ := q.mul (-c) |>.combine (s.mul b)
|
||||
d' == b*d && p' == p₂.addConst ((-c)*k)
|
||||
|
||||
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
|
||||
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k → cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' → d' ∣ p'.denote' ctx := by
|
||||
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
|
||||
intros; subst d' p'; simp; assumption
|
||||
|
||||
private theorem cooper_right_core
|
||||
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
|
||||
(h₁ : a * x + p ≤ 0)
|
||||
(h₂ : b * x + q ≤ 0)
|
||||
: OrOver b.natAbs fun k =>
|
||||
b * p + (-a) * q + (-a) * k ≤ 0 ∧
|
||||
b ∣ q + k := by
|
||||
have d_pos : (0 : Int) < 1 := by decide
|
||||
have h₃ : 1 ∣ 0*x + 0 := Int.one_dvd _
|
||||
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
|
||||
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
|
||||
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
|
||||
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
|
||||
and_true, Int.neg_zero] at h
|
||||
assumption
|
||||
|
||||
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
|
||||
p₁.casesOn (fun _ => false) fun a x _ =>
|
||||
p₂.casesOn (fun _ => false) fun b y _ =>
|
||||
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
|
||||
|
||||
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let a := p₁.leadCoeff
|
||||
let b := p₂.leadCoeff
|
||||
let p₁ := p.mul b |>.combine (q.mul (-a))
|
||||
(p₁.addConst ((-a)*k)).denote' ctx ≤ 0
|
||||
∧ b ∣ (q.addConst k).denote' ctx
|
||||
|
||||
theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
|
||||
: cooper_right_cert p₁ p₂ n
|
||||
→ p₁.denote' ctx ≤ 0
|
||||
→ p₂.denote' ctx ≤ 0
|
||||
→ OrOver n (cooper_right_split ctx p₁ p₂) := by
|
||||
unfold cooper_right_split
|
||||
cases p₁ <;> cases p₂ <;> simp [cooper_right_cert, Poly.tail, -Poly.denote'_eq_denote]
|
||||
next a x p b y q =>
|
||||
intro; subst y
|
||||
intro ha hb
|
||||
intro; subst n
|
||||
simp only [Poly.denote'_add, Poly.leadCoeff]
|
||||
intro h₁ h₂
|
||||
have := cooper_right_core ha hb h₁ h₂
|
||||
simp only [denote'_mul_combine_mul_addConst_eq]
|
||||
simp only [denote'_addConst_eq, ←Int.neg_mul]
|
||||
assumption
|
||||
|
||||
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
|
||||
let p := p₁.tail
|
||||
let q := p₂.tail
|
||||
let b := p₂.leadCoeff
|
||||
let p₂ := p.mul b |>.combine (q.mul (-a))
|
||||
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
|
||||
|
||||
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_ineq_cert p₁ p₂ k a p' → p'.denote' ctx ≤ 0 := by
|
||||
simp [cooper_right_split_ineq_cert, cooper_right_split]
|
||||
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
|
||||
|
||||
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
|
||||
b == p₂.leadCoeff && p' == p₂.tail.addConst k
|
||||
|
||||
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
|
||||
: cooper_right_split ctx p₁ p₂ k → cooper_right_split_dvd_cert p₂ p' b k → b ∣ p'.denote' ctx := by
|
||||
simp [cooper_right_split_dvd_cert, cooper_right_split]
|
||||
intros; subst b p'; simp; assumption
|
||||
|
||||
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
|
||||
have aux₁ := Int.ediv_add_emod 1 a
|
||||
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
|
||||
simp [this] at aux₁
|
||||
assumption
|
||||
|
||||
private theorem ex_of_dvd {α β a b d x : Int}
|
||||
(h₀ : d > 1)
|
||||
(h₁ : d ∣ a*x + b)
|
||||
(h₂ : α * a + β * d = 1)
|
||||
: ∃ k, x = k * d + (- α * b) % d := by
|
||||
have ⟨k, h₁⟩ := h₁
|
||||
have aux₁ : (α * a) % d = 1 := by
|
||||
replace h₂ := congrArg (· % d) h₂; simp at h₂
|
||||
rw [one_emod_eq_one h₀] at h₂
|
||||
assumption
|
||||
have : ((α * a) * x) % d = (- α * b) % d := by
|
||||
replace h₁ := congrArg (α * ·) h₁; simp only at h₁
|
||||
rw [Int.mul_add] at h₁
|
||||
replace h₁ := congrArg (· - α * b) h₁; simp only [Int.add_sub_cancel] at h₁
|
||||
rw [← Int.mul_assoc, Int.mul_left_comm, Int.sub_eq_add_neg] at h₁
|
||||
replace h₁ := congrArg (· % d) h₁; simp only at h₁
|
||||
rw [Int.add_emod, Int.mul_emod_right, Int.zero_add, Int.emod_emod, ← Int.neg_mul] at h₁
|
||||
assumption
|
||||
have : x % d = (- α * b) % d := by
|
||||
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
|
||||
assumption
|
||||
have : x = (x / d)*d + (- α * b) % d := by
|
||||
conv => lhs; rw [← Int.ediv_add_emod x d]
|
||||
rw [Int.mul_comm, this]
|
||||
exists x / d
|
||||
|
||||
private theorem cdiv_le {a d k : Int} : d > 0 → a ≤ k * d → cdiv a d ≤ k := by
|
||||
intro h₁ h₂
|
||||
simp [cdiv]
|
||||
replace h₂ := Int.neg_le_neg h₂
|
||||
rw [← Int.neg_mul] at h₂
|
||||
replace h₂ := Int.le_ediv_of_mul_le h₁ h₂
|
||||
replace h₂ := Int.neg_le_neg h₂
|
||||
simp at h₂
|
||||
assumption
|
||||
|
||||
private theorem cooper_unsat'_helper {a b d c k x : Int}
|
||||
(d_pos : d > 0)
|
||||
(h₁ : x = k * d + c)
|
||||
(h₂ : a ≤ x)
|
||||
(h₃ : x ≤ b)
|
||||
: ¬ b < (cdiv (a - c) d) * d + c := by
|
||||
intro h₄
|
||||
have aux₁ : cdiv (a - c) d ≤ k := by
|
||||
rw [h₁] at h₂
|
||||
replace h₂ := Int.sub_right_le_of_le_add h₂
|
||||
exact cdiv_le d_pos h₂
|
||||
have aux₂ : cdiv (a - c) d * d ≤ k * d := Int.mul_le_mul_of_nonneg_right aux₁ (Int.le_of_lt d_pos)
|
||||
have aux₃ : cdiv (a - c) d * d + c ≤ k * d + c := Int.add_le_add_right aux₂ _
|
||||
have aux₄ : cdiv (a - c) d * d + c ≤ x := by rw [←h₁] at aux₃; assumption
|
||||
have aux₅ : cdiv (a - c) d * d + c ≤ b := Int.le_trans aux₄ h₃
|
||||
have := Int.lt_of_le_of_lt aux₅ h₄
|
||||
exact Int.lt_irrefl _ this
|
||||
|
||||
private theorem cooper_unsat' {a c b d e α β x : Int}
|
||||
(h₁ : d > 1)
|
||||
(h₂ : d ∣ c*x + e)
|
||||
(h₃ : α * c + β * d = 1)
|
||||
(h₄ : (-1)*x + a ≤ 0)
|
||||
(h₅ : x + b ≤ 0)
|
||||
(h₆ : -b < cdiv (a - -α * e % d) d * d + -α * e % d)
|
||||
: False := by
|
||||
have ⟨k, h⟩ := ex_of_dvd h₁ h₂ h₃
|
||||
have d_pos : d > 0 := Int.lt_trans (by decide) h₁
|
||||
replace h₄ := Int.le_neg_add_of_add_le h₄; simp at h₄
|
||||
replace h₅ := Int.neg_le_neg (Int.le_neg_add_of_add_le h₅); simp at h₅
|
||||
have := cooper_unsat'_helper d_pos h h₄ h₅
|
||||
exact this h₆
|
||||
|
||||
abbrev Poly.casesOnAdd (p : Poly) (k : Int → Var → Poly → Bool) : Bool :=
|
||||
p.casesOn (fun _ => false) k
|
||||
|
||||
abbrev Poly.casesOnNum (p : Poly) (k : Int → Bool) : Bool :=
|
||||
p.casesOn k (fun _ _ _ => false)
|
||||
|
||||
def cooper_unsat_cert (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int) : Bool :=
|
||||
p₁.casesOnAdd fun k₁ x p₁ =>
|
||||
p₂.casesOnAdd fun k₂ y p₂ =>
|
||||
p₃.casesOnAdd fun c z p₃ =>
|
||||
p₁.casesOnNum fun a =>
|
||||
p₂.casesOnNum fun b =>
|
||||
p₃.casesOnNum fun e =>
|
||||
(k₁ == -1) |>.and (k₂ == 1) |>.and
|
||||
(x == y) |>.and (x == z) |>.and
|
||||
(d > 1) |>.and (α * c + β * d == 1) |>.and
|
||||
(-b < cdiv (a - -α * e % d) d * d + -α * e % d)
|
||||
|
||||
theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int)
|
||||
: cooper_unsat_cert p₁ p₂ p₃ d α β →
|
||||
p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → d ∣ p₃.denote' ctx → False := by
|
||||
unfold cooper_unsat_cert <;> cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnAdd,
|
||||
Bool.false_eq_true, Poly.denote'_add, mul_def, add_def, false_implies]
|
||||
next k₁ x p₁ k₂ y p₂ c z p₃ =>
|
||||
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnNum, Int.reduceNeg,
|
||||
Bool.and_eq_true, beq_iff_eq, decide_eq_true_eq, and_imp, Bool.false_eq_true,
|
||||
mul_def, add_def, false_implies, Poly.denote]
|
||||
next a b e =>
|
||||
intro _ _ _ _; subst k₁ k₂ y z
|
||||
intro h₁ h₃ h₆; generalize Var.denote ctx x = x'
|
||||
intro h₄ h₅ h₂
|
||||
rw [Int.one_mul] at h₅
|
||||
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
|
||||
|
||||
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
|
||||
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
|
||||
simp
|
||||
rw [Int.add_neg_eq_sub, Int.sub_self]
|
||||
|
||||
theorem emod_nonneg (x y : Int) : y != 0 → -1 * (x % y) ≤ 0 := by
|
||||
simp; intro h
|
||||
have := Int.neg_le_neg (Int.emod_nonneg x h)
|
||||
simp at this
|
||||
assumption
|
||||
|
||||
def emod_le_cert (y n : Int) : Bool :=
|
||||
y != 0 && n == 1 - y.natAbs
|
||||
|
||||
theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n → x % y + n ≤ 0 := by
|
||||
simp [emod_le_cert]
|
||||
intro h₁
|
||||
cases Int.lt_or_gt_of_ne h₁
|
||||
next h =>
|
||||
rw [Int.ofNat_natAbs_of_nonpos (Int.le_of_lt h)]
|
||||
simp only [Int.sub_neg]
|
||||
intro; subst n
|
||||
rw [Int.add_assoc, Int.add_left_comm]
|
||||
apply Int.add_le_of_le_sub_left
|
||||
rw [Int.zero_sub, Int.add_comm]
|
||||
have : 0 < -y := by
|
||||
have := Int.neg_lt_neg h
|
||||
rw [Int.neg_zero] at this
|
||||
assumption
|
||||
have := Int.emod_lt_of_pos x this
|
||||
rw [Int.emod_neg] at this
|
||||
exact this
|
||||
next h =>
|
||||
rw [Int.natAbs_of_nonneg (Int.le_of_lt h)]
|
||||
intro; subst n
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm]
|
||||
apply Int.add_le_of_le_sub_left
|
||||
simp only [Int.add_comm, Int.sub_neg, Int.add_zero]
|
||||
exact Int.emod_lt_of_pos x h
|
||||
|
||||
theorem natCast_nonneg (x : Nat) : (-1:Int) * NatCast.natCast x ≤ 0 := by
|
||||
simp
|
||||
|
||||
private theorem dvd_le_tight' {d p b₁ b₂ : Int} (hd : d > 0) (h₁ : d ∣ p + b₁) (h₂ : p + b₂ ≤ 0)
|
||||
: p + (b₁ - d*((b₁-b₂) / d)) ≤ 0 := by
|
||||
have ⟨k, h⟩ := h₁
|
||||
replace h₁ : p = d*k - b₁ := by
|
||||
replace h := congrArg (· - b₁) h
|
||||
simp only [Int.add_sub_cancel] at h
|
||||
assumption
|
||||
replace h₂ : d*k - b₁ + b₂ ≤ 0 := by
|
||||
rw [h₁] at h₂; assumption
|
||||
have : d*k ≤ b₁ - b₂ := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, Lean.Omega.Int.add_le_zero_iff_le_neg,
|
||||
Int.neg_add, Int.neg_neg, Int.add_neg_eq_sub] at h₂
|
||||
assumption
|
||||
replace this : k ≤ (b₁ - b₂)/d := by
|
||||
rw [Int.mul_comm] at this; exact Int.le_ediv_of_mul_le hd this
|
||||
replace this := Int.mul_le_mul_of_nonneg_left this (Int.le_of_lt hd)
|
||||
rw [←h] at this
|
||||
replace this := Int.sub_nonpos_of_le this
|
||||
rw [Int.add_sub_assoc] at this
|
||||
exact this
|
||||
|
||||
private theorem eq_neg_addConst_add (ctx : Context) (p : Poly)
|
||||
: p.denote' ctx = (p.addConst (-p.getConst)).denote' ctx + p.getConst := by
|
||||
simp only [Poly.denote'_eq_denote, Poly.denote_addConst, Int.add_comm, Int.add_left_comm]
|
||||
rw [Int.add_right_neg]
|
||||
simp
|
||||
|
||||
def dvd_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
|
||||
let b₁ := p₁.getConst
|
||||
let b₂ := p₂.getConst
|
||||
let p := p₁.addConst (-b₁)
|
||||
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
|
||||
|
||||
theorem dvd_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
|
||||
: dvd_le_tight_cert d p₁ p₂ p₃ → d ∣ p₁.denote' ctx → p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp only [dvd_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
|
||||
generalize p₂.getConst = b₂
|
||||
intro hd _ _; subst p₂ p₃
|
||||
have := eq_neg_addConst_add ctx p₁
|
||||
revert this
|
||||
generalize p₁.getConst = b₁
|
||||
generalize p₁.addConst (-b₁) = p
|
||||
intro h₁; rw [h₁]; clear h₁
|
||||
simp only [denote'_addConst_eq]
|
||||
simp only [Poly.denote'_eq_denote]
|
||||
exact dvd_le_tight' hd
|
||||
|
||||
def dvd_neg_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
|
||||
let b₁ := p₁.getConst
|
||||
let b₂ := p₂.getConst
|
||||
let p := p₁.addConst (-b₁)
|
||||
let b₁ := -b₁
|
||||
let p := p.mul (-1)
|
||||
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
|
||||
|
||||
theorem Poly.mul_minus_one_getConst_eq (p : Poly) : (p.mul (-1)).getConst = -p.getConst := by
|
||||
simp [Poly.mul, Poly.getConst]
|
||||
induction p <;> simp [Poly.mul', Poly.getConst, *]
|
||||
|
||||
theorem dvd_neg_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
|
||||
: dvd_neg_le_tight_cert d p₁ p₂ p₃ → d ∣ p₁.denote' ctx → p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
|
||||
simp only [dvd_neg_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
|
||||
generalize p₂.getConst = b₂
|
||||
intro hd _ _; subst p₂ p₃
|
||||
simp only [Poly.denote'_eq_denote, Int.reduceNeg, Poly.denote_addConst, Poly.denote_mul,
|
||||
Int.mul_add, Int.neg_mul, Int.one_mul, Int.mul_neg, Int.neg_neg, Int.add_comm, Int.add_assoc]
|
||||
intro h₁ h₂
|
||||
replace h₁ := Int.dvd_neg.mpr h₁
|
||||
have := eq_neg_addConst_add ctx (p₁.mul (-1))
|
||||
simp [Poly.mul_minus_one_getConst_eq] at this
|
||||
rw [← Int.add_assoc] at this
|
||||
rw [this] at h₁; clear this
|
||||
rw [← Int.add_assoc]
|
||||
revert h₁ h₂
|
||||
generalize -Poly.denote ctx p₁ + p₁.getConst = p
|
||||
generalize -p₁.getConst = b₁
|
||||
intro h₁ h₂; rw [Int.add_comm] at h₁
|
||||
exact dvd_le_tight' hd h₂ h₁
|
||||
|
||||
theorem le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: norm_eq_cert lhs rhs p → lhs.denote ctx ≤ rhs.denote ctx → p.denote' ctx ≤ 0 := by
|
||||
intro h₁ h₂; rwa [norm_le ctx lhs rhs p h₁] at h₂
|
||||
|
||||
def not_le_norm_expr_cert (lhs rhs : Expr) (p : Poly) : Bool :=
|
||||
p == (((lhs.sub rhs).norm).mul (-1)).addConst 1
|
||||
|
||||
theorem not_le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
|
||||
: not_le_norm_expr_cert lhs rhs p → ¬ lhs.denote ctx ≤ rhs.denote ctx → p.denote' ctx ≤ 0 := by
|
||||
simp [not_le_norm_expr_cert]
|
||||
intro; subst p; simp
|
||||
intro h
|
||||
replace h := Int.sub_nonpos_of_le h
|
||||
rw [Int.add_comm, Int.add_sub_assoc] at h
|
||||
rw [Int.neg_sub]; assumption
|
||||
|
||||
theorem dvd_norm_expr (ctx : Context) (d : Int) (e : Expr) (p : Poly)
|
||||
: p == e.norm → d ∣ e.denote ctx → d ∣ p.denote' ctx := by
|
||||
simp; intro; subst p; simp
|
||||
|
||||
end Int.Linear
|
||||
|
||||
|
||||
70
src/Init/Data/Int/OfNat.lean
Normal file
70
src/Init/Data/Int/OfNat.lean
Normal file
@@ -0,0 +1,70 @@
|
||||
/-
|
||||
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.DivMod
|
||||
import Init.Data.RArray
|
||||
|
||||
namespace Int.OfNat
|
||||
/-!
|
||||
Helper definitions and theorems for converting `Nat` expressions into `Int` one.
|
||||
We use them to implement the arithmetic theories in `grind`
|
||||
-/
|
||||
|
||||
abbrev Var := Nat
|
||||
abbrev Context := Lean.RArray Nat
|
||||
def Var.denote (ctx : Context) (v : Var) : Nat :=
|
||||
ctx.get v
|
||||
|
||||
inductive Expr where
|
||||
| num (v : Nat)
|
||||
| var (i : Var)
|
||||
| add (a b : Expr)
|
||||
| mul (a b : Expr)
|
||||
| div (a b : Expr)
|
||||
| mod (a b : Expr)
|
||||
deriving BEq
|
||||
|
||||
def Expr.denote (ctx : Context) : Expr → Nat
|
||||
| .num k => k
|
||||
| .var v => v.denote ctx
|
||||
| .add a b => Nat.add (denote ctx a) (denote ctx b)
|
||||
| .mul a b => Nat.mul (denote ctx a) (denote ctx b)
|
||||
| .div a b => Nat.div (denote ctx a) (denote ctx b)
|
||||
| .mod a b => Nat.mod (denote ctx a) (denote ctx b)
|
||||
|
||||
def Expr.denoteAsInt (ctx : Context) : Expr → Int
|
||||
| .num k => Int.ofNat k
|
||||
| .var v => Int.ofNat (v.denote ctx)
|
||||
| .add a b => Int.add (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
| .mul a b => Int.mul (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
| .div a b => Int.ediv (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
| .mod a b => Int.emod (denoteAsInt ctx a) (denoteAsInt ctx b)
|
||||
|
||||
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
|
||||
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
|
||||
|
||||
theorem Expr.eq (ctx : Context) (lhs rhs : Expr)
|
||||
: (lhs.denote ctx = rhs.denote ctx) = (lhs.denoteAsInt ctx = rhs.denoteAsInt ctx) := by
|
||||
simp [denoteAsInt_eq, Int.ofNat_inj]
|
||||
|
||||
theorem Expr.le (ctx : Context) (lhs rhs : Expr)
|
||||
: (lhs.denote ctx ≤ rhs.denote ctx) = (lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx) := by
|
||||
simp [denoteAsInt_eq, Int.ofNat_le]
|
||||
|
||||
theorem of_le (ctx : Context) (lhs rhs : Expr)
|
||||
: lhs.denote ctx ≤ rhs.denote ctx → lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx := by
|
||||
rw [Expr.le ctx lhs rhs]; simp
|
||||
|
||||
theorem of_not_le (ctx : Context) (lhs rhs : Expr)
|
||||
: ¬ lhs.denote ctx ≤ rhs.denote ctx → ¬ lhs.denoteAsInt ctx ≤ rhs.denoteAsInt ctx := by
|
||||
rw [Expr.le ctx lhs rhs]; simp
|
||||
|
||||
theorem of_dvd (ctx : Context) (d : Nat) (e : Expr)
|
||||
: d ∣ e.denote ctx → Int.ofNat d ∣ e.denoteAsInt ctx := by
|
||||
simp [Expr.denoteAsInt_eq, Int.ofNat_dvd]
|
||||
|
||||
end Int.OfNat
|
||||
@@ -33,20 +33,18 @@ theorem lt_iff_add_one_le {a b : Int} : a < b ↔ a + 1 ≤ b := .rfl
|
||||
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a ≤ b := by
|
||||
simp [le_def, h]; constructor
|
||||
|
||||
attribute [local simp] Int.add_left_neg Int.add_right_neg Int.neg_add
|
||||
|
||||
theorem le.intro {a b : Int} (n : Nat) (h : a + n = b) : a ≤ b :=
|
||||
le.intro_sub n <| by rw [← h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
le.intro_sub n <| by rw [← h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_right_neg]
|
||||
|
||||
theorem le.dest_sub {a b : Int} (h : a ≤ b) : ∃ n : Nat, b - a = n := nonneg_def.1 h
|
||||
|
||||
theorem le.dest {a b : Int} (h : a ≤ b) : ∃ n : Nat, a + n = b :=
|
||||
let ⟨n, h₁⟩ := le.dest_sub h
|
||||
⟨n, by rw [← h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]⟩
|
||||
⟨n, by rw [← h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_neg]⟩
|
||||
|
||||
protected theorem le_total (a b : Int) : a ≤ b ∨ b ≤ a :=
|
||||
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
|
||||
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
|
||||
rwa [show -(b - a) = a - b by simp [Int.neg_add,Int.add_comm, Int.sub_eq_add_neg]] at H
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (↑m : Int) ≤ ↑n ↔ m ≤ n :=
|
||||
⟨fun h =>
|
||||
@@ -61,14 +59,14 @@ protected theorem le_total (a b : Int) : a ≤ b ∨ b ≤ a :=
|
||||
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 ≤ a) : ∃ n : Nat, a = n := by
|
||||
have t := le.dest_sub h; rwa [Int.sub_zero] at t
|
||||
|
||||
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : ∃ n : Nat, a = n.succ :=
|
||||
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : ∃ n : Nat, a = n + 1 :=
|
||||
let ⟨n, (h : ↑(1 + n) = a)⟩ := le.dest h
|
||||
⟨n, by rw [Nat.add_comm] at h; exact h.symm⟩
|
||||
|
||||
theorem lt_add_succ (a : Int) (n : Nat) : a < a + Nat.succ n :=
|
||||
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]; rfl
|
||||
theorem lt_add_succ (a : Int) (n : Nat) : a < a + (n + 1) :=
|
||||
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]
|
||||
|
||||
theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
|
||||
theorem lt.intro {a b : Int} {n : Nat} (h : a + (n + 1) = b) : a < b :=
|
||||
h ▸ lt_add_succ a n
|
||||
|
||||
theorem lt.dest {a b : Int} (h : a < b) : ∃ n : Nat, a + Nat.succ n = b :=
|
||||
@@ -117,7 +115,7 @@ protected theorem lt_iff_le_and_ne {a b : Int} : a < b ↔ a ≤ b ∧ a ≠ b :
|
||||
have : n ≠ 0 := aneb.imp fun eq => by rw [← hn, eq, ofNat_zero, Int.add_zero]
|
||||
apply lt.intro; rwa [← Nat.succ_pred_eq_of_pos (Nat.pos_of_ne_zero this)] at hn
|
||||
|
||||
theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
|
||||
protected theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
|
||||
|
||||
protected theorem zero_lt_one : (0 : Int) < 1 := ⟨_⟩
|
||||
|
||||
@@ -133,12 +131,15 @@ protected theorem lt_of_not_ge {a b : Int} (h : ¬a ≤ b) : b < a :=
|
||||
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a ≤ b :=
|
||||
(Int.lt_iff_le_not_le.mp h).right
|
||||
|
||||
protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
@[simp] protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
|
||||
|
||||
protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
@[simp] protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
by rw [← Int.not_le, Decidable.not_not]
|
||||
|
||||
protected theorem le_of_not_gt {a b : Int} (h : ¬ a > b) : a ≤ b :=
|
||||
Int.not_lt.mp h
|
||||
|
||||
protected theorem lt_trichotomy (a b : Int) : a < b ∨ a = b ∨ b < a :=
|
||||
if eq : a = b then .inr <| .inl eq else
|
||||
if le : a ≤ b then .inl <| Int.lt_iff_le_and_ne.2 ⟨le, eq⟩ else
|
||||
@@ -183,61 +184,19 @@ instance : Trans (α := Int) (· ≤ ·) (· < ·) (· < ·) := ⟨Int.lt_of_le_
|
||||
|
||||
instance : Trans (α := Int) (· < ·) (· < ·) (· < ·) := ⟨Int.lt_trans⟩
|
||||
|
||||
protected theorem min_def (n m : Int) : min n m = if n ≤ m then n else m := rfl
|
||||
|
||||
protected theorem max_def (n m : Int) : max n m = if n ≤ m then m else n := rfl
|
||||
|
||||
protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
simp [Int.min_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) min := ⟨Int.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
protected theorem min_le_left (a b : Int) : min a b ≤ a := Int.min_comm .. ▸ Int.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Int} (h : a ≤ b) : min a b = a := by simp [Int.min_def, h]
|
||||
|
||||
protected theorem min_eq_right {a b : Int} (h : b ≤ a) : min a b = b := by
|
||||
rw [Int.min_comm a b]; exact Int.min_eq_left h
|
||||
|
||||
protected theorem le_min {a b c : Int} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.min_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
simp only [Int.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) max := ⟨Int.max_comm⟩
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
protected theorem le_max_right (a b : Int) : b ≤ max a b := Int.max_comm .. ▸ Int.le_max_left ..
|
||||
|
||||
protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.max_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_eq_right {a b : Int} (h : a ≤ b) : max a b = b := by
|
||||
simp [Int.max_def, h, Int.not_lt.2 h]
|
||||
|
||||
protected theorem max_eq_left {a b : Int} (h : b ≤ a) : max a b = a := by
|
||||
rw [← Int.max_comm b a]; exact Int.max_eq_right h
|
||||
|
||||
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 ≤ a) : a = natAbs a := by
|
||||
theorem eq_natAbs_of_nonneg {a : Int} (h : 0 ≤ a) : a = natAbs a := by
|
||||
let ⟨n, e⟩ := eq_ofNat_of_zero_le h
|
||||
rw [e]; rfl
|
||||
|
||||
@[deprecated eq_natAbs_of_nonneg (since := "2025-03-11")]
|
||||
abbrev eq_natAbs_of_zero_le := @eq_natAbs_of_nonneg
|
||||
|
||||
theorem le_natAbs {a : Int} : a ≤ natAbs a :=
|
||||
match Int.le_total 0 a with
|
||||
| .inl h => by rw [eq_natAbs_of_zero_le h]; apply Int.le_refl
|
||||
| .inl h => by rw [eq_natAbs_of_nonneg h]; apply Int.le_refl
|
||||
| .inr h => Int.le_trans h (ofNat_zero_le _)
|
||||
|
||||
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
@[simp] theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
Int.not_le.1 fun h => let ⟨_, h⟩ := eq_ofNat_of_zero_le h; nomatch h
|
||||
|
||||
theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
|
||||
@@ -246,18 +205,6 @@ theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
|
||||
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 ≤ -[n+1] ↔ False := by
|
||||
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
|
||||
|
||||
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
|
||||
rw [Int.max_eq_left (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
|
||||
rw [Int.max_eq_right (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
|
||||
rw [Int.max_eq_right (negSucc_le_zero _)]
|
||||
|
||||
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
|
||||
rw [Int.max_eq_left (negSucc_le_zero _)]
|
||||
|
||||
protected theorem add_le_add_left {a b : Int} (h : a ≤ b) (c : Int) : c + a ≤ c + b :=
|
||||
let ⟨n, hn⟩ := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
|
||||
|
||||
@@ -279,10 +226,10 @@ protected theorem le_of_add_le_add_left {a b c : Int} (h : a + b ≤ a + c) : b
|
||||
protected theorem le_of_add_le_add_right {a b c : Int} (h : a + b ≤ c + b) : a ≤ c :=
|
||||
Int.le_of_add_le_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
|
||||
|
||||
protected theorem add_le_add_iff_left (a : Int) : a + b ≤ a + c ↔ b ≤ c :=
|
||||
@[simp] protected theorem add_le_add_iff_left (a : Int) : a + b ≤ a + c ↔ b ≤ c :=
|
||||
⟨Int.le_of_add_le_add_left, (Int.add_le_add_left · _)⟩
|
||||
|
||||
protected theorem add_le_add_iff_right (c : Int) : a + c ≤ b + c ↔ a ≤ b :=
|
||||
@[simp] protected theorem add_le_add_iff_right (c : Int) : a + c ≤ b + c ↔ a ≤ b :=
|
||||
⟨Int.le_of_add_le_add_right, (Int.add_le_add_right · _)⟩
|
||||
|
||||
protected theorem add_le_add {a b c d : Int} (h₁ : a ≤ b) (h₂ : c ≤ d) : a + c ≤ b + d :=
|
||||
@@ -301,6 +248,15 @@ protected theorem neg_le_neg {a b : Int} (h : a ≤ b) : -b ≤ -a := by
|
||||
have : 0 + -b ≤ -a + b + -b := Int.add_le_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
@[simp] protected theorem neg_le_neg_iff {a b : Int} : -a ≤ -b ↔ b ≤ a :=
|
||||
⟨fun h => by simpa using Int.neg_le_neg h, Int.neg_le_neg⟩
|
||||
|
||||
@[simp] protected theorem neg_le_zero_iff {a : Int} : -a ≤ 0 ↔ 0 ≤ a := by
|
||||
rw [← Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
|
||||
|
||||
@[simp] protected theorem zero_le_neg_iff {a : Int} : 0 ≤ -a ↔ a ≤ 0 := by
|
||||
rw [← Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
|
||||
|
||||
protected theorem le_of_neg_le_neg {a b : Int} (h : -b ≤ -a) : a ≤ b :=
|
||||
suffices - -a ≤ - -b by simp [Int.neg_neg] at this; assumption
|
||||
Int.neg_le_neg h
|
||||
@@ -318,6 +274,15 @@ protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
|
||||
have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
@[simp] protected theorem neg_lt_neg_iff {a b : Int} : -a < -b ↔ b < a :=
|
||||
⟨fun h => by simpa using Int.neg_lt_neg h, Int.neg_lt_neg⟩
|
||||
|
||||
@[simp] protected theorem neg_lt_zero_iff {a : Int} : -a < 0 ↔ 0 < a := by
|
||||
rw [← Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
|
||||
|
||||
@[simp] protected theorem zero_lt_neg_iff {a : Int} : 0 < -a ↔ a < 0 := by
|
||||
rw [← Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
|
||||
|
||||
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
|
||||
have : -a < -0 := Int.neg_lt_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
@@ -358,8 +323,124 @@ protected theorem sub_lt_self (a : Int) {b : Int} (h : 0 < b) : a - b < a :=
|
||||
|
||||
theorem add_one_le_of_lt {a b : Int} (H : a < b) : a + 1 ≤ b := H
|
||||
|
||||
/- ### Order properties and multiplication -/
|
||||
protected theorem le_iff_lt_add_one {a b : Int} : a ≤ b ↔ a < b + 1 := by
|
||||
rw [Int.lt_iff_add_one_le]
|
||||
exact (Int.add_le_add_iff_right 1).symm
|
||||
|
||||
/- ### min and max -/
|
||||
|
||||
protected theorem min_def (n m : Int) : min n m = if n ≤ m then n else m := rfl
|
||||
|
||||
protected theorem max_def (n m : Int) : max n m = if n ≤ m then m else n := rfl
|
||||
|
||||
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
|
||||
rw [Int.min_def, Int.max_def]
|
||||
simp
|
||||
split <;> rename_i h₁ <;> split <;> rename_i h₂
|
||||
· simpa using Int.le_antisymm h₂ h₁
|
||||
· simp
|
||||
· simp
|
||||
· simp only [Int.not_le] at h₁ h₂
|
||||
exfalso
|
||||
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
|
||||
|
||||
@[simp] protected theorem min_add_right (a b c : Int) : min (a + c) (b + c) = min a b + c := by
|
||||
rw [Int.min_def, Int.min_def]
|
||||
simp only [Int.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem min_add_left (a b c : Int) : min (a + b) (a + c) = a + min b c := by
|
||||
rw [Int.min_def, Int.min_def]
|
||||
simp only [Int.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
simp [Int.min_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) min := ⟨Int.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
protected theorem min_le_left (a b : Int) : min a b ≤ a := Int.min_comm .. ▸ Int.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Int} (h : a ≤ b) : min a b = a := by simp [Int.min_def, h]
|
||||
|
||||
protected theorem min_eq_right {a b : Int} (h : b ≤ a) : min a b = b := by
|
||||
rw [Int.min_comm a b]; exact Int.min_eq_left h
|
||||
|
||||
protected theorem le_min {a b c : Int} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.min_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem lt_min {a b c : Int} : a < min b c ↔ a < b ∧ a < c := Int.le_min
|
||||
|
||||
@[simp] protected theorem neg_max_neg (a b : Int) : max (-a) (-b) = -min a b := by
|
||||
rw [Int.min_def, Int.max_def]
|
||||
simp
|
||||
split <;> rename_i h₁ <;> split <;> rename_i h₂
|
||||
· simpa using Int.le_antisymm h₁ h₂
|
||||
· simp
|
||||
· simp
|
||||
· simp only [Int.not_le] at h₁ h₂
|
||||
exfalso
|
||||
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
|
||||
|
||||
@[simp] protected theorem max_add_right (a b c : Int) : max (a + c) (b + c) = max a b + c := by
|
||||
rw [Int.max_def, Int.max_def]
|
||||
simp only [Int.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem max_add_left (a b c : Int) : max (a + b) (a + c) = a + max b c := by
|
||||
rw [Int.max_def, Int.max_def]
|
||||
simp only [Int.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
simp only [Int.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
instance : Std.Commutative (α := Int) max := ⟨Int.max_comm⟩
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
protected theorem le_max_right (a b : Int) : b ≤ max a b := Int.max_comm .. ▸ Int.le_max_left ..
|
||||
|
||||
protected theorem max_eq_right {a b : Int} (h : a ≤ b) : max a b = b := by
|
||||
simp [Int.max_def, h, Int.not_lt.2 h]
|
||||
|
||||
protected theorem max_eq_left {a b : Int} (h : b ≤ a) : max a b = a := by
|
||||
rw [← Int.max_comm b a]; exact Int.max_eq_right h
|
||||
|
||||
protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.max_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_lt {a b c : Int} : max a b < c ↔ a < c ∧ b < c := by
|
||||
simp only [Int.lt_iff_add_one_le]
|
||||
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
|
||||
|
||||
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
|
||||
rw [Int.max_eq_left (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
|
||||
rw [Int.max_eq_right (ofNat_zero_le n)]
|
||||
|
||||
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
|
||||
rw [Int.max_eq_right (negSucc_le_zero _)]
|
||||
|
||||
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
|
||||
rw [Int.max_eq_left (negSucc_le_zero _)]
|
||||
|
||||
@[simp] protected theorem min_self (a : Int) : min a a = a := Int.min_eq_left (Int.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Int) min := ⟨Int.min_self⟩
|
||||
|
||||
@[simp] protected theorem max_self (a : Int) : max a a = a := Int.max_eq_right (Int.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Int) max := ⟨Int.max_self⟩
|
||||
|
||||
/- ### Order properties and multiplication -/
|
||||
|
||||
protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := by
|
||||
let ⟨n, hn⟩ := eq_ofNat_of_zero_le ha
|
||||
@@ -369,7 +450,8 @@ protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a
|
||||
protected theorem mul_pos {a b : Int} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := by
|
||||
let ⟨n, hn⟩ := eq_succ_of_zero_lt ha
|
||||
let ⟨m, hm⟩ := eq_succ_of_zero_lt hb
|
||||
rw [hn, hm, ← ofNat_mul]; apply ofNat_succ_pos
|
||||
rw [hn, hm]
|
||||
apply ofNat_succ_pos
|
||||
|
||||
protected theorem mul_lt_mul_of_pos_left {a b c : Int}
|
||||
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by
|
||||
@@ -425,7 +507,7 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
|
||||
|
||||
/- ## natAbs -/
|
||||
|
||||
@[simp] theorem natAbs_ofNat (n : Nat) : natAbs ↑n = n := rfl
|
||||
@[simp, norm_cast] theorem natAbs_ofNat (n : Nat) : natAbs ↑n = n := rfl
|
||||
@[simp] theorem natAbs_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
|
||||
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
|
||||
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : Nat) := rfl
|
||||
@@ -470,6 +552,13 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
|
||||
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a ≤ 0) : (natAbs a : Int) = -a := by
|
||||
rw [← natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
|
||||
|
||||
theorem natAbs_sub_of_nonneg_of_le {a b : Int} (h₁ : 0 ≤ b) (h₂ : b ≤ a) :
|
||||
(a - b).natAbs = a.natAbs - b.natAbs := by
|
||||
rw [← Int.ofNat_inj]
|
||||
rw [natAbs_of_nonneg, ofNat_sub, natAbs_of_nonneg (Int.le_trans h₁ h₂), natAbs_of_nonneg h₁]
|
||||
· rwa [← Int.ofNat_le, natAbs_of_nonneg h₁, natAbs_of_nonneg (Int.le_trans h₁ h₂)]
|
||||
· exact Int.sub_nonneg_of_le h₂
|
||||
|
||||
/-! ### toNat -/
|
||||
|
||||
theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
|
||||
@@ -492,8 +581,8 @@ theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
|
||||
|
||||
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
|
||||
match a with
|
||||
| Int.ofNat n => simp
|
||||
| Int.negSucc n => simp
|
||||
| (n : Nat) => simp
|
||||
| -(n + 1 : Nat) => norm_cast
|
||||
|
||||
theorem self_le_toNat (a : Int) : a ≤ toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
|
||||
|
||||
@@ -529,12 +618,15 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
| 0 => rfl
|
||||
| _+1 => rfl
|
||||
|
||||
/-! ### toNat' -/
|
||||
/-! ### toNat? -/
|
||||
|
||||
theorem mem_toNat' : ∀ {a : Int} {n : Nat}, toNat' a = some n ↔ a = n
|
||||
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
|
||||
theorem mem_toNat? : ∀ {a : Int} {n : Nat}, toNat? a = some n ↔ a = n
|
||||
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj]
|
||||
| -[m+1], n => by constructor <;> nofun
|
||||
|
||||
@[deprecated mem_toNat? (since := "2025-03-11")]
|
||||
abbrev mem_toNat' := @mem_toNat?
|
||||
|
||||
/-! ## Order properties of the integers -/
|
||||
|
||||
protected theorem le_of_not_le {a b : Int} : ¬ a ≤ b → b ≤ a := (Int.le_total a b).resolve_left
|
||||
@@ -554,10 +646,10 @@ protected theorem lt_of_add_lt_add_left {a b c : Int} (h : a + b < a + c) : b <
|
||||
protected theorem lt_of_add_lt_add_right {a b c : Int} (h : a + b < c + b) : a < c :=
|
||||
Int.lt_of_add_lt_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
|
||||
|
||||
protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c ↔ b < c :=
|
||||
@[simp] protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c ↔ b < c :=
|
||||
⟨Int.lt_of_add_lt_add_left, (Int.add_lt_add_left · _)⟩
|
||||
|
||||
protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c ↔ a < b :=
|
||||
@[simp] protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c ↔ a < b :=
|
||||
⟨Int.lt_of_add_lt_add_right, (Int.add_lt_add_right · _)⟩
|
||||
|
||||
protected theorem add_lt_add {a b c d : Int} (h₁ : a < b) (h₂ : c < d) : a + c < b + d :=
|
||||
@@ -752,6 +844,18 @@ protected theorem sub_le_sub_right {a b : Int} (h : a ≤ b) (c : Int) : a - c
|
||||
protected theorem sub_le_sub {a b c d : Int} (hab : a ≤ b) (hcd : c ≤ d) : a - d ≤ b - c :=
|
||||
Int.add_le_add hab (Int.neg_le_neg hcd)
|
||||
|
||||
protected theorem le_of_sub_le_sub_left {a b c : Int} (h : c - a ≤ c - b) : b ≤ a :=
|
||||
Int.le_of_neg_le_neg <| Int.le_of_add_le_add_left h
|
||||
|
||||
protected theorem le_of_sub_le_sub_right {a b c : Int} (h : a - c ≤ b - c) : a ≤ b :=
|
||||
Int.le_of_add_le_add_right h
|
||||
|
||||
@[simp] protected theorem sub_le_sub_left_iff {a b c : Int} : c - a ≤ c - b ↔ b ≤ a :=
|
||||
⟨Int.le_of_sub_le_sub_left, (Int.sub_le_sub_left · c)⟩
|
||||
|
||||
@[simp] protected theorem sub_le_sub_right_iff {a b c : Int} : a - c ≤ b - c ↔ a ≤ b :=
|
||||
⟨Int.le_of_sub_le_sub_right, (Int.sub_le_sub_right · c)⟩
|
||||
|
||||
protected theorem add_lt_of_lt_neg_add {a b c : Int} (h : b < -a + c) : a + b < c := by
|
||||
have h := Int.add_lt_add_left h a
|
||||
rwa [Int.add_neg_cancel_left] at h
|
||||
@@ -860,11 +964,11 @@ protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a <
|
||||
⟨Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)⟩
|
||||
|
||||
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
|
||||
(hab : a ≤ b) (hcd : c < d) : a - d < b - c :=
|
||||
(hab : a ≤ b) (hcd : c < d) : a - d < b - c :=
|
||||
Int.add_lt_add_of_le_of_lt hab (Int.neg_lt_neg hcd)
|
||||
|
||||
protected theorem sub_lt_sub_of_lt_of_le {a b c d : Int}
|
||||
(hab : a < b) (hcd : c ≤ d) : a - d < b - c :=
|
||||
(hab : a < b) (hcd : c ≤ d) : a - d < b - c :=
|
||||
Int.add_lt_add_of_lt_of_le hab (Int.neg_le_neg hcd)
|
||||
|
||||
protected theorem add_le_add_three {a b c d e f : Int}
|
||||
@@ -938,6 +1042,22 @@ protected theorem mul_self_le_mul_self {a b : Int} (h1 : 0 ≤ a) (h2 : a ≤ b)
|
||||
protected theorem mul_self_lt_mul_self {a b : Int} (h1 : 0 ≤ a) (h2 : a < b) : a * a < b * b :=
|
||||
Int.mul_lt_mul' (Int.le_of_lt h2) h2 h1 (Int.lt_of_le_of_lt h1 h2)
|
||||
|
||||
protected theorem nonneg_of_mul_nonneg_left {a b : Int}
|
||||
(h : 0 ≤ a * b) (hb : 0 < b) : 0 ≤ a :=
|
||||
Int.le_of_not_gt fun ha => Int.not_le_of_gt (Int.mul_neg_of_neg_of_pos ha hb) h
|
||||
|
||||
protected theorem nonneg_of_mul_nonneg_right {a b : Int}
|
||||
(h : 0 ≤ a * b) (ha : 0 < a) : 0 ≤ b :=
|
||||
Int.le_of_not_gt fun hb => Int.not_le_of_gt (Int.mul_neg_of_pos_of_neg ha hb) h
|
||||
|
||||
protected theorem nonpos_of_mul_nonpos_left {a b : Int}
|
||||
(h : a * b ≤ 0) (hb : 0 < b) : a ≤ 0 :=
|
||||
Int.le_of_not_gt fun ha : a > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
|
||||
|
||||
protected theorem nonpos_of_mul_nonpos_right {a b : Int}
|
||||
(h : a * b ≤ 0) (ha : 0 < a) : b ≤ 0 :=
|
||||
Int.le_of_not_gt fun hb : b > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
|
||||
|
||||
/- ## sign -/
|
||||
|
||||
@[simp] theorem sign_zero : sign 0 = 0 := rfl
|
||||
@@ -988,10 +1108,10 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
|
||||
| 0, h => nomatch h
|
||||
| -[_+1], _ => negSucc_lt_zero _
|
||||
|
||||
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 ↔ 0 < a :=
|
||||
@[simp] theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 ↔ 0 < a :=
|
||||
⟨pos_of_sign_eq_one, sign_eq_one_of_pos⟩
|
||||
|
||||
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
@[simp] theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
⟨neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg⟩
|
||||
|
||||
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 ↔ a = 0 :=
|
||||
@@ -1003,7 +1123,7 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
| .ofNat (_ + 1) => rfl
|
||||
| .negSucc _ => rfl
|
||||
|
||||
@[simp] theorem sign_nonneg : 0 ≤ sign x ↔ 0 ≤ x := by
|
||||
@[simp] theorem sign_nonneg_iff : 0 ≤ sign x ↔ 0 ≤ x := by
|
||||
match x with
|
||||
| 0 => rfl
|
||||
| .ofNat (_ + 1) =>
|
||||
@@ -1011,6 +1131,26 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
exact Int.le_add_one (ofNat_nonneg _)
|
||||
| .negSucc _ => simp +decide [sign]
|
||||
|
||||
@[deprecated sign_nonneg_iff (since := "2025-03-11")] abbrev sign_nonneg := @sign_nonneg_iff
|
||||
|
||||
@[simp] theorem sign_pos_iff : 0 < sign x ↔ 0 < x := by
|
||||
match x with
|
||||
| 0
|
||||
| .ofNat (_ + 1) => simp
|
||||
| .negSucc x => simp
|
||||
|
||||
@[simp] theorem sign_nonpos_iff : sign x ≤ 0 ↔ x ≤ 0 := by
|
||||
match x with
|
||||
| 0 => rfl
|
||||
| .ofNat (_ + 1) => simp
|
||||
| .negSucc _ => simpa using negSucc_le_zero _
|
||||
|
||||
@[simp] theorem sign_neg_iff : sign x < 0 ↔ x < 0 := by
|
||||
match x with
|
||||
| 0 => simp
|
||||
| .ofNat (_ + 1) => simpa using le.intro_sub _ rfl
|
||||
| .negSucc _ => simp
|
||||
|
||||
@[simp] theorem mul_sign_self : ∀ i : Int, i * sign i = natAbs i
|
||||
| succ _ => Int.mul_one _
|
||||
| 0 => Int.mul_zero _
|
||||
@@ -1021,6 +1161,12 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
|
||||
@[simp] theorem sign_mul_self : sign i * i = natAbs i := by
|
||||
rw [Int.mul_comm, mul_sign_self]
|
||||
|
||||
theorem sign_trichotomy (a : Int) : sign a = 1 ∨ sign a = 0 ∨ sign a = -1 := by
|
||||
match a with
|
||||
| 0 => simp
|
||||
| .ofNat (_ + 1) => simp
|
||||
| .negSucc _ => simp
|
||||
|
||||
/- ## natAbs -/
|
||||
|
||||
theorem natAbs_ne_zero {a : Int} : a.natAbs ≠ 0 ↔ a ≠ 0 := not_congr Int.natAbs_eq_zero
|
||||
@@ -1029,7 +1175,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
theorem eq_nat_or_neg (a : Int) : ∃ n : Nat, a = n ∨ a = -↑n := ⟨_, natAbs_eq a⟩
|
||||
protected theorem eq_nat_or_neg (a : Int) : ∃ n : Nat, a = n ∨ a = -↑n := ⟨_, natAbs_eq a⟩
|
||||
|
||||
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
|
||||
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [← natAbs_mul, h, natAbs.eq_def]
|
||||
@@ -1043,7 +1189,7 @@ theorem natAbs_eq_iff {a : Int} {n : Nat} : a.natAbs = n ↔ a = n ∨ a = -↑n
|
||||
theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
|
||||
suffices ∀ a b : Nat, natAbs (subNatNat a b.succ) ≤ (a + b).succ by
|
||||
match a, b with
|
||||
| (a:Nat), (b:Nat) => rw [ofNat_add_ofNat, natAbs_ofNat]; apply Nat.le_refl
|
||||
| (a:Nat), (b:Nat) => rw [← ofNat_add, natAbs_ofNat]; apply Nat.le_refl
|
||||
| (a:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
|
||||
| -[a+1], (b:Nat) =>
|
||||
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this
|
||||
@@ -1062,6 +1208,7 @@ theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
|
||||
theorem natAbs_sub_le (a b : Int) : natAbs (a - b) ≤ natAbs a + natAbs b := by
|
||||
rw [← Int.natAbs_neg b]; apply natAbs_add_le
|
||||
|
||||
@[deprecated negSucc_eq (since := "2025-03-11")]
|
||||
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl
|
||||
|
||||
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
|
||||
@@ -1069,7 +1216,10 @@ theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
|
||||
match a, b, eq_ofNat_of_zero_le w₁, eq_ofNat_of_zero_le (Int.le_trans w₁ (Int.le_of_lt w₂)) with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => ofNat_lt.1 w₂
|
||||
|
||||
theorem eq_natAbs_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
|
||||
theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
|
||||
rw [natAbs_eq_iff, Int.mul_eq_zero, ← Int.sub_neg, Int.sub_eq_zero, Int.sub_eq_zero]
|
||||
|
||||
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
|
||||
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
|
||||
|
||||
end Int
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Int
|
||||
|
||||
/-! # pow -/
|
||||
|
||||
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
|
||||
@[simp] protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
|
||||
|
||||
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
|
||||
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
|
||||
@@ -27,11 +27,11 @@ abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
|
||||
abbrev pos_pow_of_pos := @Nat.pow_pos
|
||||
|
||||
@[norm_cast]
|
||||
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
|
||||
match n with
|
||||
| 0 => rfl
|
||||
| n + 1 =>
|
||||
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
|
||||
simp only [Nat.pow_succ, Int.pow_succ, Int.natCast_mul, Int.natCast_pow _ n]
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :
|
||||
|
||||
@@ -13,29 +13,44 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace List
|
||||
|
||||
/-- `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`. -/
|
||||
/--
|
||||
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
|
||||
a list `l : List α`, given a proof that every element of `l` in fact satisfies `P`.
|
||||
|
||||
`O(|l|)`. `List.pmap`, named for “partial map,” is the equivalent of `List.map` for such partial
|
||||
functions.
|
||||
-/
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) : ∀ l : List α, (H : ∀ a ∈ l, P a) → List β
|
||||
| [], _ => []
|
||||
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
Unsafe implementation of `attachWith` that takes advantage of the fact that the representation of
|
||||
`List {x // P x}` is the same as the input `List α`.
|
||||
(Someday, the compiler might do this optimization automatically, but until then...)
|
||||
-/
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(l : List α) (P : α → Prop) (_ : ∀ x ∈ l, P x) : List {x // P x} := unsafeCast l
|
||||
|
||||
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
/--
|
||||
“Attaches” individual proofs to a list of values that satisfy a predicate `P`, returning a list of
|
||||
elements in the corresponding subtype `{ x // P x }`.
|
||||
|
||||
`O(1)`.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(l : List α) (P : α → Prop) (H : ∀ x ∈ l, P x) : List {x // P x} := pmap Subtype.mk l H
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
|
||||
with the same elements but in the type `{x // x ∈ l}`. -/
|
||||
/--
|
||||
"Attaches" the proof that the elements of `l` are in fact elements of `l`, producing a new list with
|
||||
the same elements but in the subtype `{ x // x ∈ l }`.
|
||||
|
||||
`O(1)`.
|
||||
|
||||
This function is primarily used to allow definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
|
||||
`List.map`) to prove that an value taken from a list is smaller than the list. This allows the
|
||||
well-founded recursion mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[inline] def attach (l : List α) : List {x // x ∈ l} := attachWith l _ fun _ => id
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@@ -650,11 +665,19 @@ 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 an intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
Maps a list of terms in a subtype to the corresponding terms in the type by forgetting that they
|
||||
satisfy the predicate.
|
||||
|
||||
If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
|
||||
This is the inverse of `List.attachWith` and a synonym for `l.map (·.val)`.
|
||||
|
||||
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
|
||||
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
This function is usually inserted automatically by Lean as an intermediate step while proving
|
||||
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
|
||||
the elaboration of definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
|
||||
state, the right approach is usually the tactic `simp [List.unattach, -List.map_subtype]`.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α := l.map (·.val)
|
||||
|
||||
@@ -662,6 +685,10 @@ def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α :
|
||||
@[simp] theorem unattach_cons {p : α → Prop} {a : { x // p x }} {l : List { x // p x }} :
|
||||
(a :: l).unattach = a.val :: l.unattach := rfl
|
||||
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {l : List { x // p x }} {a} :
|
||||
a ∈ l.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ l := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem length_unattach {p : α → Prop} {l : List { x // p x }} :
|
||||
l.unattach.length = l.length := by
|
||||
unfold unattach
|
||||
@@ -766,6 +793,16 @@ and simplifies these to the function directly taking the value.
|
||||
simp [hf, find?_cons]
|
||||
split <;> simp [ih]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.all f = l.unattach.all g := by
|
||||
simp [all_eq, hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {l : List { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
l.any f = l.unattach.any g := by
|
||||
simp [any_eq, hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : List { x // p x }}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,10 +79,16 @@ theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a
|
||||
/-! ### getD -/
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
Returns the element at the provided index, counting from `0`. Returns `fallback` if the index is out
|
||||
of bounds.
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
|
||||
See also `get?` and `get!`.
|
||||
To return an `Option` depending on whether the index is in bounds, use `as[i]?`. To panic if the
|
||||
index is out of bounds, use `as[i]!`.
|
||||
|
||||
Examples:
|
||||
* `["spring", "summer", "fall", "winter"].getD 2 "never" = "fall"`
|
||||
* `["spring", "summer", "fall", "winter"].getD 0 "never" = "spring"`
|
||||
* `["spring", "summer", "fall", "winter"].getD 4 "never" = "never"`
|
||||
-/
|
||||
def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
as[i]?.getD fallback
|
||||
@@ -92,10 +98,16 @@ def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
/-! ### getLast! -/
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
Returns the last element in the list. Panics and returns `default` if the list is empty.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `getLast` and `getLastD` for safer alternatives.
|
||||
Safer alternatives include:
|
||||
* `getLast?`, which returns an `Option`,
|
||||
* `getLastD`, which takes a fallback value for empty lists, and
|
||||
* `getLast`, which requires a proof that the list is non-empty.
|
||||
|
||||
Examples:
|
||||
* `["circle", "rectangle"].getLast! = "rectangle"`
|
||||
* `["circle"].getLast! = "circle"`
|
||||
-/
|
||||
def getLast! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
@@ -106,10 +118,12 @@ def getLast! [Inhabited α] : List α → α
|
||||
/-! ### head! -/
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
Returns the first element in the list. If the list is empty, panics and returns `default`.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `head` and `headD` for safer alternatives.
|
||||
Safer alternatives include:
|
||||
* `List.head`, which requires a proof that the list is non-empty,
|
||||
* `List.head?`, which returns an `Option`, and
|
||||
* `List.headD`, which returns an explicitly-provided fallback value on empty lists.
|
||||
-/
|
||||
def head! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
@@ -118,10 +132,17 @@ def head! [Inhabited α] : List α → α
|
||||
/-! ### tail! -/
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
Drops the first element of a nonempty list, returning the tail. If the list is empty, this function
|
||||
panics when executed and returns the empty list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns the empty list.
|
||||
See `tail` and `tailD` for safer alternatives.
|
||||
Safer alternatives include
|
||||
* `tail`, which returns the empty list without panicking,
|
||||
* `tail?`, which returns an `Option`, and
|
||||
* `tailD`, which returns a fallback value when passed the empty list.
|
||||
|
||||
Examples:
|
||||
* `["apple", "banana", "grape"].tail! = ["banana", "grape"]`
|
||||
* `["banana", "grape"].tail! = ["grape"]`
|
||||
-/
|
||||
def tail! : List α → List α
|
||||
| [] => panic! "empty list"
|
||||
@@ -132,17 +153,30 @@ def tail! : List α → List α
|
||||
/-! ### partitionM -/
|
||||
|
||||
/--
|
||||
Monadic generalization of `List.partition`.
|
||||
Returns a pair of lists that together contain all the elements of `as`. The first list contains
|
||||
those elements for which the monadic predicate `p` returns `true`, and the second contains those for
|
||||
which `p` returns `false`. The list's elements are examined in order, from left to right.
|
||||
|
||||
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
|
||||
```
|
||||
This is a monadic version of `List.partition`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
def posOrNeg (x : Int) : Except String Bool :=
|
||||
if x > 0 then pure true
|
||||
else if x < 0 then pure false
|
||||
else throw "Zero is not positive or negative"
|
||||
|
||||
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
|
||||
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
|
||||
```
|
||||
```lean example
|
||||
#eval [-1, 2, 3].partitionM posOrNeg
|
||||
```
|
||||
```output
|
||||
Except.ok ([2, 3], [-1])
|
||||
```
|
||||
```lean example
|
||||
#eval [0, 2, 3].partitionM posOrNeg
|
||||
```
|
||||
```output
|
||||
Except.error "Zero is not positive or negative"
|
||||
```
|
||||
-/
|
||||
@[inline] def partitionM [Monad m] (p : α → m Bool) (l : List α) : m (List α × List α) :=
|
||||
@@ -162,12 +196,12 @@ where
|
||||
/-! ### partitionMap -/
|
||||
|
||||
/--
|
||||
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
|
||||
whilst partitioning the result into a pair of lists, `List β × List γ`,
|
||||
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
|
||||
```
|
||||
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
|
||||
```
|
||||
Applies a function that returns a disjoint union to each element of a list, collecting the `Sum.inl`
|
||||
and `Sum.inr` results into separate lists.
|
||||
|
||||
Examples:
|
||||
* `[0, 1, 2, 3].partitionMap (fun x => if x % 2 = 0 then .inl x else .inr x) = ([0, 2], [1, 3])`
|
||||
* `[0, 1, 2, 3].partitionMap (fun x => if x = 0 then .inl x else .inr x) = ([0], [1, 2, 3])`
|
||||
-/
|
||||
@[inline] def partitionMap (f : α → β ⊕ γ) (l : List α) : List β × List γ := go l #[] #[] where
|
||||
/-- Auxiliary for `partitionMap`:
|
||||
@@ -199,19 +233,30 @@ For verification purposes, `List.mapMono = List.map`.
|
||||
return b' :: bs'
|
||||
|
||||
/--
|
||||
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
|
||||
if the result of each `f a` is a pointer equal value `a`.
|
||||
Applies a monadic function to each element of a list, returning the list of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new list if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α → m α) : m (List α) :=
|
||||
match as with
|
||||
| [] => return []
|
||||
| a :: as => return (← f a) :: (← mapMonoM as f)
|
||||
|
||||
/--
|
||||
Applies a function to each element of a list, returning the list of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new list if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
|
||||
For verification purposes, `List.mapMono = List.map`.
|
||||
-/
|
||||
def mapMono (as : List α) (f : α → α) : List α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
/-! ## Additional lemmas required for bootstrapping `Array`. -/
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
|
||||
(as ++ bs)[i] = as[i] := by
|
||||
induction as generalizing i with
|
||||
@@ -221,6 +266,7 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as
|
||||
| zero => rfl
|
||||
| succ i => apply ih
|
||||
|
||||
@[simp]
|
||||
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i) {h₂} :
|
||||
(as ++ bs)[i]'h₂ =
|
||||
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
|
||||
|
||||
@@ -49,8 +49,9 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
See `List.forM` for the variant that discards the results.
|
||||
See `List.mapA` for the variant that works with `Applicative`.
|
||||
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
|
||||
more convenient to reason about. `List.forM` is the variant that discards the results and
|
||||
`List.mapA` is the variant that works with `Applicative`.
|
||||
-/
|
||||
@[inline]
|
||||
def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) (as : List α) : m (List β) :=
|
||||
@@ -60,15 +61,15 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list
|
||||
of results.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
|
||||
See `List.forA` for the variant that discards the results.
|
||||
See `List.mapM` for the variant that works with `Monad`.
|
||||
See `List.forA` for the variant that discards the results. See `List.mapM` for the variant that
|
||||
works with `Monad`.
|
||||
|
||||
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
|
||||
This function is not tail-recursive, so it may fail with a stack overflow on long lists.
|
||||
-/
|
||||
@[specialize]
|
||||
def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m (List β)
|
||||
@@ -76,10 +77,10 @@ def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f
|
||||
| a::as => List.cons <$> f a <*> mapA f as
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right.
|
||||
Applies the monadic action `f` to every element in the list, in order.
|
||||
|
||||
See `List.mapM` for the variant that collects results.
|
||||
See `List.forA` for the variant that works with `Applicative`.
|
||||
`List.mapM` is a variant that collects results. `List.forA` is a variant that works on any
|
||||
`Applicative`.
|
||||
-/
|
||||
@[specialize]
|
||||
protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
@@ -88,12 +89,11 @@ protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α
|
||||
| a :: as => do f a; List.forM as f
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right.
|
||||
Applies the applicative action `f` to every element in the list, in order.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
|
||||
If `m` is also a `Monad`, then using `List.forM` can be more efficient.
|
||||
|
||||
See `List.mapA` for the variant that collects results.
|
||||
See `List.forM` for the variant that works with `Monad`.
|
||||
`List.mapA` is a variant that collects results.
|
||||
-/
|
||||
@[specialize]
|
||||
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
@@ -110,8 +110,28 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
|
||||
filterAuxM f t (cond b (h :: acc) acc)
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
Applies the monadic predicate `p` to every element in the list, in order from left to right, and
|
||||
returns the list of elements for which `p` returns `true`.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterM fun x => do
|
||||
IO.println s!"Checking {x}"
|
||||
return x < 3
|
||||
```
|
||||
```output
|
||||
Checking 1
|
||||
Checking 2
|
||||
Checking 5
|
||||
Checking 2
|
||||
Checking 7
|
||||
Checking 7
|
||||
```
|
||||
```output
|
||||
[1, 2, 2]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) := do
|
||||
@@ -119,16 +139,56 @@ def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as
|
||||
pure as.reverse
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
Applies the monadic predicate `p` on every element in the list in reverse order, from right to left,
|
||||
and returns those elements for which `p` returns `true`. The elements of the returned list are in
|
||||
the same order as in the input list.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterRevM fun x => do
|
||||
IO.println s!"Checking {x}"
|
||||
return x < 3
|
||||
```
|
||||
```output
|
||||
Checking 7
|
||||
Checking 7
|
||||
Checking 2
|
||||
Checking 5
|
||||
Checking 2
|
||||
Checking 1
|
||||
```
|
||||
```output
|
||||
[1, 2, 2]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM p as.reverse []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
|
||||
results `y` for which `f x` returns `some y`.
|
||||
Applies a monadic function that returns an `Option` to each element of a list, collecting the
|
||||
non-`none` values.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterMapM fun x => do
|
||||
IO.println s!"Examining {x}"
|
||||
if x > 2 then return some (2 * x)
|
||||
else return none
|
||||
```
|
||||
```output
|
||||
Examining 1
|
||||
Examining 2
|
||||
Examining 5
|
||||
Examining 2
|
||||
Examining 7
|
||||
Examining 7
|
||||
```
|
||||
```output
|
||||
[10, 14, 14]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
|
||||
@@ -141,8 +201,8 @@ def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns the
|
||||
concatenation of the results.
|
||||
Applies a monadic function that returns a list to each element of a list, from left to right, and
|
||||
concatenates the resulting lists.
|
||||
-/
|
||||
@[inline]
|
||||
def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (List β)) (as : List α) : m (List β) :=
|
||||
@@ -153,14 +213,20 @@ def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f :
|
||||
loop as (bs' :: bs)
|
||||
loop as []
|
||||
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
```
|
||||
foldlM f x₀ [a, b, c] = do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃
|
||||
Folds a monadic function over a list from the left, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in order, using `f`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example [Monad m] (f : α → β → m α) :
|
||||
List.foldlM (m := m) f x₀ [a, b, c] = (do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃)
|
||||
:= by rfl
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
@@ -176,13 +242,18 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
|
||||
simp [List.foldlM]
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from right to left:
|
||||
```
|
||||
foldrM f x₀ [a, b, c] = do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃
|
||||
Folds a monadic function over a list from the right, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in order, using `f`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example [Monad m] (f : α → β → m β) :
|
||||
List.foldrM (m := m) f x₀ [a, b, c] = (do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃)
|
||||
:= by rfl
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
@@ -192,32 +263,70 @@ def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α
|
||||
@[simp] theorem foldrM_nil [Monad m] (f : α → β → m β) (b) : [].foldrM f b = pure b := rfl
|
||||
|
||||
/--
|
||||
Maps `f` over the list and collects the results with `<|>`.
|
||||
```
|
||||
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
|
||||
```
|
||||
Maps `f` over the list and collects the results with `<|>`. The result for the end of the list is
|
||||
`failure`.
|
||||
|
||||
Examples:
|
||||
* `[[], [1, 2], [], [2]].firstM List.head? = some 1`
|
||||
* `[[], [], []].firstM List.head? = none`
|
||||
* `[].firstM List.head? = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def firstM {m : Type u → Type v} [Alternative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m β
|
||||
| [] => failure
|
||||
| a::as => f a <|> firstM f as
|
||||
|
||||
/--
|
||||
Returns true if the monadic predicate `p` returns `true` for any element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `true`. The elements in `l` are examined in
|
||||
order from left to right.
|
||||
-/
|
||||
@[specialize]
|
||||
def anyM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
|
||||
def anyM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
|
||||
| [] => pure false
|
||||
| a::as => do
|
||||
match (← f a) with
|
||||
match (← p a) with
|
||||
| true => pure true
|
||||
| false => anyM f as
|
||||
| false => anyM p as
|
||||
|
||||
/--
|
||||
Returns true if the monadic predicate `p` returns `true` for every element of `l`.
|
||||
|
||||
`O(|l|)`. Short-circuits upon encountering the first `false`. The elements in `l` are examined in
|
||||
order from left to right.
|
||||
-/
|
||||
@[specialize]
|
||||
def allM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
|
||||
def allM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
|
||||
| [] => pure true
|
||||
| a::as => do
|
||||
match (← f a) with
|
||||
| true => allM f as
|
||||
match (← p a) with
|
||||
| true => allM p as
|
||||
| false => pure false
|
||||
|
||||
/--
|
||||
Returns the first element of the list for which the monadic predicate `p` returns `true`, or `none`
|
||||
if no such element is found. Elements of the list are checked in order.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [7, 6, 5, 8, 1, 2, 6].findM? fun i => do
|
||||
if i < 5 then
|
||||
return true
|
||||
if i ≤ 6 then
|
||||
IO.println s!"Almost! {i}"
|
||||
return false
|
||||
```
|
||||
```output
|
||||
Almost! 6
|
||||
Almost! 5
|
||||
```
|
||||
```output
|
||||
some 1
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : List α → m (Option α)
|
||||
| [] => pure none
|
||||
@@ -227,15 +336,43 @@ def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : Lis
|
||||
| false => findM? p as
|
||||
|
||||
@[simp]
|
||||
theorem findM?_id (p : α → Bool) (as : List α) : findM? (m := Id) p as = as.find? p := by
|
||||
theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α → Bool) (as : List α) :
|
||||
findM? (m := m) (pure <| p ·) as = pure (as.find? p) := by
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons a as ih =>
|
||||
simp only [findM?, find?]
|
||||
cases p a with
|
||||
| true => rfl
|
||||
| false => rw [ih]; rfl
|
||||
| true => simp
|
||||
| false => simp [ih]
|
||||
|
||||
@[simp]
|
||||
theorem findM?_id (p : α → Bool) (as : List α) : findM? (m := Id) p as = as.find? p :=
|
||||
findM?_pure _ _
|
||||
|
||||
/--
|
||||
Returns the first non-`none` result of applying the monadic function `f` to each element of the
|
||||
list, in order. Returns `none` if `f` returns `none` for all elements.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [7, 6, 5, 8, 1, 2, 6].findSomeM? fun i => do
|
||||
if i < 5 then
|
||||
return some (i * 10)
|
||||
if i ≤ 6 then
|
||||
IO.println s!"Almost! {i}"
|
||||
return none
|
||||
```
|
||||
```output
|
||||
Almost! 6
|
||||
Almost! 5
|
||||
```
|
||||
```output
|
||||
some 10
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) : List α → m (Option β)
|
||||
| [] => pure none
|
||||
@@ -245,14 +382,19 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
|
||||
| none => findSomeM? f as
|
||||
|
||||
@[simp]
|
||||
theorem findSomeM?_id (f : α → Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f := by
|
||||
theorem findSomeM?_pure [Monad m] [LawfulMonad m] (f : α → Option β) (as : List α) :
|
||||
findSomeM? (m := m) (pure <| f ·) as = pure (as.findSome? f) := by
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons a as ih =>
|
||||
simp only [findSomeM?, findSome?]
|
||||
cases f a with
|
||||
| some b => rfl
|
||||
| none => rw [ih]; rfl
|
||||
| some b => simp
|
||||
| none => simp [ih]
|
||||
|
||||
@[simp]
|
||||
theorem findSomeM?_id (f : α → Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f :=
|
||||
findSomeM?_pure _ _
|
||||
|
||||
theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] (p : α → m Bool) (as : List α) :
|
||||
as.findM? p = as.findSomeM? fun a => return if (← p a) then some a else none := by
|
||||
|
||||
@@ -11,7 +11,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace List
|
||||
|
||||
/-- `finRange n` lists all elements of `Fin n` in order -/
|
||||
/--
|
||||
Lists all elements of `Fin n` in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `List.finRange 0 = ([] : List Fin 0)`
|
||||
* `List.finRange 2 = ([0, 1] : List Fin 2)`
|
||||
-/
|
||||
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
|
||||
|
||||
@[simp] theorem length_finRange (n) : (List.finRange n).length = n := by
|
||||
|
||||
@@ -17,7 +17,8 @@ then at runtime you will get non-tail recursive versions of the following defini
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace List
|
||||
|
||||
@@ -49,7 +50,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### set -/
|
||||
|
||||
/-- Tail recursive version of `List.set`. -/
|
||||
/--
|
||||
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
|
||||
the list is returned unmodified.
|
||||
|
||||
This is a tail-recursive version of `List.set` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
|
||||
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
|
||||
-/
|
||||
@[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`,
|
||||
unless `n ≥ l.length` in which case it returns `l` -/
|
||||
@@ -69,7 +79,22 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### filterMap -/
|
||||
|
||||
/-- Tail recursive version of `filterMap`. -/
|
||||
|
||||
/--
|
||||
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
|
||||
values.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.filterMap`, used at runtime.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval [1, 2, 5, 2, 7, 7].filterMapTR fun x =>
|
||||
if x > 2 then some (2 * x) else none
|
||||
```
|
||||
```output
|
||||
[10, 14, 14]
|
||||
```
|
||||
-/
|
||||
@[inline] def filterMapTR (f : α → Option β) (l : List α) : List β := go l #[] where
|
||||
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
|
||||
@[specialize] go : List α → Array β → List β
|
||||
@@ -90,7 +115,17 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### foldr -/
|
||||
|
||||
/-- Tail recursive version of `List.foldr`. -/
|
||||
/--
|
||||
Folds a function over a list from the right, accumulating a value starting with `init`. The
|
||||
accumulated value is combined with the each element of the list in reverse order, using `f`.
|
||||
|
||||
`O(|l|)`. This is the tail-recursive replacement for `List.foldr` in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].foldrTR f init = f a (f b (f c init))`
|
||||
* `[1, 2, 3].foldrTR (toString · ++ ·) "" = "123"`
|
||||
* `[1, 2, 3].foldrTR (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
|
||||
-/
|
||||
@[specialize] def foldrTR (f : α → β → β) (init : β) (l : List α) : β := l.toArray.foldr f init
|
||||
|
||||
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
|
||||
@@ -98,7 +133,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### flatMap -/
|
||||
|
||||
/-- Tail recursive version of `List.flatMap`. -/
|
||||
/--
|
||||
Applies a function that returns a list to each element of a list, and concatenates the resulting
|
||||
lists.
|
||||
|
||||
This is the tail-recursive version of `List.flatMap` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[2, 3, 2].flatMapTR List.range = [0, 1, 0, 1, 2, 0, 1]`
|
||||
* `["red", "blue"].flatMapTR String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
|
||||
-/
|
||||
@[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 β
|
||||
@@ -114,7 +158,15 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### flatten -/
|
||||
|
||||
/-- Tail recursive version of `List.flatten`. -/
|
||||
/--
|
||||
Concatenates a list of lists into a single list, preserving the order of the elements.
|
||||
|
||||
`O(|flatten L|)`. This is a tail-recursive version of `List.flatten`, used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[["a"], ["b", "c"]].flattenTR = ["a", "b", "c"]`
|
||||
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flattenTR = ["a", "b", "c", "d", "e", "f"]`
|
||||
-/
|
||||
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
|
||||
|
||||
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
|
||||
@@ -124,7 +176,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
/-- Tail recursive version of `List.take`. -/
|
||||
/--
|
||||
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.
|
||||
|
||||
`O(min n |xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c, d, e].takeTR 0 = []`
|
||||
* `[a, b, c, d, e].takeTR 3 = [a, b, c]`
|
||||
* `[a, b, c, d, e].takeTR 6 = [a, b, c, d, e]`
|
||||
-/
|
||||
@[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`,
|
||||
unless `n ≥ xs.length` in which case it returns `l`. -/
|
||||
@@ -146,7 +207,17 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### takeWhile -/
|
||||
|
||||
/-- Tail recursive version of `List.takeWhile`. -/
|
||||
|
||||
/--
|
||||
Returns the longest initial segment of `xs` for which `p` returns true.
|
||||
|
||||
`O(|xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[7, 6, 4, 8].takeWhileTR (· > 5) = [7, 6]`
|
||||
* `[7, 6, 6, 5].takeWhileTR (· > 5) = [7, 6, 6]`
|
||||
* `[7, 6, 6, 8].takeWhileTR (· > 5) = [7, 6, 6, 8]`
|
||||
-/
|
||||
@[inline] def takeWhileTR (p : α → Bool) (l : List α) : List α := go l #[] where
|
||||
/-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`,
|
||||
unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/
|
||||
@@ -169,7 +240,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### dropLast -/
|
||||
|
||||
/-- Tail recursive version of `dropLast`. -/
|
||||
/--
|
||||
Removes the last element of the list, if one exists.
|
||||
|
||||
This is a tail-recursive version of `List.dropLast`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[].dropLastTR = []`
|
||||
* `["tea"].dropLastTR = []`
|
||||
* `["tea", "coffee", "juice"].dropLastTR = ["tea", "coffee"]`
|
||||
-/
|
||||
@[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList
|
||||
|
||||
@[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by
|
||||
@@ -179,7 +259,16 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### replace -/
|
||||
|
||||
/-- Tail recursive version of `List.replace`. -/
|
||||
/--
|
||||
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
|
||||
`a`, then the list is returned unchanged.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.replace` that's used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[1, 4, 2, 3, 3, 7].replaceTR 3 6 = [1, 4, 2, 6, 3, 7]`
|
||||
* `[1, 4, 2, 3, 3, 7].replaceTR 5 6 = [1, 4, 2, 3, 3, 7]`
|
||||
-/
|
||||
@[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where
|
||||
/-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`,
|
||||
unless `b` is not found in `xs` in which case it returns `l`. -/
|
||||
@@ -202,42 +291,76 @@ The following operations are given `@[csimp]` replacements below:
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
/-- Tail-recursive version of `modify`. -/
|
||||
def modifyTR (f : α → α) (n : Nat) (l : List α) : List α := go l n #[] where
|
||||
/-- Auxiliary for `modifyTR`: `modifyTR.go f l n acc = acc.toList ++ modify f n l`. -/
|
||||
/--
|
||||
Replaces the element at the given index, if it exists, with the result of applying `f` to it.
|
||||
|
||||
This is a tail-recursive version of `List.modify`.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].modifyTR (· * 10) 0 = [10, 2, 3]`
|
||||
* `[1, 2, 3].modifyTR (· * 10) 2 = [1, 2, 30]`
|
||||
* `[1, 2, 3].modifyTR (· * 10) 3 = [1, 2, 3]`
|
||||
-/
|
||||
def modifyTR (l : List α) (i : Nat) (f : α → α) : List α := go l i #[] where
|
||||
/-- Auxiliary for `modifyTR`: `modifyTR.go f l i acc = acc.toList ++ modify f i l`. -/
|
||||
go : List α → Nat → Array α → List α
|
||||
| [], _, acc => acc.toList
|
||||
| a :: l, 0, acc => acc.toListAppend (f a :: l)
|
||||
| a :: l, n+1, acc => go l n (acc.push a)
|
||||
| a :: l, i+1, acc => go l i (acc.push a)
|
||||
|
||||
theorem modifyTR_go_eq : ∀ l i, modifyTR.go f l i acc = acc.toList ++ modify f i l
|
||||
| [], n => by cases n <;> simp [modifyTR.go, modify]
|
||||
theorem modifyTR_go_eq : ∀ l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
|
||||
| [], i => by cases i <;> simp [modifyTR.go, modify]
|
||||
| a :: l, 0 => by simp [modifyTR.go, modify]
|
||||
| a :: l, n+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
|
||||
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
|
||||
|
||||
@[csimp] theorem modify_eq_modifyTR : @modify = @modifyTR := by
|
||||
funext α f n l; simp [modifyTR, modifyTR_go_eq]
|
||||
funext α l i f; simp [modifyTR, modifyTR_go_eq]
|
||||
|
||||
/-! ### insertIdx -/
|
||||
|
||||
/-- Tail-recursive version of `insertIdx`. -/
|
||||
@[inline] def insertIdxTR (n : Nat) (a : α) (l : List α) : List α := go n l #[] where
|
||||
/--
|
||||
Inserts an element into a list at the specified index. If the index is greater than the length of
|
||||
the list, then the list is returned unmodified.
|
||||
|
||||
In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.
|
||||
|
||||
This is a tail-recursive version of `List.insertIdx`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `["tues", "thur", "sat"].insertIdxTR 1 "wed" = ["tues", "wed", "thur", "sat"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 2 "wed" = ["tues", "thur", "wed", "sat"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 3 "wed" = ["tues", "thur", "sat", "wed"]`
|
||||
* `["tues", "thur", "sat"].insertIdxTR 4 "wed" = ["tues", "thur", "sat"]`
|
||||
|
||||
-/
|
||||
@[inline] def insertIdxTR (l : List α) (n : Nat) (a : α) : List α := go n l #[] where
|
||||
/-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/
|
||||
go : Nat → List α → Array α → List α
|
||||
| 0, l, acc => acc.toListAppend (a :: l)
|
||||
| _, [], acc => acc.toList
|
||||
| n+1, a :: l, acc => go n l (acc.push a)
|
||||
|
||||
theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx i a l
|
||||
theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
|
||||
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
|
||||
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
|
||||
|
||||
@[csimp] theorem insertIdx_eq_insertIdxTR : @insertIdx = @insertIdxTR := by
|
||||
funext α f n l; simp [insertIdxTR, insertIdxTR_go_eq]
|
||||
funext α l i a; simp [insertIdxTR, insertIdxTR_go_eq]
|
||||
|
||||
/-! ### erase -/
|
||||
|
||||
/-- Tail recursive version of `List.erase`. -/
|
||||
/--
|
||||
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
|
||||
unmodified.
|
||||
|
||||
`O(|l|)`.
|
||||
|
||||
This is a tail-recursive version of `List.erase`, used in runtime code.
|
||||
|
||||
Examples:
|
||||
* `[1, 5, 3, 2, 5].eraseTR 5 = [1, 3, 2, 5]`
|
||||
* `[1, 5, 3, 2, 5].eraseTR 6 = [1, 5, 3, 2, 5]`
|
||||
-/
|
||||
@[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where
|
||||
/-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`,
|
||||
unless `a` is not present in which case it returns `l` -/
|
||||
@@ -257,7 +380,17 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
· rw [IH] <;> simp_all
|
||||
· simp
|
||||
|
||||
/-- Tail-recursive version of `eraseP`. -/
|
||||
/--
|
||||
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
|
||||
the list is returned unchanged.
|
||||
|
||||
This is a tail-recursive version of `eraseP`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· < 2) = [2, 2, 1, 3, 4]`
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 2) = [2, 1, 2, 1, 4]`
|
||||
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 8) = [2, 1, 2, 1, 3, 4]`
|
||||
-/
|
||||
@[inline] def erasePTR (p : α → Bool) (l : List α) : List α := go l #[] where
|
||||
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
|
||||
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
|
||||
@@ -277,7 +410,20 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.eraseIdx`. -/
|
||||
|
||||
/--
|
||||
Removes the element at the specified index. If the index is out of bounds, the list is returned
|
||||
unmodified.
|
||||
|
||||
`O(i)`.
|
||||
|
||||
This is a tail-recursive version of `List.eraseIdx`, used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 0 = [1, 2, 3, 4]`
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 1 = [0, 2, 3, 4]`
|
||||
* `[0, 1, 2, 3, 4].eraseIdxTR 5 = [0, 1, 2, 3, 4]`
|
||||
-/
|
||||
@[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where
|
||||
/-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`,
|
||||
unless `a` is not present in which case it returns `l` -/
|
||||
@@ -303,7 +449,18 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
/-- Tail recursive version of `List.zipWith`. -/
|
||||
/--
|
||||
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
|
||||
list.
|
||||
|
||||
`O(min |xs| |ys|)`. This is a tail-recursive version of `List.zipWith` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[1, 2].zipWithTR (· + ·) [5, 6] = [6, 8]`
|
||||
* `[1, 2, 3].zipWithTR (· + ·) [5, 6, 10] = [6, 8, 13]`
|
||||
* `[].zipWithTR (· + ·) [5, 6] = []`
|
||||
* `[x₁, x₂, x₃].zipWithTR f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
|
||||
-/
|
||||
@[inline] def zipWithTR (f : α → β → γ) (as : List α) (bs : List β) : List γ := go as bs #[] where
|
||||
/-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/
|
||||
go : List α → List β → Array γ → List γ
|
||||
@@ -321,7 +478,16 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
|
||||
|
||||
/-! ### zipIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.zipIdx`. -/
|
||||
|
||||
/--
|
||||
Pairs each element of a list with its index, optionally starting from an index other than `0`.
|
||||
|
||||
`O(|l|)`. This is a tail-recursive version of `List.zipIdx` that's used at runtime.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].zipIdxTR = [(a, 0), (b, 1), (c, 2)]`
|
||||
* `[a, b, c].zipIdxTR 5 = [(a, 5), (b, 6), (c, 7)]`
|
||||
-/
|
||||
def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
|
||||
let as := l.toArray
|
||||
(as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2
|
||||
@@ -363,8 +529,18 @@ theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
|
||||
/-! ### intercalate -/
|
||||
|
||||
set_option linter.listVariables false in
|
||||
/-- Tail recursive version of `List.intercalate`. -/
|
||||
def intercalateTR (sep : List α) : List (List α) → List α
|
||||
/--
|
||||
Alternates the lists in `xs` with the separator `sep`.
|
||||
|
||||
This is a tail-recursive version of `List.intercalate` used at runtime.
|
||||
|
||||
Examples:
|
||||
* `List.intercalateTR sep [] = []`
|
||||
* `List.intercalateTR sep [a] = a`
|
||||
* `List.intercalateTR sep [a, b] = a ++ sep ++ b`
|
||||
* `List.intercalateTR sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
|
||||
-/
|
||||
def intercalateTR (sep : List α) : (xs : List (List α)) → List α
|
||||
| [] => []
|
||||
| [x] => x
|
||||
| x::xs => go sep.toArray x xs #[]
|
||||
|
||||
@@ -2535,6 +2535,14 @@ theorem flatMap_reverse {β} (l : List α) (f : α → List β) : (l.reverse.fla
|
||||
simp only [foldrM]
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem foldlM_pure [Monad m] [LawfulMonad m] (f : β → α → β) (b) (l : List α) :
|
||||
l.foldlM (m := m) (pure <| f · ·) b = pure (l.foldl f b) := by
|
||||
induction l generalizing b <;> simp [*]
|
||||
|
||||
@[simp] theorem foldrM_pure [Monad m] [LawfulMonad m] (f : α → β → β) (b) (l : List α) :
|
||||
l.foldrM (m := m) (pure <| f · ·) b = pure (l.foldr f b) := by
|
||||
induction l generalizing b <;> simp [*]
|
||||
|
||||
theorem foldl_eq_foldlM (f : β → α → β) (b) (l : List α) :
|
||||
l.foldl f b = l.foldlM (m := Id) f b := by
|
||||
induction l generalizing b <;> simp [*, foldl]
|
||||
@@ -2689,12 +2697,20 @@ theorem foldr_hom (f : β₁ → β₂) (g₁ : α → β₁ → β₁) (g₂ :
|
||||
induction l <;> simp [*, H]
|
||||
|
||||
/--
|
||||
Prove a proposition about the result of `List.foldl`,
|
||||
by proving it for the initial data,
|
||||
and the implication that the operation applied to any element of the list preserves the property.
|
||||
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
|
||||
invariant that is true for the initial data and preserved by the operation being folded.
|
||||
|
||||
The motive can take values in `Sort _`, so this may be used to construct data,
|
||||
as well as to prove propositions.
|
||||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||||
well as to prove propositions.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
|
||||
apply List.foldlRecOn
|
||||
. show 0 < 1; trivial
|
||||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
|
||||
intros; omega
|
||||
```
|
||||
-/
|
||||
def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α → β) (b : β) (_ : motive b)
|
||||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op b a)), motive (List.foldl op b l)
|
||||
@@ -2715,12 +2731,20 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α →
|
||||
rfl
|
||||
|
||||
/--
|
||||
Prove a proposition about the result of `List.foldr`,
|
||||
by proving it for the initial data,
|
||||
and the implication that the operation applied to any element of the list preserves the property.
|
||||
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an
|
||||
invariant that is true for the initial data and preserved by the operation being folded.
|
||||
|
||||
The motive can take values in `Sort _`, so this may be used to construct data,
|
||||
as well as to prove propositions.
|
||||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||||
well as to prove propositions.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
|
||||
apply List.foldrRecOn
|
||||
. show 0 < 1; trivial
|
||||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
|
||||
intros; omega
|
||||
```
|
||||
-/
|
||||
def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β → β) (b : β) (_ : motive b)
|
||||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op a b)), motive (List.foldr op b l)
|
||||
|
||||
@@ -19,8 +19,11 @@ namespace List
|
||||
/-! ## Operations using indexes -/
|
||||
|
||||
/--
|
||||
Given a list `as = [a₀, a₁, ...]` and a function `f : (i : Nat) → α → (h : i < as.length) → β`, returns the list
|
||||
`[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
|
||||
Applies a function to each element of the list along with the index at which that element is found,
|
||||
returning the list of results. In addition to the index, the function is also provided with a proof
|
||||
that the index is valid.
|
||||
|
||||
`List.mapIdx` is a variant that does not provide the function with evidence that the index is valid.
|
||||
-/
|
||||
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) → α → (h : i < as.length) → β) : List β :=
|
||||
go as #[] (by simp)
|
||||
@@ -33,8 +36,11 @@ where
|
||||
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
|
||||
`[f 0 a₀, f 1 a₁, ...]`.
|
||||
Applies a function to each element of the list along with the index at which that element is found,
|
||||
returning the list of results.
|
||||
|
||||
`List.mapFinIdx` is a variant that additionally provides the function with a proof that the index
|
||||
is valid.
|
||||
-/
|
||||
@[inline] def mapIdx (f : Nat → α → β) (as : List α) : List β := go as #[] where
|
||||
/-- Auxiliary for `mapIdx`:
|
||||
@@ -44,8 +50,12 @@ Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁,
|
||||
| a :: as, acc => go as (acc.push (f acc.size a))
|
||||
|
||||
/--
|
||||
Given a list `as = [a₀, a₁, ...]` and a monadic function `f : (i : Nat) → α → (h : i < as.length) → m β`,
|
||||
returns the list `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
|
||||
Applies a monadic function to each element of the list along with the index at which that element is
|
||||
found, returning the list of results. In addition to the index, the function is also provided with a
|
||||
proof that the index is valid.
|
||||
|
||||
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
|
||||
valid.
|
||||
-/
|
||||
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) → α → (h : i < as.length) → m β) : m (List β) :=
|
||||
go as #[] (by simp)
|
||||
@@ -58,8 +68,11 @@ where
|
||||
go as (acc.push (← f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
|
||||
|
||||
/--
|
||||
Given a monadic function `f : Nat → α → m β` and `as : List α`, `as = [a₀, a₁, ...]`,
|
||||
returns the list `[f 0 a₀, f 1 a₁, ...]`.
|
||||
Applies a monadic function to each element of the list along with the index at which that element is
|
||||
found, returning the list of results.
|
||||
|
||||
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
|
||||
is valid.
|
||||
-/
|
||||
@[inline] def mapIdxM [Monad m] (f : Nat → α → m β) (as : List α) : m (List β) := go as #[] where
|
||||
/-- Auxiliary for `mapIdxM`:
|
||||
|
||||
@@ -31,10 +31,13 @@ attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
/-- Alternate (non-tail-recursive) form of mapM for proofs.
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
Note that we can not have this as the main definition and replace it using a `@[csimp]` lemma,
|
||||
because they are only equal when `m` is a `LawfulMonad`.
|
||||
This is a non-tail-recursive variant of `List.mapM` that's easier to reason about. It cannot be used
|
||||
as the main definition and replaced by the tail-recursive version because they can only be proved
|
||||
equal when `m` is a `LawfulMonad`.
|
||||
-/
|
||||
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
@@ -56,9 +59,13 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
|
||||
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α → m β) :
|
||||
(a :: l).mapM f = (return (← f a) :: (← l.mapM f)) := by simp [← mapM'_eq_mapM, mapM']
|
||||
|
||||
@[simp] theorem mapM_id {l : List α} {f : α → Id β} : l.mapM f = l.map f := by
|
||||
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] (l : List α) (f : α → β) :
|
||||
l.mapM (m := m) (pure <| f ·) = pure (l.map f) := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem mapM_id {l : List α} {f : α → Id β} : l.mapM f = l.map f :=
|
||||
mapM_pure _ _
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).mapM f = (return (← l₁.mapM f) ++ (← l₂.mapM f)) := by induction l₁ <;> simp [*]
|
||||
|
||||
@@ -323,7 +330,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
simp only [forIn'_eq_foldlM]
|
||||
induction l.attach generalizing init <;> simp_all
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : List α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' l init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
@@ -376,7 +383,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
simp only [forIn_eq_foldlM]
|
||||
induction l generalizing init <;> simp_all
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : List α) (f : α → β → β) (init : β) :
|
||||
forIn l init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (l.foldl (fun b a => f a b) init) := by
|
||||
@@ -395,7 +402,7 @@ theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
forIn (l.map g) init f = forIn l init fun a y => f (g a) y := by
|
||||
induction l generalizing init <;> simp_all
|
||||
|
||||
/-! ### allM -/
|
||||
/-! ### allM and anyM -/
|
||||
|
||||
theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as : List α) :
|
||||
allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by
|
||||
@@ -407,6 +414,18 @@ theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as :
|
||||
funext b
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] (p : α → Bool) (as : List α) :
|
||||
as.anyM (m := m) (pure <| p ·) = pure (as.any p) := by
|
||||
induction as with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [anyM, ih, pure_bind, all_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] (p : α → Bool) (as : List α) :
|
||||
as.allM (m := m) (pure <| p ·) = pure (as.all p) := by
|
||||
simp [allM_eq_not_anyM_not, all_eq_not_any_not]
|
||||
|
||||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||||
|
||||
/--
|
||||
@@ -422,12 +441,12 @@ and simplifies these to the function directly taking the value.
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β → α → m β) :
|
||||
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
|
||||
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β → α → m β) (init : β) :
|
||||
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α → Prop) (xs : List (Subtype P)) (f : β → α → m β) :
|
||||
xs.unattach.foldlM f = xs.foldlM fun b ⟨x, h⟩ =>
|
||||
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α → Prop) (xs : List (Subtype P)) (f : β → α → m β) (init : β):
|
||||
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b ⟨x, h⟩ =>
|
||||
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
|
||||
f b (wfParam x) := by
|
||||
simp [wfParam]
|
||||
@@ -449,12 +468,12 @@ and simplifies these to the function directly taking the value.
|
||||
funext b
|
||||
simp [hf]
|
||||
|
||||
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α → β → m β) :
|
||||
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
|
||||
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α → β → m β) (init : β) :
|
||||
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α → Prop) (xs : List (Subtype P)) (f : α → β → m β) :
|
||||
xs.unattach.foldrM f = xs.foldrM fun ⟨x, h⟩ b =>
|
||||
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α → Prop) (xs : List (Subtype P)) (f : α → β → m β) (init : β) :
|
||||
xs.unattach.foldrM f init = xs.foldrM (init := init) fun ⟨x, h⟩ b =>
|
||||
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
|
||||
f (wfParam x) b := by
|
||||
simp [wfParam]
|
||||
|
||||
@@ -13,7 +13,8 @@ Proves various lemmas about `List.insertIdx`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Function Nat
|
||||
|
||||
@@ -28,29 +29,29 @@ section InsertIdx
|
||||
variable {a : α}
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_zero (s : List α) (x : α) : insertIdx 0 x s = x :: s :=
|
||||
theorem insertIdx_zero (xs : List α) (x : α) : xs.insertIdx 0 x = x :: xs :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_succ_nil (n : Nat) (a : α) : insertIdx (n + 1) a [] = [] :=
|
||||
theorem insertIdx_succ_nil (n : Nat) (a : α) : ([] : List α).insertIdx (n + 1) a = [] :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_succ_cons (s : List α) (hd x : α) (i : Nat) :
|
||||
insertIdx (i + 1) x (hd :: s) = hd :: insertIdx i x s :=
|
||||
theorem insertIdx_succ_cons (xs : List α) (hd x : α) (i : Nat) :
|
||||
(hd :: xs).insertIdx (i + 1) x = hd :: xs.insertIdx i x :=
|
||||
rfl
|
||||
|
||||
theorem length_insertIdx : ∀ i as, (insertIdx i a as).length = if i ≤ as.length then as.length + 1 else as.length
|
||||
theorem length_insertIdx : ∀ i (as : List α), (as.insertIdx i a).length = if i ≤ as.length then as.length + 1 else as.length
|
||||
| 0, _ => by simp
|
||||
| n + 1, [] => by simp
|
||||
| n + 1, a :: as => by
|
||||
simp only [insertIdx_succ_cons, length_cons, length_insertIdx, Nat.add_le_add_iff_right]
|
||||
split <;> rfl
|
||||
|
||||
theorem length_insertIdx_of_le_length (h : i ≤ length as) : length (insertIdx i a as) = length as + 1 := by
|
||||
theorem length_insertIdx_of_le_length (h : i ≤ length as) : (as.insertIdx i a).length = as.length + 1 := by
|
||||
simp [length_insertIdx, h]
|
||||
|
||||
theorem length_insertIdx_of_length_lt (h : length as < i) : length (insertIdx i a as) = length as := by
|
||||
theorem length_insertIdx_of_length_lt (h : length as < i) : (as.insertIdx i a).length = as.length := by
|
||||
simp [length_insertIdx, h]
|
||||
|
||||
@[simp]
|
||||
@@ -60,7 +61,7 @@ theorem eraseIdx_insertIdx (i : Nat) (l : List α) : (l.insertIdx i a).eraseIdx
|
||||
|
||||
theorem insertIdx_eraseIdx_of_ge :
|
||||
∀ i m as,
|
||||
i < length as → i ≤ m → insertIdx m a (as.eraseIdx i) = (as.insertIdx (m + 1) a).eraseIdx i
|
||||
i < length as → i ≤ m → (as.eraseIdx i).insertIdx m a = (as.insertIdx (m + 1) a).eraseIdx i
|
||||
| 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim
|
||||
| 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx]
|
||||
| 0, _ + 1, _ :: _, _, _ => rfl
|
||||
@@ -70,7 +71,7 @@ theorem insertIdx_eraseIdx_of_ge :
|
||||
|
||||
theorem insertIdx_eraseIdx_of_le :
|
||||
∀ i j as,
|
||||
i < length as → j ≤ i → insertIdx j a (as.eraseIdx i) = (as.insertIdx j a).eraseIdx (i + 1)
|
||||
i < length as → j ≤ i → (as.eraseIdx i).insertIdx j a = (as.insertIdx j a).eraseIdx (i + 1)
|
||||
| _, 0, _ :: _, _, _ => rfl
|
||||
| n + 1, m + 1, a :: as, has, hmn =>
|
||||
congrArg (cons a) <|
|
||||
@@ -95,7 +96,7 @@ theorem mem_insertIdx {a b : α} :
|
||||
← or_assoc, @or_comm (a = a'), or_assoc, mem_cons]
|
||||
|
||||
theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length < i) :
|
||||
insertIdx i x l = l := by
|
||||
l.insertIdx i x = l := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
cases i
|
||||
@@ -108,24 +109,24 @@ theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length <
|
||||
simpa using ih _ h
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_length_self (l : List α) (x : α) : insertIdx l.length x l = l ++ [x] := by
|
||||
theorem insertIdx_length_self (l : List α) (x : α) : l.insertIdx l.length x = l ++ [x] := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x l ih => simpa using ih
|
||||
|
||||
theorem length_le_length_insertIdx (l : List α) (x : α) (i : Nat) :
|
||||
l.length ≤ (insertIdx i x l).length := by
|
||||
l.length ≤ (l.insertIdx i x).length := by
|
||||
simp only [length_insertIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem length_insertIdx_le_succ (l : List α) (x : α) (i : Nat) :
|
||||
(insertIdx i x l).length ≤ l.length + 1 := by
|
||||
(l.insertIdx i x).length ≤ l.length + 1 := by
|
||||
simp only [length_insertIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
|
||||
(hk : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
(hk : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
induction i generalizing j l with
|
||||
| zero => simp at hn
|
||||
| succ n ih =>
|
||||
@@ -138,8 +139,8 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
|
||||
simpa using ih hn _
|
||||
|
||||
@[simp]
|
||||
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[i] = x := by
|
||||
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[i] = x := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
simp [length_insertIdx] at hi
|
||||
@@ -153,8 +154,8 @@ theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (inser
|
||||
simpa using ih hi
|
||||
|
||||
theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
|
||||
(hk : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
(hk : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
|
||||
induction l generalizing i j with
|
||||
| nil =>
|
||||
cases i with
|
||||
@@ -182,8 +183,8 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
|
||||
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
|
||||
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
|
||||
|
||||
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx i x l).length) :
|
||||
(insertIdx i x l)[j] =
|
||||
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) :
|
||||
(l.insertIdx i x)[j] =
|
||||
if h₁ : j < i then
|
||||
l[j]'(by simp [length_insertIdx] at h; split at h <;> omega)
|
||||
else
|
||||
@@ -199,7 +200,7 @@ theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx
|
||||
· rw [getElem_insertIdx_of_gt (by omega)]
|
||||
|
||||
theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
|
||||
(insertIdx i x l)[j]? =
|
||||
(l.insertIdx i x)[j]? =
|
||||
if j < i then
|
||||
l[j]?
|
||||
else
|
||||
@@ -230,16 +231,16 @@ theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
|
||||
split at h <;> omega
|
||||
|
||||
theorem getElem?_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (h : j < i) :
|
||||
(insertIdx i x l)[j]? = l[j]? := by
|
||||
(l.insertIdx i x)[j]? = l[j]? := by
|
||||
rw [getElem?_insertIdx, if_pos h]
|
||||
|
||||
theorem getElem?_insertIdx_self {l : List α} {x : α} {i : Nat} :
|
||||
(insertIdx i x l)[i]? = if i ≤ l.length then some x else none := by
|
||||
(l.insertIdx i x)[i]? = if i ≤ l.length then some x else none := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega)]
|
||||
simp
|
||||
|
||||
theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j) :
|
||||
(insertIdx i x l)[j]? = l[j - 1]? := by
|
||||
(l.insertIdx i x)[j]? = l[j - 1]? := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
|
||||
|
||||
@[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")]
|
||||
|
||||
@@ -87,75 +87,75 @@ theorem eraseIdx_modifyHead_zero {f : α → α} {l : List α} :
|
||||
|
||||
/-! ### modifyTailIdx -/
|
||||
|
||||
@[simp] theorem modifyTailIdx_id : ∀ n (l : List α), l.modifyTailIdx id n = l
|
||||
@[simp] theorem modifyTailIdx_id : ∀ i (l : List α), l.modifyTailIdx i id = l
|
||||
| 0, _ => rfl
|
||||
| _+1, [] => rfl
|
||||
| n+1, a :: l => congrArg (cons a) (modifyTailIdx_id n l)
|
||||
|
||||
theorem eraseIdx_eq_modifyTailIdx : ∀ i (l : List α), eraseIdx l i = modifyTailIdx tail i l
|
||||
theorem eraseIdx_eq_modifyTailIdx : ∀ i (l : List α), eraseIdx l i = l.modifyTailIdx i tail
|
||||
| 0, l => by cases l <;> rfl
|
||||
| _+1, [] => rfl
|
||||
| _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _)
|
||||
|
||||
@[simp] theorem length_modifyTailIdx (f : List α → List α) (H : ∀ l, length (f l) = length l) :
|
||||
∀ n l, length (modifyTailIdx f n l) = length l
|
||||
| 0, _ => H _
|
||||
| _+1, [] => rfl
|
||||
| _+1, _ :: _ => congrArg (·+1) (length_modifyTailIdx _ H _ _)
|
||||
@[simp] theorem length_modifyTailIdx (f : List α → List α) (H : ∀ l, (f l).length = l.length) :
|
||||
∀ (l : List α) i, (l.modifyTailIdx i f).length = l.length
|
||||
| _, 0 => H _
|
||||
| [], _+1 => rfl
|
||||
| _ :: _, _+1 => congrArg (·+1) (length_modifyTailIdx _ H _ _)
|
||||
|
||||
theorem modifyTailIdx_add (f : List α → List α) (n) (l₁ l₂ : List α) :
|
||||
modifyTailIdx f (l₁.length + n) (l₁ ++ l₂) = l₁ ++ modifyTailIdx f n l₂ := by
|
||||
theorem modifyTailIdx_add (f : List α → List α) (i) (l₁ l₂ : List α) :
|
||||
(l₁ ++ l₂).modifyTailIdx (l₁.length + i) f = l₁ ++ l₂.modifyTailIdx i f := by
|
||||
induction l₁ <;> simp [*, Nat.succ_add]
|
||||
|
||||
theorem modifyTailIdx_eq_take_drop (f : List α → List α) (H : f [] = []) :
|
||||
∀ i l, modifyTailIdx f i l = take i l ++ f (drop i l)
|
||||
| 0, _ => rfl
|
||||
| _ + 1, [] => H.symm
|
||||
| n + 1, b :: l => congrArg (cons b) (modifyTailIdx_eq_take_drop f H n l)
|
||||
∀ (l : List α) i, l.modifyTailIdx i f = l.take i ++ f (l.drop i)
|
||||
| _, 0 => rfl
|
||||
| [], _ + 1 => H.symm
|
||||
| b :: l, i + 1 => congrArg (cons b) (modifyTailIdx_eq_take_drop f H l i)
|
||||
|
||||
theorem exists_of_modifyTailIdx (f : List α → List α) {n} {l : List α} (h : n ≤ l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = n ∧ modifyTailIdx f n l = l₁ ++ f l₂ :=
|
||||
have ⟨_, _, eq, hl⟩ : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = n :=
|
||||
⟨_, _, (take_append_drop n l).symm, length_take_of_le h⟩
|
||||
⟨_, _, eq, hl, hl ▸ eq ▸ modifyTailIdx_add (n := 0) ..⟩
|
||||
theorem exists_of_modifyTailIdx (f : List α → List α) {i} {l : List α} (h : i ≤ l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = i ∧ l.modifyTailIdx i f = l₁ ++ f l₂ := by
|
||||
obtain ⟨l₁, l₂, rfl, rfl⟩ : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁.length = i :=
|
||||
⟨_, _, (take_append_drop i l).symm, length_take_of_le h⟩
|
||||
exact ⟨l₁, l₂, rfl, rfl, modifyTailIdx_add f 0 l₁ l₂⟩
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx {f g : List α → List α} (m : Nat) :
|
||||
∀ (n) (l : List α),
|
||||
(l.modifyTailIdx f n).modifyTailIdx g (m + n) =
|
||||
l.modifyTailIdx (fun l => (f l).modifyTailIdx g m) n
|
||||
theorem modifyTailIdx_modifyTailIdx {f g : List α → List α} (i : Nat) :
|
||||
∀ (j) (l : List α),
|
||||
(l.modifyTailIdx j f).modifyTailIdx (i + j) g =
|
||||
l.modifyTailIdx j (fun l => (f l).modifyTailIdx i g)
|
||||
| 0, _ => rfl
|
||||
| _ + 1, [] => rfl
|
||||
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx m n l)
|
||||
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx i n l)
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx_le {f g : List α → List α} (m n : Nat) (l : List α)
|
||||
(h : n ≤ m) :
|
||||
(l.modifyTailIdx f n).modifyTailIdx g m =
|
||||
l.modifyTailIdx (fun l => (f l).modifyTailIdx g (m - n)) n := by
|
||||
theorem modifyTailIdx_modifyTailIdx_le {f g : List α → List α} (i j : Nat) (l : List α)
|
||||
(h : j ≤ i) :
|
||||
(l.modifyTailIdx j f).modifyTailIdx i g =
|
||||
l.modifyTailIdx j (fun l => (f l).modifyTailIdx (i - j) g) := by
|
||||
rcases Nat.exists_eq_add_of_le h with ⟨m, rfl⟩
|
||||
rw [Nat.add_comm, modifyTailIdx_modifyTailIdx, Nat.add_sub_cancel]
|
||||
|
||||
theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (n : Nat) (l : List α) :
|
||||
(l.modifyTailIdx f n).modifyTailIdx g n = l.modifyTailIdx (g ∘ f) n := by
|
||||
rw [modifyTailIdx_modifyTailIdx_le n n l (Nat.le_refl n), Nat.sub_self]; rfl
|
||||
theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (i : Nat) (l : List α) :
|
||||
(l.modifyTailIdx i f).modifyTailIdx i g = l.modifyTailIdx i (g ∘ f) := by
|
||||
rw [modifyTailIdx_modifyTailIdx_le i i l (Nat.le_refl i), Nat.sub_self]; rfl
|
||||
|
||||
/-! ### modify -/
|
||||
|
||||
@[simp] theorem modify_nil (f : α → α) (i) : [].modify f i = [] := by cases i <;> rfl
|
||||
@[simp] theorem modify_nil (f : α → α) (i) : [].modify i f = [] := by cases i <;> rfl
|
||||
|
||||
@[simp] theorem modify_zero_cons (f : α → α) (a : α) (l : List α) :
|
||||
(a :: l).modify f 0 = f a :: l := rfl
|
||||
(a :: l).modify 0 f = f a :: l := rfl
|
||||
|
||||
@[simp] theorem modify_succ_cons (f : α → α) (a : α) (l : List α) (i) :
|
||||
(a :: l).modify f (i + 1) = a :: l.modify f i := by rfl
|
||||
(a :: l).modify (i + 1) f = a :: l.modify i f := by rfl
|
||||
|
||||
theorem modifyHead_eq_modify_zero (f : α → α) (l : List α) :
|
||||
l.modifyHead f = l.modify f 0 := by cases l <;> simp
|
||||
l.modifyHead f = l.modify 0 f := by cases l <;> simp
|
||||
|
||||
@[simp] theorem modify_eq_nil_iff {f : α → α} {i} {l : List α} :
|
||||
l.modify f i = [] ↔ l = [] := by cases l <;> cases i <;> simp
|
||||
l.modify i f = [] ↔ l = [] := by cases l <;> cases i <;> simp
|
||||
|
||||
theorem getElem?_modify (f : α → α) :
|
||||
∀ i (l : List α) j, (modify f i l)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
|
||||
∀ i (l : List α) j, (l.modify i f)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
|
||||
| n, l, 0 => by cases l <;> cases n <;> simp
|
||||
| n, [], _+1 => by cases n <;> rfl
|
||||
| 0, _ :: l, j+1 => by cases h : l[j]? <;> simp [h, modify, j.succ_ne_zero.symm]
|
||||
@@ -165,32 +165,32 @@ theorem getElem?_modify (f : α → α) :
|
||||
cases h' : l[j]? <;> by_cases h : i = j <;>
|
||||
simp [h, if_pos, if_neg, Option.map, mt Nat.succ.inj, not_false_iff, h']
|
||||
|
||||
@[simp] theorem length_modify (f : α → α) : ∀ i l, length (modify f i l) = length l :=
|
||||
@[simp] theorem length_modify (f : α → α) : ∀ (l : List α) i, (l.modify i f).length = l.length :=
|
||||
length_modifyTailIdx _ fun l => by cases l <;> rfl
|
||||
|
||||
@[simp] theorem getElem?_modify_eq (f : α → α) (i) (l : List α) :
|
||||
(modify f i l)[i]? = f <$> l[i]? := by
|
||||
(l.modify i f)[i]? = f <$> l[i]? := by
|
||||
simp only [getElem?_modify, if_pos]
|
||||
|
||||
@[simp] theorem getElem?_modify_ne (f : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
(modify f i l)[j]? = l[j]? := by
|
||||
(l.modify i f)[j]? = l[j]? := by
|
||||
simp only [getElem?_modify, if_neg h, id_map']
|
||||
|
||||
theorem getElem_modify (f : α → α) (i) (l : List α) (j) (h : j < (modify f i l).length) :
|
||||
(modify f i l)[j] =
|
||||
theorem getElem_modify (f : α → α) (i) (l : List α) (j) (h : j < (l.modify i f).length) :
|
||||
(l.modify i f)[j] =
|
||||
if i = j then f (l[j]'(by simp at h; omega)) else l[j]'(by simp at h; omega) := by
|
||||
rw [getElem_eq_iff, getElem?_modify]
|
||||
simp at h
|
||||
simp [h]
|
||||
|
||||
@[simp] theorem getElem_modify_eq (f : α → α) (i) (l : List α) (h) :
|
||||
(modify f i l)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
|
||||
(l.modify i f)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
|
||||
|
||||
@[simp] theorem getElem_modify_ne (f : α → α) {i j} (l : List α) (h : i ≠ j) (h') :
|
||||
(modify f i l)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
|
||||
(l.modify i f)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
|
||||
|
||||
theorem modify_eq_self {f : α → α} {i} {l : List α} (h : l.length ≤ i) :
|
||||
l.modify f i = l := by
|
||||
l.modify i f = l := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -199,7 +199,7 @@ theorem modify_eq_self {f : α → α} {i} {l : List α} (h : l.length ≤ i) :
|
||||
omega
|
||||
|
||||
theorem modify_modify_eq (f g : α → α) (i) (l : List α) :
|
||||
(modify f i l).modify g i = modify (g ∘ f) i l := by
|
||||
(l.modify i f).modify i g = l.modify i (g ∘ f) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -207,7 +207,7 @@ theorem modify_modify_eq (f g : α → α) (i) (l : List α) :
|
||||
split <;> simp
|
||||
|
||||
theorem modify_modify_ne (f g : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
(modify f i l).modify g j = (l.modify g j).modify f i := by
|
||||
(l.modify i f).modify j g = (l.modify j g).modify i f := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -215,7 +215,7 @@ theorem modify_modify_ne (f g : α → α) {i j} (l : List α) (h : i ≠ j) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem modify_eq_set [Inhabited α] (f : α → α) (i) (l : List α) :
|
||||
modify f i l = l.set i (f (l[i]?.getD default)) := by
|
||||
l.modify i f = l.set i (f (l[i]?.getD default)) := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m h₁ h₂
|
||||
@@ -227,24 +227,24 @@ theorem modify_eq_set [Inhabited α] (f : α → α) (i) (l : List α) :
|
||||
· rfl
|
||||
|
||||
theorem modify_eq_take_drop (f : α → α) :
|
||||
∀ i l, modify f i l = take i l ++ modifyHead f (drop i l) :=
|
||||
∀ (l : List α) i, l.modify i f = l.take i ++ modifyHead f (l.drop i) :=
|
||||
modifyTailIdx_eq_take_drop _ rfl
|
||||
|
||||
theorem modify_eq_take_cons_drop {f : α → α} {i} {l : List α} (h : i < l.length) :
|
||||
modify f i l = take i l ++ f l[i] :: drop (i + 1) l := by
|
||||
l.modify i f = l.take i ++ f l[i] :: l.drop (i + 1) := by
|
||||
rw [modify_eq_take_drop, drop_eq_getElem_cons h]; rfl
|
||||
|
||||
theorem exists_of_modify (f : α → α) {i} {l : List α} (h : i < l.length) :
|
||||
∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ l₁.length = i ∧ modify f i l = l₁ ++ f a :: l₂ :=
|
||||
∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ l₁.length = i ∧ l.modify i f = l₁ ++ f a :: l₂ :=
|
||||
match exists_of_modifyTailIdx _ (Nat.le_of_lt h) with
|
||||
| ⟨_, _::_, eq, hl, H⟩ => ⟨_, _, _, eq, hl, H⟩
|
||||
| ⟨_, [], eq, hl, _⟩ => nomatch Nat.ne_of_gt h (eq ▸ append_nil _ ▸ hl)
|
||||
|
||||
@[simp] theorem modify_id (i) (l : List α) : l.modify id i = l := by
|
||||
@[simp] theorem modify_id (i) (l : List α) : l.modify i id = l := by
|
||||
simp [modify]
|
||||
|
||||
theorem take_modify (f : α → α) (i j) (l : List α) :
|
||||
(modify f i l).take j = (take j l).modify f i := by
|
||||
(l.modify i f).take j = (l.take j).modify i f := by
|
||||
induction j generalizing l i with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
@@ -256,7 +256,7 @@ theorem take_modify (f : α → α) (i j) (l : List α) :
|
||||
| succ i => simp [ih]
|
||||
|
||||
theorem drop_modify_of_lt (f : α → α) (i j) (l : List α) (h : i < j) :
|
||||
(modify f i l).drop j = l.drop j := by
|
||||
(l.modify i f).drop j = l.drop j := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -265,7 +265,7 @@ theorem drop_modify_of_lt (f : α → α) (i j) (l : List α) (h : i < j) :
|
||||
omega
|
||||
|
||||
theorem drop_modify_of_ge (f : α → α) (i j) (l : List α) (h : i ≥ j) :
|
||||
(modify f i l).drop j = modify f (i - j) (drop j l) := by
|
||||
(l.modify i f).drop j = (l.drop j).modify (i - j) f := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro m' h₁ h₂
|
||||
@@ -273,7 +273,7 @@ theorem drop_modify_of_ge (f : α → α) (i j) (l : List α) (h : i ≥ j) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem eraseIdx_modify_of_eq (f : α → α) (i) (l : List α) :
|
||||
(modify f i l).eraseIdx i = l.eraseIdx i := by
|
||||
(l.modify i f).eraseIdx i = l.eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro m h₁ h₂
|
||||
@@ -281,7 +281,7 @@ theorem eraseIdx_modify_of_eq (f : α → α) (i) (l : List α) :
|
||||
split <;> split <;> first | rfl | omega
|
||||
|
||||
theorem eraseIdx_modify_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f (i - 1) := by
|
||||
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify (i - 1) f := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -291,7 +291,7 @@ theorem eraseIdx_modify_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem eraseIdx_modify_of_gt (f : α → α) (i j) (l : List α) (h : j > i) :
|
||||
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f i := by
|
||||
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify i f := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -301,7 +301,7 @@ theorem eraseIdx_modify_of_gt (f : α → α) (i j) (l : List α) (h : j > i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem modify_eraseIdx_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
(l.eraseIdx i).modify f j = (l.modify f j).eraseIdx i := by
|
||||
(l.eraseIdx i).modify j f = (l.modify j f).eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
@@ -311,7 +311,7 @@ theorem modify_eraseIdx_of_lt (f : α → α) (i j) (l : List α) (h : j < i) :
|
||||
all_goals (first | rfl | omega)
|
||||
|
||||
theorem modify_eraseIdx_of_ge (f : α → α) (i j) (l : List α) (h : j ≥ i) :
|
||||
(l.eraseIdx i).modify f j = (l.modify f (j + 1)).eraseIdx i := by
|
||||
(l.eraseIdx i).modify j f = (l.modify (j + 1) f).eraseIdx i := by
|
||||
apply ext_getElem
|
||||
· simp [length_eraseIdx]
|
||||
· intro k h₁ h₂
|
||||
|
||||
@@ -17,10 +17,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace List
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : fin n → α` returns the list whose ith element is `f i`
|
||||
```
|
||||
ofFn f = [f 0, f 1, ... , f (n - 1)]
|
||||
```
|
||||
Creates a list by applying `f` to each potential index in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `List.ofFn (n := 3) toString = ["0", "1", "2"]`
|
||||
* `List.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = ["red", "green", "blue"]`
|
||||
-/
|
||||
def ofFn {n} (f : Fin n → α) : List α := Fin.foldr n (f · :: ·) []
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ The notation `~` is used for permutation equivalence.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Nat
|
||||
|
||||
@@ -47,6 +48,14 @@ instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
|
||||
|
||||
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ ↔ l₂ ~ l₁ := ⟨Perm.symm, Perm.symm⟩
|
||||
|
||||
protected theorem Perm.congr_left {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
|
||||
l₁ ~ l₃ ↔ l₂ ~ l₃ :=
|
||||
⟨h.symm.trans, h.trans⟩
|
||||
|
||||
protected theorem Perm.congr_right {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
|
||||
l₃ ~ l₁ ↔ l₃ ~ l₂ :=
|
||||
⟨fun h' => h'.trans h, fun h' => h'.trans h.symm⟩
|
||||
|
||||
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
|
||||
(swap ..).trans <| p.cons _ |>.cons _
|
||||
|
||||
@@ -514,7 +523,7 @@ theorem Perm.eraseP (f : α → Bool) {l₁ l₂ : List α}
|
||||
exact fun h h₁ h₂ => h h₂ h₁
|
||||
|
||||
theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i ≤ l.length) :
|
||||
insertIdx i x l ~ x :: l := by
|
||||
l.insertIdx i x ~ x :: l := by
|
||||
induction l generalizing i with
|
||||
| nil =>
|
||||
cases i with
|
||||
|
||||
@@ -20,10 +20,14 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
namespace List
|
||||
|
||||
/--
|
||||
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
|
||||
Merges two lists, using `le` to select the first element of the resulting list if both are
|
||||
non-empty.
|
||||
|
||||
This version is not tail-recursive,
|
||||
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
|
||||
If both input lists are sorted according to `le`, then the resulting list is also sorted according
|
||||
to `le`. `O(min |l| |r|)`.
|
||||
|
||||
This implementation is not tail-recursive, but it is replaced at runtime by a proven-equivalent
|
||||
tail-recursive merge.
|
||||
-/
|
||||
def merge (xs ys : List α) (le : α → α → Bool := by exact fun a b => a ≤ b) : List α :=
|
||||
match xs, ys with
|
||||
@@ -54,16 +58,16 @@ def MergeSort.Internal.splitInTwo (l : { l : List α // l.length = n }) :
|
||||
open MergeSort.Internal in
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
Simplified implementation of stable merge sort.
|
||||
A stable merge sort.
|
||||
|
||||
This function is designed for reasoning about the algorithm, and is not efficient.
|
||||
(It particular it uses the non tail-recursive `merge` function,
|
||||
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
|
||||
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
|
||||
This function is a simplified implementation that's designed to be easy to reason about, rather than
|
||||
for efficiency. In particular, it uses the non-tail-recursive `List.merge` function and traverses
|
||||
lists unnecessarily.
|
||||
|
||||
Because we want the sort to be stable,
|
||||
it is essential that we split the list in two contiguous sublists.
|
||||
It is replaced at runtime by an efficient implementation that has been proven to be equivalent.
|
||||
-/
|
||||
-- Because we want the sort to be stable, it is essential that we split the list in two contiguous
|
||||
-- sublists.
|
||||
def mergeSort : ∀ (xs : List α) (le : α → α → Bool := by exact fun a b => a ≤ b), List α
|
||||
| [], _ => []
|
||||
| [a], _ => [a]
|
||||
|
||||
@@ -16,7 +16,8 @@ We prefer to pull `List.toArray` outwards past `Array` operations.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
-- TODO: restore after an update-stage0
|
||||
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
|
||||
@@ -18,7 +18,13 @@ def List.toArrayAux : List α → Array α → Array α
|
||||
| nil, xs => xs
|
||||
| cons a as, xs => toArrayAux as (xs.push a)
|
||||
|
||||
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
|
||||
/--
|
||||
Converts a `List α` into an `Array α` by repeatedly pushing elements from the list onto an empty
|
||||
array. `O(|xs|)`.
|
||||
|
||||
Use `List.toArray` instead of calling this function directly. At runtime, this operation implements
|
||||
both `List.toArray` and `Array.mk`.
|
||||
-/
|
||||
-- This function is exported to C, where it is called by `Array.mk`
|
||||
-- (the constructor) to implement this functionality.
|
||||
@[inline, match_pattern, pp_nodot, export lean_list_to_array]
|
||||
|
||||
@@ -500,6 +500,9 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
|
||||
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _⟩
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _⟩
|
||||
|
||||
/-! ### le/lt -/
|
||||
|
||||
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
|
||||
@@ -709,6 +712,16 @@ protected theorem le_of_mul_le_mul_left {a b c : Nat} (h : c * a ≤ c * b) (hc
|
||||
have h' : c * b < c * a := Nat.mul_lt_mul_of_pos_left hlt hc
|
||||
absurd h (Nat.not_le_of_gt h')
|
||||
|
||||
protected theorem le_of_mul_le_mul_right {a b c : Nat} (h : a * c ≤ b * c) (hc : 0 < c) : a ≤ b := by
|
||||
rw [Nat.mul_comm a c, Nat.mul_comm b c] at h
|
||||
exact Nat.le_of_mul_le_mul_left h hc
|
||||
|
||||
protected theorem mul_le_mul_left_iff {n m k : Nat} (w : 0 < k) : k * n ≤ k * m ↔ n ≤ m :=
|
||||
⟨fun h => Nat.le_of_mul_le_mul_left h w, fun h => mul_le_mul_left _ h⟩
|
||||
|
||||
protected theorem mul_le_mul_right_iff {n m k : Nat} (w : 0 < k) : n * k ≤ m * k ↔ n ≤ m :=
|
||||
⟨fun h => Nat.le_of_mul_le_mul_right h w, fun h => mul_le_mul_right _ h⟩
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_left {m k n : Nat} (hn : 0 < n) (h : n * m = n * k) : m = k :=
|
||||
Nat.le_antisymm (Nat.le_of_mul_le_mul_left (Nat.le_of_eq h) hn)
|
||||
(Nat.le_of_mul_le_mul_left (Nat.le_of_eq h.symm) hn)
|
||||
|
||||
@@ -74,6 +74,10 @@ theorem shiftRight_eq_div_pow (m : Nat) : ∀ n, m >>> n = m / 2 ^ n
|
||||
theorem shiftRight_eq_zero (m n : Nat) (hn : m < 2^n) : m >>> n = 0 := by
|
||||
simp [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt hn]
|
||||
|
||||
theorem shiftRight_le (m n : Nat) : m >>> n ≤ m := by
|
||||
simp only [shiftRight_eq_div_pow]
|
||||
apply Nat.div_le_self
|
||||
|
||||
/-!
|
||||
### testBit
|
||||
We define an operation for testing individual bits in the binary representation
|
||||
|
||||
@@ -143,7 +143,7 @@ theorem mod_eq_of_lt {a b : Nat} (h : a < b) : a % b = a :=
|
||||
simp only [add_one_ne_zero, false_iff, ne_eq]
|
||||
exact ne_of_beq_eq_false rfl
|
||||
|
||||
@[simp] theorem Nat.zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n ↔ n = 1 := by
|
||||
@[simp] theorem zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n ↔ n = 1 := by
|
||||
rw [eq_comm]
|
||||
simp
|
||||
|
||||
|
||||
@@ -14,6 +14,31 @@ import Init.Data.Nat.Simproc
|
||||
|
||||
namespace Nat
|
||||
|
||||
theorem mod_add_mod_lt (a b : Nat) {c : Nat} (h : 0 < c) : a % c + b % c < 2 * c - 1 := by
|
||||
have := mod_lt a h
|
||||
have := mod_lt b h
|
||||
omega
|
||||
|
||||
theorem mod_add_mod_eq {a b c : Nat} : a % c + b % c = (a + b) % c + if a % c + b % c < c then 0 else c := by
|
||||
if h : 0 < c then
|
||||
rw [add_mod]
|
||||
split <;> rename_i h'
|
||||
· simp [mod_eq_of_lt h']
|
||||
· have : (a % c + b % c) % c = a % c + b % c - c := by
|
||||
rw [mod_eq_iff]
|
||||
right
|
||||
have := mod_lt a h
|
||||
have := mod_lt b h
|
||||
exact ⟨by omega, ⟨1, by simp; omega⟩⟩
|
||||
omega
|
||||
else
|
||||
replace h : c = 0 := by omega
|
||||
simp [h]
|
||||
|
||||
theorem add_mod_eq_sub : (a + b) % c = a % c + b % c - if a % c + b % c < c then 0 else c := by
|
||||
conv => rhs; congr; rw [mod_add_mod_eq]
|
||||
omega
|
||||
|
||||
theorem lt_div_iff_mul_lt (h : 0 < k) : x < y / k ↔ x * k < y - (k - 1) := by
|
||||
have t := le_div_iff_mul_le h (x := x + 1) (y := y)
|
||||
rw [succ_le, add_one_mul] at t
|
||||
@@ -27,8 +52,8 @@ theorem div_le_iff_le_mul (h : 0 < k) : x / k ≤ y ↔ x ≤ y * k + k - 1 := b
|
||||
omega
|
||||
|
||||
-- TODO: reprove `div_eq_of_lt_le` in terms of this:
|
||||
protected theorem div_eq_iff (h : 0 < k) : x / k = y ↔ x ≤ y * k + k - 1 ∧ y * k ≤ x := by
|
||||
rw [Nat.eq_iff_le_and_ge, le_div_iff_mul_le h, Nat.div_le_iff_le_mul h]
|
||||
protected theorem div_eq_iff (h : 0 < k) : x / k = y ↔ y * k ≤ x ∧ x ≤ y * k + k - 1 := by
|
||||
rw [Nat.eq_iff_le_and_ge, and_comm, le_div_iff_mul_le h, Nat.div_le_iff_le_mul h]
|
||||
|
||||
theorem lt_of_div_eq_zero (h : 0 < k) (h' : x / k = 0) : x < k := by
|
||||
rw [Nat.div_eq_iff h] at h'
|
||||
@@ -98,18 +123,49 @@ theorem succ_div_of_not_dvd {a b : Nat} (h : ¬ b ∣ a + 1) :
|
||||
rw [eq_comm, Nat.div_eq_iff (by simp)]
|
||||
constructor
|
||||
· rw [Nat.div_mul_self_eq_mod_sub_self]
|
||||
have : (a + 1) % (b + 1) < b + 1 := Nat.mod_lt _ (by simp)
|
||||
omega
|
||||
· rw [Nat.div_mul_self_eq_mod_sub_self]
|
||||
have : (a + 1) % (b + 1) < b + 1 := Nat.mod_lt _ (by simp)
|
||||
omega
|
||||
|
||||
theorem succ_div_of_mod_ne_zero {a b : Nat} (h : (a + 1) % b ≠ 0) :
|
||||
(a + 1) / b = a / b := by
|
||||
rw [succ_div_of_not_dvd (by rwa [dvd_iff_mod_eq_zero])]
|
||||
|
||||
theorem succ_div {a b : Nat} : (a + 1) / b = a / b + if b ∣ a + 1 then 1 else 0 := by
|
||||
protected theorem succ_div {a b : Nat} : (a + 1) / b = a / b + if b ∣ a + 1 then 1 else 0 := by
|
||||
split <;> rename_i h
|
||||
· simp [succ_div_of_dvd h]
|
||||
· simp [succ_div_of_not_dvd h]
|
||||
|
||||
protected theorem add_div {a b c : Nat} (h : 0 < c) :
|
||||
(a + b) / c = a / c + b / c + if c ≤ a % c + b % c then 1 else 0 := by
|
||||
conv => lhs; rw [← Nat.div_add_mod a c]
|
||||
rw [Nat.add_assoc, mul_add_div h]
|
||||
conv => lhs; rw [← Nat.div_add_mod b c]
|
||||
rw [Nat.add_comm (a % c), Nat.add_assoc, mul_add_div h, ← Nat.add_assoc, Nat.add_comm (b % c)]
|
||||
congr
|
||||
rw [Nat.div_eq_iff h]
|
||||
constructor
|
||||
· split <;> rename_i h
|
||||
· simpa using h
|
||||
· simp
|
||||
· have := mod_lt a h
|
||||
have := mod_lt b h
|
||||
split <;> · simp; omega
|
||||
|
||||
/-- If `(a + b) % c = c - 1`, then `a % c + b % c < c`, because `a % c + b % c` can not reach `2*c - 1`. -/
|
||||
theorem mod_add_mod_lt_of_add_mod_eq_sub_one (w : 0 < c) (h : (a + b) % c = c - 1) : a % c + b % c < c := by
|
||||
have := mod_add_mod_lt a b w
|
||||
rw [mod_add_mod_eq, h] at this
|
||||
split at this
|
||||
· assumption
|
||||
· omega
|
||||
|
||||
theorem add_div_of_dvd_add_add_one (h : c ∣ a + b + 1) : (a + b) / c = a / c + b / c := by
|
||||
have w : c ≠ 0 := by rintro rfl; simp at h
|
||||
replace w : 0 < c := by omega
|
||||
rw [Nat.add_div w, if_neg, Nat.add_zero]
|
||||
have := mod_add_mod_lt_of_add_mod_eq_sub_one w ((mod_eq_sub_iff Nat.zero_lt_one w).mpr h)
|
||||
omega
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -191,25 +191,43 @@ end Nat
|
||||
namespace Prod
|
||||
|
||||
/--
|
||||
`(start, stop).foldI f a` evaluates `f` on all the numbers
|
||||
Combines an initial value with each natural number from in a range, in increasing order.
|
||||
|
||||
In particular, `(start, stop).foldI f init` applies `f`on all the numbers
|
||||
from `start` (inclusive) to `stop` (exclusive) in increasing order:
|
||||
* `(5, 8).foldI f init = init |> f 5 |> f 6 |> f 7`
|
||||
|
||||
Examples:
|
||||
* `(5, 8).foldI (fun j _ _ xs => xs.push j) #[] = (#[] |>.push 5 |>.push 6 |>.push 7)`
|
||||
* `(5, 8).foldI (fun j _ _ xs => xs.push j) #[] = #[5, 6, 7]`
|
||||
* `(5, 8).foldI (fun j _ _ xs => toString j :: xs) [] = ["7", "6", "5"]`
|
||||
-/
|
||||
@[inline] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) → i.1 ≤ j → j < i.2 → α → α) (a : α) : α :=
|
||||
(i.2 - i.1).fold (fun j _ => f (i.1 + j) (by omega) (by omega)) a
|
||||
@[inline] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) → i.1 ≤ j → j < i.2 → α → α) (init : α) : α :=
|
||||
(i.2 - i.1).fold (fun j _ => f (i.1 + j) (by omega) (by omega)) init
|
||||
|
||||
/--
|
||||
`(start, stop).anyI f a` returns true if `f` is true for some natural number
|
||||
from `start` (inclusive) to `stop` (exclusive):
|
||||
* `(5, 8).anyI f = f 5 || f 6 || f 7`
|
||||
Checks whether a predicate holds for any natural number in a range.
|
||||
|
||||
In particular, `(start, stop).allI f` returns true if `f` is true for any natural number from
|
||||
`start` (inclusive) to `stop` (exclusive).
|
||||
|
||||
Examples:
|
||||
* `(5, 8).anyI (fun j _ _ => j == 6) = (5 == 6) || (6 == 6) || (7 == 6)`
|
||||
* `(5, 8).anyI (fun j _ _ => j % 2 = 0) = true`
|
||||
* `(6, 6).anyI (fun j _ _ => j % 2 = 0) = false`
|
||||
-/
|
||||
@[inline] def anyI (i : Nat × Nat) (f : (j : Nat) → i.1 ≤ j → j < i.2 → Bool) : Bool :=
|
||||
(i.2 - i.1).any (fun j _ => f (i.1 + j) (by omega) (by omega))
|
||||
|
||||
/--
|
||||
`(start, stop).allI f a` returns true if `f` is true for all natural numbers
|
||||
from `start` (inclusive) to `stop` (exclusive):
|
||||
* `(5, 8).anyI f = f 5 && f 6 && f 7`
|
||||
Checks whether a predicate holds for all natural numbers in a range.
|
||||
|
||||
In particular, `(start, stop).allI f` returns true if `f` is true for all natural numbers from
|
||||
`start` (inclusive) to `stop` (exclusive).
|
||||
|
||||
Examples:
|
||||
* `(5, 8).allI (fun j _ _ => j < 10) = (5 < 10) && (6 < 10) && (7 < 10)`
|
||||
* `(5, 8).allI (fun j _ _ => j % 2 = 0) = false`
|
||||
* `(6, 7).allI (fun j _ _ => j % 2 = 0) = true`
|
||||
-/
|
||||
@[inline] def allI (i : Nat × Nat) (f : (j : Nat) → i.1 ≤ j → j < i.2 → Bool) : Bool :=
|
||||
(i.2 - i.1).all (fun j _ => f (i.1 + j) (by omega) (by omega))
|
||||
|
||||
@@ -7,6 +7,14 @@ prelude
|
||||
import Init.Data.Nat.Gcd
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `Nat.lcm`
|
||||
|
||||
## Future work:
|
||||
Most of the material about `Nat.gcd` from `Init.Data.Nat.Gcd` has analogues for `Nat.lcm`
|
||||
that should be added to this file.
|
||||
-/
|
||||
|
||||
namespace Nat
|
||||
|
||||
/-- The least common multiple of `m` and `n`, defined using `gcd`. -/
|
||||
|
||||
@@ -131,9 +131,6 @@ protected theorem eq_zero_of_add_eq_zero_right (h : n + m = 0) : n = 0 :=
|
||||
@[simp] protected theorem self_eq_add_right {a b : Nat} : a = a + b ↔ b = 0 := by omega
|
||||
@[simp] protected theorem self_eq_add_left {a b : Nat} : a = b + a ↔ b = 0 := by omega
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _⟩
|
||||
|
||||
protected theorem lt_of_add_lt_add_right : ∀ {n : Nat}, k + n < m + n → k < m
|
||||
| 0, h => h
|
||||
| _+1, h => Nat.lt_of_add_lt_add_right (Nat.lt_of_succ_lt_succ h)
|
||||
@@ -290,10 +287,6 @@ theorem succ_min_succ (x y) : min (succ x) (succ y) = succ (min x y) := by
|
||||
@[simp] protected theorem min_self (a : Nat) : min a a = a := Nat.min_eq_left (Nat.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Nat) min := ⟨Nat.min_self⟩
|
||||
|
||||
@[simp] protected theorem zero_min (a) : min 0 a = 0 := Nat.min_eq_left (Nat.zero_le _)
|
||||
|
||||
@[simp] protected theorem min_zero (a) : min a 0 = 0 := Nat.min_eq_right (Nat.zero_le _)
|
||||
|
||||
@[simp] protected theorem min_assoc : ∀ (a b c : Nat), min (min a b) c = min a (min b c)
|
||||
| 0, _, _ => by rw [Nat.zero_min, Nat.zero_min, Nat.zero_min]
|
||||
| _, 0, _ => by rw [Nat.zero_min, Nat.min_zero, Nat.zero_min]
|
||||
@@ -307,16 +300,16 @@ instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
|
||||
@[simp] protected theorem min_self_assoc' {m n : Nat} : min n (min m n) = min n m := by
|
||||
rw [Nat.min_comm m n, ← Nat.min_assoc, Nat.min_self]
|
||||
|
||||
@[simp] theorem min_add_left {a b : Nat} : min a (b + a) = a := by
|
||||
@[simp] theorem min_add_left_self {a b : Nat} : min a (b + a) = a := by
|
||||
rw [Nat.min_def]
|
||||
simp
|
||||
@[simp] theorem min_add_right {a b : Nat} : min a (a + b) = a := by
|
||||
@[simp] theorem min_add_right_self {a b : Nat} : min a (a + b) = a := by
|
||||
rw [Nat.min_def]
|
||||
simp
|
||||
@[simp] theorem add_left_min {a b : Nat} : min (b + a) a = a := by
|
||||
rw [Nat.min_comm, min_add_left]
|
||||
@[simp] theorem add_right_min {a b : Nat} : min (a + b) a = a := by
|
||||
rw [Nat.min_comm, min_add_right]
|
||||
@[simp] theorem add_left_min_self {a b : Nat} : min (b + a) a = a := by
|
||||
rw [Nat.min_comm, min_add_left_self]
|
||||
@[simp] theorem add_right_min_self {a b : Nat} : min (a + b) a = a := by
|
||||
rw [Nat.min_comm, min_add_right_self]
|
||||
|
||||
protected theorem sub_sub_eq_min : ∀ (a b : Nat), a - (a - b) = min a b
|
||||
| 0, _ => by rw [Nat.zero_sub, Nat.zero_min]
|
||||
@@ -333,55 +326,35 @@ protected theorem sub_eq_sub_min (n m : Nat) : n - m = n - min n m := by
|
||||
@[simp] protected theorem sub_add_min_cancel (n m : Nat) : n - m + min n m = n := by
|
||||
rw [Nat.sub_eq_sub_min, Nat.sub_add_cancel (Nat.min_le_left ..)]
|
||||
|
||||
protected theorem max_eq_right {a b : Nat} (h : a ≤ b) : max a b = b := if_pos h
|
||||
|
||||
protected theorem max_eq_left {a b : Nat} (h : b ≤ a) : max a b = a := by
|
||||
rw [Nat.max_comm]; exact Nat.max_eq_right h
|
||||
|
||||
protected theorem succ_max_succ (x y) : max (succ x) (succ y) = succ (max x y) := by
|
||||
cases Nat.le_total x y with
|
||||
| inl h => rw [Nat.max_eq_right h, Nat.max_eq_right (Nat.succ_le_succ h)]
|
||||
| inr h => rw [Nat.max_eq_left h, Nat.max_eq_left (Nat.succ_le_succ h)]
|
||||
|
||||
protected theorem max_le_of_le_of_le {a b c : Nat} : a ≤ c → b ≤ c → max a b ≤ c := by
|
||||
intros; cases Nat.le_total a b with
|
||||
| inl h => rw [Nat.max_eq_right h]; assumption
|
||||
| inr h => rw [Nat.max_eq_left h]; assumption
|
||||
|
||||
protected theorem max_le {a b c : Nat} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Nat.le_trans (Nat.le_max_left ..) h, Nat.le_trans (Nat.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => Nat.max_le_of_le_of_le h₁ h₂⟩
|
||||
|
||||
protected theorem max_lt {a b c : Nat} : max a b < c ↔ a < c ∧ b < c := by
|
||||
rw [← Nat.succ_le, ← Nat.succ_max_succ a b]; exact Nat.max_le
|
||||
|
||||
@[simp] protected theorem max_self (a : Nat) : max a a = a := Nat.max_eq_right (Nat.le_refl _)
|
||||
instance : Std.IdempotentOp (α := Nat) max := ⟨Nat.max_self⟩
|
||||
|
||||
@[simp] protected theorem zero_max (a) : max 0 a = a := Nat.max_eq_right (Nat.zero_le _)
|
||||
|
||||
@[simp] protected theorem max_zero (a) : max a 0 = a := Nat.max_eq_left (Nat.zero_le _)
|
||||
instance : Std.LawfulIdentity (α := Nat) max 0 where
|
||||
left_id := Nat.zero_max
|
||||
right_id := Nat.max_zero
|
||||
|
||||
protected theorem max_assoc : ∀ (a b c : Nat), max (max a b) c = max a (max b c)
|
||||
@[simp] protected theorem max_assoc : ∀ (a b c : Nat), max (max a b) c = max a (max b c)
|
||||
| 0, _, _ => by rw [Nat.zero_max, Nat.zero_max]
|
||||
| _, 0, _ => by rw [Nat.zero_max, Nat.max_zero]
|
||||
| _, _, 0 => by rw [Nat.max_zero, Nat.max_zero]
|
||||
| _+1, _+1, _+1 => by simp only [Nat.succ_max_succ]; exact congrArg succ <| Nat.max_assoc ..
|
||||
instance : Std.Associative (α := Nat) max := ⟨Nat.max_assoc⟩
|
||||
|
||||
@[simp] theorem max_add_left {a b : Nat} : max a (b + a) = b + a := by
|
||||
@[simp] theorem max_add_left_self {a b : Nat} : max a (b + a) = b + a := by
|
||||
rw [Nat.max_def]
|
||||
simp
|
||||
@[simp] theorem max_add_right {a b : Nat} : max a (a + b) = a + b := by
|
||||
@[simp] theorem max_add_right_self {a b : Nat} : max a (a + b) = a + b := by
|
||||
rw [Nat.max_def]
|
||||
simp
|
||||
@[simp] theorem add_left_max {a b : Nat} : max (b + a) a = b + a := by
|
||||
rw [Nat.max_comm, max_add_left]
|
||||
@[simp] theorem add_right_max {a b : Nat} : max (a + b) a = a + b := by
|
||||
rw [Nat.max_comm, max_add_right]
|
||||
@[simp] theorem add_left_max_self {a b : Nat} : max (b + a) a = b + a := by
|
||||
rw [Nat.max_comm, max_add_left_self]
|
||||
@[simp] theorem add_right_max_self {a b : Nat} : max (a + b) a = a + b := by
|
||||
rw [Nat.max_comm, max_add_right_self]
|
||||
|
||||
protected theorem sub_add_eq_max (a b : Nat) : a - b + b = max a b := by
|
||||
match Nat.le_total a b with
|
||||
@@ -423,22 +396,6 @@ protected theorem min_max_distrib_right (a b c : Nat) :
|
||||
repeat rw [Nat.min_comm _ c]
|
||||
exact Nat.min_max_distrib_left ..
|
||||
|
||||
protected theorem add_max_add_right : ∀ (a b c : Nat), max (a + c) (b + c) = max a b + c
|
||||
| _, _, 0 => rfl
|
||||
| _, _, _+1 => Eq.trans (Nat.succ_max_succ ..) <| congrArg _ (Nat.add_max_add_right ..)
|
||||
|
||||
protected theorem add_min_add_right : ∀ (a b c : Nat), min (a + c) (b + c) = min a b + c
|
||||
| _, _, 0 => rfl
|
||||
| _, _, _+1 => Eq.trans (Nat.succ_min_succ ..) <| congrArg _ (Nat.add_min_add_right ..)
|
||||
|
||||
protected theorem add_max_add_left (a b c : Nat) : max (a + b) (a + c) = a + max b c := by
|
||||
repeat rw [Nat.add_comm a]
|
||||
exact Nat.add_max_add_right ..
|
||||
|
||||
protected theorem add_min_add_left (a b c : Nat) : min (a + b) (a + c) = a + min b c := by
|
||||
repeat rw [Nat.add_comm a]
|
||||
exact Nat.add_min_add_right ..
|
||||
|
||||
protected theorem pred_min_pred : ∀ (x y), min (pred x) (pred y) = pred (min x y)
|
||||
| 0, _ => by simp only [Nat.pred_zero, Nat.zero_min]
|
||||
| _, 0 => by simp only [Nat.pred_zero, Nat.min_zero]
|
||||
@@ -463,58 +420,6 @@ protected theorem sub_min_sub_left (a b c : Nat) : min (a - b) (a - c) = a - max
|
||||
protected theorem sub_max_sub_left (a b c : Nat) : max (a - b) (a - c) = a - min b c := by
|
||||
omega
|
||||
|
||||
protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
|
||||
induction a generalizing b with
|
||||
| zero => simp
|
||||
| succ i ind =>
|
||||
cases b <;> simp [succ_eq_add_one, Nat.succ_mul, Nat.add_max_add_right, ind]
|
||||
|
||||
protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
|
||||
induction a generalizing b with
|
||||
| zero => simp
|
||||
| succ i ind =>
|
||||
cases b <;> simp [succ_eq_add_one, Nat.succ_mul, Nat.add_min_add_right, ind]
|
||||
|
||||
protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
|
||||
repeat rw [Nat.mul_comm a]
|
||||
exact Nat.mul_max_mul_right ..
|
||||
|
||||
protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
|
||||
repeat rw [Nat.mul_comm a]
|
||||
exact Nat.mul_min_mul_right ..
|
||||
|
||||
-- protected theorem sub_min_sub_left (a b c : Nat) : min (a - b) (a - c) = a - max b c := by
|
||||
-- induction b, c using Nat.recDiagAux with
|
||||
-- | zero_left => rw [Nat.sub_zero, Nat.zero_max]; exact Nat.min_eq_right (Nat.sub_le ..)
|
||||
-- | zero_right => rw [Nat.sub_zero, Nat.max_zero]; exact Nat.min_eq_left (Nat.sub_le ..)
|
||||
-- | succ_succ _ _ ih => simp only [Nat.sub_succ, Nat.succ_max_succ, Nat.pred_min_pred, ih]
|
||||
|
||||
-- protected theorem sub_max_sub_left (a b c : Nat) : max (a - b) (a - c) = a - min b c := by
|
||||
-- induction b, c using Nat.recDiagAux with
|
||||
-- | zero_left => rw [Nat.sub_zero, Nat.zero_min]; exact Nat.max_eq_left (Nat.sub_le ..)
|
||||
-- | zero_right => rw [Nat.sub_zero, Nat.min_zero]; exact Nat.max_eq_right (Nat.sub_le ..)
|
||||
-- | succ_succ _ _ ih => simp only [Nat.sub_succ, Nat.succ_min_succ, Nat.pred_max_pred, ih]
|
||||
|
||||
-- protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
|
||||
-- induction a, b using Nat.recDiagAux with
|
||||
-- | zero_left => simp only [Nat.zero_mul, Nat.zero_max]
|
||||
-- | zero_right => simp only [Nat.zero_mul, Nat.max_zero]
|
||||
-- | succ_succ _ _ ih => simp only [Nat.succ_mul, Nat.add_max_add_right, ih]
|
||||
|
||||
-- protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
|
||||
-- induction a, b using Nat.recDiagAux with
|
||||
-- | zero_left => simp only [Nat.zero_mul, Nat.zero_min]
|
||||
-- | zero_right => simp only [Nat.zero_mul, Nat.min_zero]
|
||||
-- | succ_succ _ _ ih => simp only [Nat.succ_mul, Nat.add_min_add_right, ih]
|
||||
|
||||
-- protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
|
||||
-- repeat rw [Nat.mul_comm a]
|
||||
-- exact Nat.mul_max_mul_right ..
|
||||
|
||||
-- protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
|
||||
-- repeat rw [Nat.mul_comm a]
|
||||
-- exact Nat.mul_min_mul_right ..
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
protected theorem mul_right_comm (n m k : Nat) : n * m * k = n * k * m := by
|
||||
@@ -1029,6 +934,25 @@ theorem mod_eq_iff {a b c : Nat} :
|
||||
· simp_all
|
||||
· rw [mul_add_mod, mod_eq_of_lt w]⟩
|
||||
|
||||
theorem mod_eq_sub_iff {a b c : Nat} (h₁ : 0 < c) (h : c ≤ b) : a % b = b - c ↔ b ∣ a + c := by
|
||||
rw [Nat.mod_eq_iff]
|
||||
refine ⟨?_, ?_⟩
|
||||
· rintro (⟨rfl, rfl⟩|⟨hlt, ⟨k, hk⟩⟩)
|
||||
· simp; omega
|
||||
· refine ⟨k + 1, ?_⟩
|
||||
rw [← Nat.add_sub_assoc h] at hk
|
||||
rw [Nat.mul_succ, eq_comm]
|
||||
apply Nat.eq_add_of_sub_eq (by omega) hk.symm
|
||||
· rintro ⟨k, hk⟩
|
||||
obtain (rfl|hb) := Nat.eq_zero_or_pos b
|
||||
· obtain rfl : c = 0 := by omega
|
||||
refine Or.inl ⟨rfl, by simpa using hk⟩
|
||||
· have : k ≠ 0 := by rintro rfl; omega
|
||||
refine Or.inr ⟨by omega, ⟨k - 1, ?_⟩⟩
|
||||
rw [← Nat.add_sub_assoc h, eq_comm]
|
||||
apply Nat.sub_eq_of_eq_add
|
||||
rw [mul_sub_one, Nat.sub_add_cancel (Nat.le_mul_of_pos_right _ (by omega)), hk]
|
||||
|
||||
theorem succ_mod_succ_eq_zero_iff {a b : Nat} :
|
||||
(a + 1) % (b + 1) = 0 ↔ a % (b + 1) = b := by
|
||||
symm
|
||||
|
||||
@@ -12,6 +12,34 @@ namespace Nat
|
||||
|
||||
protected theorem min_eq_min (a : Nat) : Nat.min a b = min a b := rfl
|
||||
|
||||
@[simp] protected theorem zero_min (a : Nat) : min 0 a = 0 := by
|
||||
simp [Nat.min_def]
|
||||
|
||||
@[simp] protected theorem min_zero (a : Nat) : min a 0 = 0 := by
|
||||
simp [Nat.min_def]
|
||||
|
||||
@[simp] protected theorem add_min_add_right (a b c : Nat) : min (a + c) (b + c) = min a b + c := by
|
||||
rw [Nat.min_def, Nat.min_def]
|
||||
simp only [Nat.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem add_min_add_left (a b c : Nat) : min (a + b) (a + c) = a + min b c := by
|
||||
rw [Nat.min_def, Nat.min_def]
|
||||
simp only [Nat.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
|
||||
by_cases h : 0 < c
|
||||
· rw [Nat.min_def, Nat.min_def]
|
||||
simp only [Nat.mul_le_mul_right_iff h]
|
||||
split <;> simp
|
||||
· replace h : c = 0 := by exact Nat.eq_zero_of_not_pos h
|
||||
subst h
|
||||
simp
|
||||
|
||||
@[simp] protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
|
||||
rw [Nat.mul_comm a, Nat.mul_comm a, Nat.mul_min_mul_right, Nat.mul_comm]
|
||||
|
||||
protected theorem min_comm (a b : Nat) : min a b = min b a := by
|
||||
match Nat.lt_trichotomy a b with
|
||||
| .inl h => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
|
||||
@@ -20,7 +48,7 @@ protected theorem min_comm (a b : Nat) : min a b = min b a := by
|
||||
instance : Std.Commutative (α := Nat) min := ⟨Nat.min_comm⟩
|
||||
|
||||
protected theorem min_le_right (a b : Nat) : min a b ≤ b := by
|
||||
by_cases (a <= b) <;> simp [Nat.min_def, *]
|
||||
by_cases (a ≤ b) <;> simp [Nat.min_def, *]
|
||||
protected theorem min_le_left (a b : Nat) : min a b ≤ a :=
|
||||
Nat.min_comm .. ▸ Nat.min_le_right ..
|
||||
|
||||
@@ -43,6 +71,34 @@ protected theorem lt_min {a b c : Nat} : a < min b c ↔ a < b ∧ a < c := Nat.
|
||||
|
||||
protected theorem max_eq_max (a : Nat) : Nat.max a b = max a b := rfl
|
||||
|
||||
@[simp] protected theorem zero_max (a : Nat) : max 0 a = a := by
|
||||
simp [Nat.max_def]
|
||||
|
||||
@[simp] protected theorem max_zero (a : Nat) : max a 0 = a := by
|
||||
simp +contextual [Nat.max_def]
|
||||
|
||||
@[simp] protected theorem add_max_add_right (a b c : Nat) : max (a + c) (b + c) = max a b + c := by
|
||||
rw [Nat.max_def, Nat.max_def]
|
||||
simp only [Nat.add_le_add_iff_right]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem add_max_add_left (a b c : Nat) : max (a + b) (a + c) = a + max b c := by
|
||||
rw [Nat.max_def, Nat.max_def]
|
||||
simp only [Nat.add_le_add_iff_left]
|
||||
split <;> simp
|
||||
|
||||
@[simp] protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
|
||||
by_cases h : 0 < c
|
||||
· rw [Nat.max_def, Nat.max_def]
|
||||
simp only [Nat.mul_le_mul_right_iff h]
|
||||
split <;> simp
|
||||
· replace h : c = 0 := by exact Nat.eq_zero_of_not_pos h
|
||||
subst h
|
||||
simp
|
||||
|
||||
@[simp] protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
|
||||
rw [Nat.mul_comm a, Nat.mul_comm a, Nat.mul_max_mul_right, Nat.mul_comm]
|
||||
|
||||
protected theorem max_comm (a b : Nat) : max a b = max b a := by
|
||||
simp only [Nat.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
@@ -50,9 +106,28 @@ protected theorem max_comm (a b : Nat) : max a b = max b a := by
|
||||
· cases not_or_intro h₁ h₂ <| Nat.le_total ..
|
||||
instance : Std.Commutative (α := Nat) max := ⟨Nat.max_comm⟩
|
||||
|
||||
protected theorem le_max_left ( a b : Nat) : a ≤ max a b := by
|
||||
by_cases (a <= b) <;> simp [Nat.max_def, *]
|
||||
protected theorem le_max_left (a b : Nat) : a ≤ max a b := by
|
||||
by_cases (a ≤ b) <;> simp [Nat.max_def, *]
|
||||
protected theorem le_max_right (a b : Nat) : b ≤ max a b :=
|
||||
Nat.max_comm .. ▸ Nat.le_max_left ..
|
||||
|
||||
protected theorem max_eq_right {a b : Nat} (h : a ≤ b) : max a b = b := if_pos h
|
||||
|
||||
protected theorem max_eq_left {a b : Nat} (h : b ≤ a) : max a b = a :=
|
||||
Nat.max_comm .. ▸ Nat.max_eq_right h
|
||||
|
||||
protected theorem max_le_of_le_of_le {a b c : Nat} : a ≤ c → b ≤ c → max a b ≤ c := by
|
||||
intros; cases Nat.le_total a b with
|
||||
| inl h => rw [Nat.max_eq_right h]; assumption
|
||||
| inr h => rw [Nat.max_eq_left h]; assumption
|
||||
|
||||
protected theorem max_le {a b c : Nat} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Nat.le_trans (Nat.le_max_left ..) h, Nat.le_trans (Nat.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => Nat.max_le_of_le_of_le h₁ h₂⟩
|
||||
|
||||
protected theorem max_lt {a b c : Nat} : max a b < c ↔ a < c ∧ b < c :=
|
||||
match c with
|
||||
| 0 => by simp
|
||||
| c + 1 => by simpa [Nat.lt_add_one_iff] using Nat.max_le
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -80,9 +80,9 @@ instance : OfScientific Float32 where
|
||||
def Float32.ofNat (n : Nat) : Float32 :=
|
||||
OfScientific.ofScientific n false 0
|
||||
|
||||
def Float32.ofInt : Int → Float
|
||||
| Int.ofNat n => Float.ofNat n
|
||||
| Int.negSucc n => Float.neg (Float.ofNat (Nat.succ n))
|
||||
def Float32.ofInt : Int → Float32
|
||||
| Int.ofNat n => Float32.ofNat n
|
||||
| Int.negSucc n => Float32.neg (Float32.ofNat (Nat.succ n))
|
||||
|
||||
instance : OfNat Float32 n := ⟨Float32.ofNat n⟩
|
||||
|
||||
|
||||
@@ -18,16 +18,30 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(o : Option α) (P : α → Prop) (_ : ∀ x ∈ o, P x) : Option {x // P x} := unsafeCast o
|
||||
|
||||
/-- "Attach" a proof `P x` that holds for the element of `o`, if present,
|
||||
to produce a new option with the same element but in the type `{x // P x}`. -/
|
||||
/--
|
||||
“Attaches” a proof that some predicate holds for an optional value, if present, returning a subtype
|
||||
that expresses this fact.
|
||||
|
||||
This function is primarily used to implement `Option.attach`, which allows definitions by
|
||||
well-founded recursion that use iteration operators (such as `Option.map`) to prove that an optional
|
||||
value drawn from a parameter is smaller than the parameter. This allows the well-founded recursion
|
||||
mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Option α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Option {x // P x} :=
|
||||
match xs with
|
||||
| none => none
|
||||
| some x => some ⟨x, H x (mem_some_self x)⟩
|
||||
|
||||
/-- "Attach" the proof that the element of `xs`, if present, is in `xs`
|
||||
to produce a new option with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
/--
|
||||
“Attaches” a proof that an optional value, if present, is indeed this value, returning a subtype
|
||||
that expresses this fact.
|
||||
|
||||
This function is primarily used to allow definitions by well-founded recursion that use iteration
|
||||
operators (such as `Option.map`) to prove that an optional value drawn from a parameter is smaller
|
||||
than the parameter. This allows the well-founded recursion mechanism to prove that the function
|
||||
terminates.
|
||||
-/
|
||||
@[inline] def attach (xs : Option α) : Option {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
|
||||
@@ -200,11 +214,15 @@ 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 an intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
Remove an attached proof that the value in an `Option` is indeed that value.
|
||||
|
||||
If not, usually the right approach is `simp [Option.unattach, -Option.map_subtype]` to unfold.
|
||||
This function is usually inserted automatically by Lean, rather than explicitly in code. It is
|
||||
introduced as an intermediate step during the elaboration of definitions by well-founded recursion.
|
||||
|
||||
If this function is encountered in a proof state, the right approach is usually the tactic
|
||||
`simp [Option.unattach, -Option.map_subtype]`.
|
||||
|
||||
It is a synonym for `Option.map Subtype.val`.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (o : Option { x // p x }) := o.map (·.val)
|
||||
|
||||
|
||||
@@ -24,7 +24,15 @@ def getM [Alternative m] : Option α → m α
|
||||
@[simp] theorem isSome_none : @isSome α none = false := rfl
|
||||
@[simp] theorem isSome_some : isSome (some a) = true := rfl
|
||||
|
||||
/-- Returns `true` on `none` and `false` on `some x`. -/
|
||||
/--
|
||||
Returns `true` on `none` and `false` on `some x`.
|
||||
|
||||
This function is more flexible than `(· == none)` because it does not require a `BEq α` instance.
|
||||
|
||||
Examples:
|
||||
* `(none : Option Nat).isNone = true`
|
||||
* `(some Nat.add).isNone = false`
|
||||
-/
|
||||
@[inline] def isNone : Option α → Bool
|
||||
| some _ => false
|
||||
| none => true
|
||||
@@ -33,17 +41,46 @@ def getM [Alternative m] : Option α → m α
|
||||
@[simp] theorem isNone_some : isNone (some a) = false := rfl
|
||||
|
||||
/--
|
||||
`x?.isEqSome y` is equivalent to `x? == some y`, but avoids an allocation.
|
||||
Checks whether an optional value is both present and equal to some other value.
|
||||
|
||||
Given `x? : Option α` and `y : α`, `x?.isEqSome y` is equivalent to `x? == some y`. It is more
|
||||
efficient because it avoids an allocation.
|
||||
-/
|
||||
@[inline] def isEqSome [BEq α] : Option α → α → Bool
|
||||
| some a, b => a == b
|
||||
| none, _ => false
|
||||
|
||||
/--
|
||||
Sequencing of `Option` computations.
|
||||
|
||||
From the perspective of `Option` as computations that might fail, this function sequences
|
||||
potentially-failing computations, failing if either fails. From the perspective of `Option` as a
|
||||
collection with at most one element, the function is applied to the element if present, and the
|
||||
final result is empty if either the initial or the resulting collections are empty.
|
||||
|
||||
This function is often accessed via the `>>=` operator from the `Bind (Option α)` instance, or
|
||||
implicitly via `do`-notation, but it is also idiomatic to call it using [generalized field
|
||||
notation](lean-manual://section/generalized-field-notation).
|
||||
|
||||
Examples:
|
||||
* `none.bind (fun x => some x) = none`
|
||||
* `(some 4).bind (fun x => some x) = some 4`
|
||||
* `none.bind (Option.guard (· > 2)) = none`
|
||||
* `(some 2).bind (Option.guard (· > 2)) = none`
|
||||
* `(some 4).bind (Option.guard (· > 2)) = some 4`
|
||||
-/
|
||||
@[inline] protected def bind : Option α → (α → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a
|
||||
|
||||
/-- Runs `f` on `o`'s value, if any, and returns its result, or else returns `none`. -/
|
||||
/--
|
||||
Runs the monadic action `f` on `o`'s value, if any, and returns the result, or `none` if there is
|
||||
no value.
|
||||
|
||||
From the perspective of `Option` as a collection with at most one element, the monadic the function
|
||||
is applied to the element if present, and the final result is empty if either the initial or the
|
||||
resulting collections are empty.
|
||||
-/
|
||||
@[inline] protected def bindM [Monad m] (f : α → m (Option β)) (o : Option α) : m (Option β) := do
|
||||
if let some a := o then
|
||||
return (← f a)
|
||||
@@ -51,8 +88,13 @@ def getM [Alternative m] : Option α → m α
|
||||
return none
|
||||
|
||||
/--
|
||||
Runs a monadic function `f` on an optional value.
|
||||
If the optional value is `none` the function is not called.
|
||||
Runs a monadic function `f` on an optional value, returning the result. If the optional value is
|
||||
`none`, the function is not called and the result is also `none`.
|
||||
|
||||
From the perspective of `Option` as a container with at most one element, this is analogous to
|
||||
`List.mapM`, returning the result of running the monadic function on all elements of the container.
|
||||
|
||||
`Option.mapA` is the corresponding operation for applicative functors.
|
||||
-/
|
||||
@[inline] protected def mapM [Monad m] (f : α → m β) (o : Option α) : m (Option β) := do
|
||||
if let some a := o then
|
||||
@@ -63,25 +105,51 @@ If the optional value is `none` the function is not called.
|
||||
theorem map_id : (Option.map id : Option α → Option α) = id :=
|
||||
funext (fun o => match o with | none => rfl | some _ => rfl)
|
||||
|
||||
/-- Keeps an optional value only if it satisfies the predicate `p`. -/
|
||||
/--
|
||||
Keeps an optional value only if it satisfies a Boolean predicate.
|
||||
|
||||
If `Option` is thought of as a collection that contains at most one element, then `Option.filter` is
|
||||
analogous to `List.filter` or `Array.filter`.
|
||||
|
||||
Examples:
|
||||
* `(some 5).filter (· % 2 == 0) = none`
|
||||
* `(some 4).filter (· % 2 == 0) = some 4`
|
||||
* `none.filter (fun x : Nat => x % 2 == 0) = none`
|
||||
* `none.filter (fun x : Nat => true) = none`
|
||||
-/
|
||||
@[always_inline, inline] protected def filter (p : α → Bool) : Option α → Option α
|
||||
| some a => if p a then some a else none
|
||||
| none => none
|
||||
|
||||
/-- Checks that an optional value satisfies a predicate `p` or is `none`. -/
|
||||
/--
|
||||
Checks whether an optional value either satisfies a Boolean predicate or is `none`.
|
||||
|
||||
Examples:
|
||||
* `(some 33).all (· % 2 == 0) = false
|
||||
* `(some 22).all (· % 2 == 0) = true
|
||||
* `none.all (fun x : Nat => x % 2 == 0) = true
|
||||
-/
|
||||
@[always_inline, inline] protected def all (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => true
|
||||
|
||||
/-- Checks that an optional value is not `none` and the value satisfies a predicate `p`. -/
|
||||
/--
|
||||
Checks whether an optional value is not `none` and satisfies a Boolean predicate.
|
||||
|
||||
Examples:
|
||||
* `(some 33).any (· % 2 == 0) = false
|
||||
* `(some 22).any (· % 2 == 0) = true
|
||||
* `none.any (fun x : Nat => true) = false
|
||||
-/
|
||||
@[always_inline, inline] protected def any (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => false
|
||||
|
||||
/--
|
||||
Implementation of `OrElse`'s `<|>` syntax for `Option`. If the first argument is `some a`, returns
|
||||
`some a`, otherwise evaluates and returns the second argument. See also `or` for a version that is
|
||||
strict in the second argument.
|
||||
`some a`, otherwise evaluates and returns the second argument.
|
||||
|
||||
See also `or` for a version that is strict in the second argument.
|
||||
-/
|
||||
@[always_inline, macro_inline] protected def orElse : Option α → (Unit → Option α) → Option α
|
||||
| some a, _ => some a
|
||||
@@ -90,16 +158,42 @@ strict in the second argument.
|
||||
instance : OrElse (Option α) where
|
||||
orElse := Option.orElse
|
||||
|
||||
/-- If the first argument is `some a`, returns `some a`, otherwise returns the second argument.
|
||||
This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
|
||||
/--
|
||||
Returns the first of its arguments that is `some`, or `none` if neither is `some`.
|
||||
|
||||
This is similar to the `<|>` operator, also known as `OrElse.orElse`, but both arguments are always
|
||||
evaluated without short-circuiting.
|
||||
-/
|
||||
@[always_inline, macro_inline] def or : Option α → Option α → Option α
|
||||
| some a, _ => some a
|
||||
| none, b => b
|
||||
|
||||
/--
|
||||
Lifts an ordering relation to `Option`, such that `none` is the least element.
|
||||
|
||||
It can be understood as adding a distinguished least element, represented by `none`, to both `α` and
|
||||
`β`.
|
||||
|
||||
This definition is part of the implementation of the `LT (Option α)` instance. However, because it
|
||||
can be used with heterogeneous relations, it is sometimes useful on its own.
|
||||
|
||||
Examples:
|
||||
* `Option.lt (fun n k : Nat => n < k) none none = False`
|
||||
* `Option.lt (fun n k : Nat => n < k) none (some 3) = True`
|
||||
* `Option.lt (fun n k : Nat => n < k) (some 3) none = False`
|
||||
* `Option.lt (fun n k : Nat => n < k) (some 4) (some 5) = True`
|
||||
* `Option.lt (fun n k : Nat => n < k) (some 4) (some 4) = False`
|
||||
-/
|
||||
@[inline] protected def lt (r : α → β → Prop) : Option α → Option β → Prop
|
||||
| none, some _ => True
|
||||
| some x, some y => r x y
|
||||
| _, _ => False
|
||||
| none, some _ => True
|
||||
| some x, some y => r x y
|
||||
| _, _ => False
|
||||
|
||||
@[inline] protected def le (r : α → β → Prop) : Option α → Option β → Prop
|
||||
| none, some _ => True
|
||||
| none, none => True
|
||||
| some _, none => False
|
||||
| some x, some y => r x y
|
||||
|
||||
instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
|
||||
| none, some _ => isTrue trivial
|
||||
@@ -107,8 +201,20 @@ instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt
|
||||
| some _, none => isFalse not_false
|
||||
| none, none => isFalse not_false
|
||||
|
||||
/-- Take a pair of options and if they are both `some`, apply the given fn to produce an output.
|
||||
Otherwise act like `orElse`. -/
|
||||
/--
|
||||
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
|
||||
it is returned and the function is not used.
|
||||
|
||||
The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
|
||||
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
|
||||
both are `none`, then the value is `none`.
|
||||
|
||||
Examples:
|
||||
* `Option.merge (· + ·) none (some 3) = some 3`
|
||||
* `Option.merge (· + ·) (some 2) (some 3) = some 5`
|
||||
* `Option.merge (· + ·) (some 2) none = some 2`
|
||||
* `Option.merge (· + ·) none none = none`
|
||||
-/
|
||||
def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
| none , none => none
|
||||
| some x, none => some x
|
||||
@@ -125,12 +231,26 @@ def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
@[simp] theorem some_bind (a) (f : α → Option β) : (some a).bind f = f a := rfl
|
||||
|
||||
|
||||
/-- An elimination principle for `Option`. It is a nondependent version of `Option.recOn`. -/
|
||||
/--
|
||||
A case analysis function for `Option`.
|
||||
|
||||
Given a value for `none` and a function to apply to the contents of `some`, `Option.elim` checks
|
||||
which constructor a given `Option` consists of, and uses the appropriate argument.
|
||||
|
||||
`Option.elim` is an elimination principle for `Option`. In particular, it is a non-dependent version
|
||||
of `Option.recOn`. It can also be seen as a combination of `Option.map` and `Option.getD`.
|
||||
|
||||
Examples:
|
||||
* `(some "hello").elim 0 String.length = 5`
|
||||
* `none.elim 0 String.length = 0`
|
||||
-/
|
||||
@[inline] protected def elim : Option α → β → (α → β) → β
|
||||
| some x, _, f => f x
|
||||
| none, y, _ => y
|
||||
|
||||
/-- Extracts the value `a` from an option that is known to be `some a` for some `a`. -/
|
||||
/--
|
||||
Extracts the value from an option that can be proven to be `some`.
|
||||
-/
|
||||
@[inline] def get {α : Type u} : (o : Option α) → isSome o → α
|
||||
| some x, _ => x
|
||||
|
||||
@@ -138,27 +258,54 @@ def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
| some _, _ => rfl
|
||||
@[simp] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl
|
||||
|
||||
/-- `guard p a` returns `some a` if `p a` holds, otherwise `none`. -/
|
||||
/--
|
||||
Returns `none` if a value doesn't satisfy a predicate, or the value itself otherwise.
|
||||
|
||||
From the perspective of `Option` as computations that might fail, this function is a run-time
|
||||
assertion operator in the `Option` monad.
|
||||
|
||||
Examples:
|
||||
* `Option.guard (· > 2) 1 = none`
|
||||
* `Option.guard (· > 2) 5 = some 5`
|
||||
-/
|
||||
@[inline] def guard (p : α → Prop) [DecidablePred p] (a : α) : Option α :=
|
||||
if p a then some a else none
|
||||
|
||||
/--
|
||||
Cast of `Option` to `List`. Returns `[a]` if the input is `some a`, and `[]` if it is `none`.
|
||||
Converts an optional value to a list with zero or one element.
|
||||
|
||||
Examples:
|
||||
* `(some "value").toList = ["value"]`
|
||||
* `none.toList = []`
|
||||
-/
|
||||
@[inline] def toList : Option α → List α
|
||||
| none => .nil
|
||||
| some a => .cons a .nil
|
||||
|
||||
/--
|
||||
Cast of `Option` to `Array`. Returns `#[a]` if the input is `some a`, and `#[]` if it is `none`.
|
||||
Converts an optional value to an array with zero or one element.
|
||||
|
||||
Examples:
|
||||
* `(some "value").toArray = #["value"]`
|
||||
* `none.toArray = #[]`
|
||||
-/
|
||||
@[inline] def toArray : Option α → Array α
|
||||
| none => List.toArray .nil
|
||||
| some a => List.toArray (.cons a .nil)
|
||||
|
||||
/--
|
||||
Two arguments failsafe function. Returns `f a b` if the inputs are `some a` and `some b`, and
|
||||
"does nothing" otherwise.
|
||||
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
|
||||
it is returned and the function is not used.
|
||||
|
||||
The value is `some (f a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
|
||||
equivalent to `Option.orElse`. If only one input is `some x`, then the value is `some x`. If both
|
||||
are `none`, then the value is `none`.
|
||||
|
||||
Examples:
|
||||
* `Option.liftOrGet (· + ·) none (some 3) = some 3`
|
||||
* `Option.liftOrGet (· + ·) (some 2) (some 3) = some 5`
|
||||
* `Option.liftOrGet (· + ·) (some 2) none = some 2`
|
||||
* `Option.liftOrGet (· + ·) none none = none`
|
||||
-/
|
||||
def liftOrGet (f : α → α → α) : Option α → Option α → Option α
|
||||
| none, none => none
|
||||
@@ -174,28 +321,69 @@ inductive Rel (r : α → β → Prop) : Option α → Option β → Prop
|
||||
/-- `none ~ none` -/
|
||||
| none : Rel r none none
|
||||
|
||||
/-- Flatten an `Option` of `Option`, a specialization of `joinM`. -/
|
||||
/--
|
||||
Flattens nested optional values, preserving any value found.
|
||||
|
||||
This is analogous to `List.flatten`.
|
||||
|
||||
Examples:
|
||||
* `none.join = none`
|
||||
* `(some none).join = none`
|
||||
* `(some (some v)).join = some v`
|
||||
-/
|
||||
@[simp, inline] def join (x : Option (Option α)) : Option α := x.bind id
|
||||
|
||||
/-- Like `Option.mapM` but for applicative functors. -/
|
||||
/--
|
||||
Applies a function in some applicative functor to an optional value, returning `none` with no
|
||||
effects if the value is missing.
|
||||
|
||||
This is analogous to `Option.mapM` for monads.
|
||||
-/
|
||||
@[inline] protected def mapA [Applicative m] {α β} (f : α → m β) : Option α → m (Option β)
|
||||
| none => pure none
|
||||
| some x => some <$> f x
|
||||
|
||||
/--
|
||||
If you maybe have a monadic computation in a `[Monad m]` which produces a term of type `α`, then
|
||||
there is a naturally associated way to always perform a computation in `m` which maybe produces a
|
||||
result.
|
||||
Converts an optional monadic computation into a monadic computation of an optional value.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval show IO (Option String) from
|
||||
Option.sequence <| some do
|
||||
IO.println "hello"
|
||||
return "world"
|
||||
```
|
||||
```output
|
||||
hello
|
||||
```
|
||||
```output
|
||||
some "world"
|
||||
```
|
||||
-/
|
||||
@[inline] def sequence [Monad m] {α : Type u} : Option (m α) → m (Option α)
|
||||
| none => pure none
|
||||
| some fn => some <$> fn
|
||||
|
||||
/-- A monadic analogue of `Option.elim`. -/
|
||||
|
||||
/--
|
||||
A monadic case analysis function for `Option`.
|
||||
|
||||
Given a fallback computation for `none` and a monadic operation to apply to the contents of `some`,
|
||||
`Option.elimM` checks which constructor a given `Option` consists of, and uses the appropriate
|
||||
argument.
|
||||
|
||||
`Option.elimM` can also be seen as a combination of `Option.mapM` and `Option.getDM`. It is a
|
||||
monadic analogue of `Option.elim`.
|
||||
-/
|
||||
@[inline] def elimM [Monad m] (x : m (Option α)) (y : m β) (z : α → m β) : m β :=
|
||||
do (← x).elim y z
|
||||
|
||||
/-- A monadic analogue of `Option.getD`. -/
|
||||
/--
|
||||
Gets the value in an option, monadically computing a default value on `none`.
|
||||
|
||||
This is the monadic analogue of `Option.getD`.
|
||||
-/
|
||||
|
||||
@[inline] def getDM [Monad m] (x : Option α) (y : m α) : m α :=
|
||||
match x with
|
||||
| some a => pure a
|
||||
@@ -217,21 +405,45 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
@[simp] theorem any_none : Option.any p none = false := rfl
|
||||
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
|
||||
|
||||
/-- The minimum of two optional values. -/
|
||||
/--
|
||||
The minimum of two optional values, with `none` treated as the least element. This function is
|
||||
usually accessed through the `Min (Option α)` instance, rather than directly.
|
||||
|
||||
Prior to `nightly-2025-02-27`, `none` was treated as the greatest element, so
|
||||
`min none (some x) = min (some x) none = some x`.
|
||||
|
||||
Examples:
|
||||
* `Option.min (some 2) (some 5) = some 2`
|
||||
* `Option.min (some 5) (some 2) = some 2`
|
||||
* `Option.min (some 2) none = none`
|
||||
* `Option.min none (some 5) = none`
|
||||
* `Option.min none none = none`
|
||||
-/
|
||||
protected def min [Min α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Min.min x y)
|
||||
| some x, none => some x
|
||||
| none, some y => some y
|
||||
| some _, none => none
|
||||
| none, some _ => none
|
||||
| none, none => none
|
||||
|
||||
instance [Min α] : Min (Option α) where min := Option.min
|
||||
|
||||
@[simp] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = some a := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = some b := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = none := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = none := rfl
|
||||
@[simp] theorem min_none_none [Min α] : min (none : Option α) none = none := rfl
|
||||
|
||||
/-- The maximum of two optional values. -/
|
||||
/--
|
||||
The maximum of two optional values.
|
||||
|
||||
This function is usually accessed through the `Max (Option α)` instance, rather than directly.
|
||||
|
||||
Examples:
|
||||
* `Option.max (some 2) (some 5) = some 5`
|
||||
* `Option.max (some 5) (some 2) = some 5`
|
||||
* `Option.max (some 2) none = some 2`
|
||||
* `Option.max none (some 5) = some 5`
|
||||
* `Option.max none none = none`
|
||||
-/
|
||||
protected def max [Max α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Max.max x y)
|
||||
| some x, none => some x
|
||||
@@ -251,6 +463,9 @@ end Option
|
||||
instance [LT α] : LT (Option α) where
|
||||
lt := Option.lt (· < ·)
|
||||
|
||||
instance [LE α] : LE (Option α) where
|
||||
le := Option.le (· ≤ ·)
|
||||
|
||||
@[always_inline]
|
||||
instance : Functor Option where
|
||||
map := Option.map
|
||||
@@ -269,6 +484,15 @@ def liftOption [Alternative m] : Option α → m α
|
||||
| some a => pure a
|
||||
| none => failure
|
||||
|
||||
/--
|
||||
Recover from failing `Option` computations with a handler function.
|
||||
|
||||
This function is usually accessed through the `MonadExceptOf Unit Option` instance.
|
||||
|
||||
Examples:
|
||||
* `Option.tryCatch none (fun () => some "handled") = some "handled"`
|
||||
* `Option.tryCatch (some "succeeded") (fun () => some "handled") = some "succeeded"`
|
||||
-/
|
||||
@[always_inline, inline] protected def Option.tryCatch (x : Option α) (handle : Unit → Option α) : Option α :=
|
||||
match x with
|
||||
| some _ => x
|
||||
|
||||
@@ -11,6 +11,9 @@ universe u
|
||||
|
||||
namespace Option
|
||||
|
||||
/--
|
||||
Extracts the value from an `Option`, panicking on `none`.
|
||||
-/
|
||||
@[inline] def get! {α : Type u} [Inhabited α] : Option α → α
|
||||
| some x => x
|
||||
| none => panic! "value is none"
|
||||
|
||||
@@ -32,9 +32,12 @@ instance [DecidableEq α] (j : α) (o : Option α) : Decidable (j ∈ o) :=
|
||||
theorem some_inj {a b : α} : some a = some b ↔ a = b := by simp; rfl
|
||||
|
||||
/--
|
||||
`o = none` is decidable even if the wrapped type does not have decidable equality.
|
||||
This is not an instance because it is not definitionally equal to `instance : DecidableEq Option`.
|
||||
Try to use `o.isNone` or `o.isSome` instead.
|
||||
Equality with `none` is decidable even if the wrapped type does not have decidable equality.
|
||||
|
||||
This is not an instance because it is not definitionally equal to the standard instance of
|
||||
`DecidableEq (Option α)`, which can cause problems. It can be locally bound if needed.
|
||||
|
||||
Try to use the Boolean comparisons `Option.isNone` or `Option.isSome` instead.
|
||||
-/
|
||||
@[inline] def decidable_eq_none {o : Option α} : Decidable (o = none) :=
|
||||
decidable_of_decidable_of_iff isNone_iff_eq_none
|
||||
@@ -50,34 +53,115 @@ instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (Exi
|
||||
| some a => if h : p a then isTrue ⟨_, rfl, h⟩ else isFalse fun ⟨_, ⟨rfl, hn⟩⟩ => h hn
|
||||
|
||||
/--
|
||||
Partial bind. If for some `x : Option α`, `f : Π (a : α), a ∈ x → Option β` is a
|
||||
partial function defined on `a : α` giving an `Option β`, where `some a = x`,
|
||||
then `pbind x f h` is essentially the same as `bind x f`
|
||||
but is defined only when all `x = some a`, using the proof to apply `f`.
|
||||
Given an optional value and a function that can be applied when the value is `some`, returns the
|
||||
result of applying the function if this is possible.
|
||||
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
|
||||
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
|
||||
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
|
||||
behavior is equivalent to that of `Option.bind`.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
v.pbind fun x h => some ⟨x, h⟩
|
||||
```
|
||||
```lean example
|
||||
#reduce attach (some 3)
|
||||
```
|
||||
```output
|
||||
some ⟨3, ⋯⟩
|
||||
```
|
||||
```lean example
|
||||
#reduce attach none
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def pbind : ∀ x : Option α, (∀ a : α, a ∈ x → Option β) → Option β
|
||||
def pbind : (o : Option α) → (f : (a : α) → a ∈ o → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a rfl
|
||||
|
||||
/--
|
||||
Partial map. If `f : Π a, p a → β` is a partial function defined on `a : α` satisfying `p`,
|
||||
then `pmap f x h` is essentially the same as `map f x` but is defined only when all members of `x`
|
||||
satisfy `p`, using the proof to apply `f`.
|
||||
Given a function from the elements of `α` that satisfy `p` to `β` and a proof that an optional value
|
||||
satisfies `p` if it's present, applies the function to the value.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
v.pmap (fun a (h : a ∈ v) => ⟨_, h⟩) (fun _ h => h)
|
||||
```
|
||||
```lean example
|
||||
#reduce attach (some 3)
|
||||
```
|
||||
```output
|
||||
some ⟨3, ⋯⟩
|
||||
```
|
||||
```lean example
|
||||
#reduce attach none
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline] def pmap {p : α → Prop} (f : ∀ a : α, p a → β) :
|
||||
∀ x : Option α, (∀ a, a ∈ x → p a) → Option β
|
||||
@[inline] def pmap {p : α → Prop}
|
||||
(f : ∀ a : α, p a → β) :
|
||||
(o : Option α) → (∀ a, a ∈ o → p a) → Option β
|
||||
| none, _ => none
|
||||
| some a, H => f a (H a rfl)
|
||||
|
||||
/-- Partial elimination. If `o : Option α` and `f : (a : α) → a ∈ o → β`, then `o.pelim b f` is
|
||||
the same as `o.elim b f` but `f` is passed the proof that `a ∈ o`. -/
|
||||
/--
|
||||
Given an optional value and a function that can be applied when the value is `some`, returns the
|
||||
result of applying the function if this is possible, or a fallback value otherwise.
|
||||
|
||||
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
|
||||
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
|
||||
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
|
||||
behavior is equivalent to that of `Option.elim`.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def attach (v : Option α) : Option { y : α // y ∈ v } :=
|
||||
v.pelim none fun x h => some ⟨x, h⟩
|
||||
```
|
||||
```lean example
|
||||
#reduce attach (some 3)
|
||||
```
|
||||
```output
|
||||
some ⟨3, ⋯⟩
|
||||
```
|
||||
```lean example
|
||||
#reduce attach none
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) → a ∈ o → β) : β :=
|
||||
match o with
|
||||
| none => b
|
||||
| some a => f a rfl
|
||||
|
||||
/-- Map a monadic function which returns `Unit` over an `Option`. -/
|
||||
/--
|
||||
Executes a monadic action on an optional value if it is present, or does nothing if there is no
|
||||
value.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval ((some 5).forM set : StateM Nat Unit).run 0
|
||||
```
|
||||
```output
|
||||
((), 5)
|
||||
```
|
||||
```lean example
|
||||
#eval (none.forM (fun x : Nat => set x) : StateM Nat Unit).run 0
|
||||
```
|
||||
```output
|
||||
((), 0)
|
||||
```
|
||||
-/
|
||||
@[inline] protected def forM [Pure m] : Option α → (α → m PUnit) → m PUnit
|
||||
| none , _ => pure ⟨⟩
|
||||
| some a, f => f a
|
||||
|
||||
@@ -352,7 +352,12 @@ section choice
|
||||
|
||||
attribute [local instance] Classical.propDecidable
|
||||
|
||||
/-- An arbitrary `some a` with `a : α` if `α` is nonempty, and otherwise `none`. -/
|
||||
/--
|
||||
An optional arbitrary element of a given type.
|
||||
|
||||
If `α` is non-empty, then there exists some `v : α` and this arbitrary element is `some v`.
|
||||
Otherwise, it is `none`.
|
||||
-/
|
||||
noncomputable def choice (α : Type _) : Option α :=
|
||||
if h : Nonempty α then some (Classical.choice h) else none
|
||||
|
||||
@@ -673,4 +678,80 @@ theorem pmap_map (o : Option α) (f : α → β) {p : β → Prop} (g : ∀ b, p
|
||||
o.pelim g (fun a h => g' (f a (H a h))) := by
|
||||
cases o <;> simp
|
||||
|
||||
/-! ### LT and LE -/
|
||||
|
||||
@[simp] theorem not_lt_none [LT α] {a : Option α} : ¬ a < none := by cases a <;> simp [LT.lt, Option.lt]
|
||||
@[simp] theorem none_lt_some [LT α] {a : α} : none < some a := by simp [LT.lt, Option.lt]
|
||||
@[simp] theorem some_lt_some [LT α] {a b : α} : some a < some b ↔ a < b := by simp [LT.lt, Option.lt]
|
||||
|
||||
@[simp] theorem none_le [LE α] {a : Option α} : none ≤ a := by cases a <;> simp [LE.le, Option.le]
|
||||
@[simp] theorem not_some_le_none [LE α] {a : α} : ¬ some a ≤ none := by simp [LE.le, Option.le]
|
||||
@[simp] theorem some_le_some [LE α] {a b : α} : some a ≤ some b ↔ a ≤ b := by simp [LE.le, Option.le]
|
||||
|
||||
/-! ### min and max -/
|
||||
|
||||
theorem min_eq_left [LE α] [Min α] (min_eq_left : ∀ x y : α, x ≤ y → min x y = x)
|
||||
{a b : Option α} (h : a ≤ b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right [LE α] [Min α] (min_eq_right : ∀ x y : α, y ≤ x → min x y = y)
|
||||
{a b : Option α} (h : b ≤ a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_left_of_lt [LT α] [Min α] (min_eq_left : ∀ x y : α, x < y → min x y = x)
|
||||
{a b : Option α} (h : a < b) : min a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_right_of_lt [LT α] [Min α] (min_eq_right : ∀ x y : α, y < x → min x y = y)
|
||||
{a b : Option α} (h : b < a) : min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_eq_or [LE α] [Min α] (min_eq_or : ∀ x y : α, min x y = x ∨ min x y = y)
|
||||
{a b : Option α} : min a b = a ∨ min a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_left [LE α] [Min α] (min_le_left : ∀ x y : α, min x y ≤ x)
|
||||
{a b : Option α} : min a b ≤ a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem min_le_right [LE α] [Min α] (min_le_right : ∀ x y : α, min x y ≤ y)
|
||||
{a b : Option α} : min a b ≤ b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem le_min [LE α] [Min α] (le_min : ∀ x y z : α, x ≤ min y z ↔ x ≤ y ∧ x ≤ z)
|
||||
{a b c : Option α} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
theorem max_eq_left [LE α] [Max α] (max_eq_left : ∀ x y : α, x ≤ y → max x y = y)
|
||||
{a b : Option α} (h : a ≤ b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right [LE α] [Max α] (max_eq_right : ∀ x y : α, y ≤ x → max x y = x)
|
||||
{a b : Option α} (h : b ≤ a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_left_of_lt [LT α] [Max α] (max_eq_left : ∀ x y : α, x < y → max x y = y)
|
||||
{a b : Option α} (h : a < b) : max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_right_of_lt [LT α] [Max α] (max_eq_right : ∀ x y : α, y < x → max x y = x)
|
||||
{a b : Option α} (h : b < a) : max a b = a := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_eq_or [LE α] [Max α] (max_eq_or : ∀ x y : α, max x y = x ∨ max x y = y)
|
||||
{a b : Option α} : max a b = a ∨ max a b = b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem left_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (left_le_max : ∀ x y : α, x ≤ max x y)
|
||||
{a b : Option α} : a ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem right_le_max [LE α] [Max α] (le_refl : ∀ x : α, x ≤ x) (right_le_max : ∀ x y : α, y ≤ max x y)
|
||||
{a b : Option α} : b ≤ max a b := by
|
||||
cases a <;> cases b <;> simp_all
|
||||
|
||||
theorem max_le [LE α] [Max α] (max_le : ∀ x y z : α, max x y ≤ z ↔ x ≤ z ∧ y ≤ z)
|
||||
{a b c : Option α} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := by
|
||||
cases a <;> cases b <;> cases c <;> simp_all
|
||||
|
||||
end Option
|
||||
|
||||
@@ -46,7 +46,7 @@ theorem forIn'_eq_pelim [Monad m] [LawfulMonad m]
|
||||
o.pelim (pure b) (fun a h => g a h b <$> f a h b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → a ∈ o → β → β) (b : β) :
|
||||
forIn' o b (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (o.pelim b (fun a h => f a h b)) := by
|
||||
@@ -75,7 +75,7 @@ theorem forIn_eq_elim [Monad m] [LawfulMonad m]
|
||||
o.elim (pure b) (fun a => g a b <$> f a b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
|
||||
@[simp] theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
|
||||
(o : Option α) (f : (a : α) → β → β) (b : β) :
|
||||
forIn o b (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (o.elim b (fun a => f a b)) := by
|
||||
|
||||
@@ -8,35 +8,81 @@ prelude
|
||||
import Init.Data.String
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
/--
|
||||
The result of a comparison according to a total order.
|
||||
|
||||
The relationship between the compared items may be:
|
||||
* `Ordering.lt`: less than
|
||||
* `Ordering.eq`: equal
|
||||
* `Ordering.gt`: greater than
|
||||
-/
|
||||
inductive Ordering where
|
||||
| lt | eq | gt
|
||||
| /-- Less than. -/
|
||||
lt
|
||||
| /-- Equal. -/
|
||||
eq
|
||||
| /-- Greater than. -/
|
||||
gt
|
||||
deriving Inhabited, DecidableEq
|
||||
|
||||
namespace Ordering
|
||||
|
||||
/-- Swaps less and greater ordering results -/
|
||||
/--
|
||||
Swaps less-than and greater-than ordering results.
|
||||
|
||||
Examples:
|
||||
* `Ordering.lt.swap = Ordering.gt`
|
||||
* `Ordering.eq.swap = Ordering.eq`
|
||||
* `Ordering.gt.swap = Ordering.lt`
|
||||
-/
|
||||
def swap : Ordering → Ordering
|
||||
| .lt => .gt
|
||||
| .eq => .eq
|
||||
| .gt => .lt
|
||||
|
||||
/--
|
||||
If `o₁` and `o₂` are `Ordering`, then `o₁.then o₂` returns `o₁` unless it is `.eq`,
|
||||
in which case it returns `o₂`. Additionally, it has "short-circuiting" semantics similar to
|
||||
boolean `x && y`: if `o₁` is not `.eq` then the expression for `o₂` is not evaluated.
|
||||
This is a useful primitive for constructing lexicographic comparator functions:
|
||||
```
|
||||
If `a` and `b` are `Ordering`, then `a.then b` returns `a` unless it is `.eq`, in which case it
|
||||
returns `b`. Additionally, it has “short-circuiting” behavior similar to boolean `&&`: if `a` is not
|
||||
`.eq` then the expression for `b` is not evaluated.
|
||||
|
||||
This is a useful primitive for constructing lexicographic comparator functions. The `deriving Ord`
|
||||
syntax on a structure uses the `Ord` instance to compare each field in order, combining the results
|
||||
equivalently to `Ordering.then`.
|
||||
|
||||
Use `compareLex` to lexicographically combine two comparison functions.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
structure Person where
|
||||
name : String
|
||||
age : Nat
|
||||
|
||||
-- Sort people first by name (in ascending order), and people with the same name by age (in
|
||||
-- descending order)
|
||||
instance : Ord Person where
|
||||
compare a b := (compare a.name b.name).then (compare b.age a.age)
|
||||
```
|
||||
This example will sort people first by name (in ascending order) and will sort people with
|
||||
the same name by age (in descending order). (If all fields are sorted ascending and in the same
|
||||
order as they are listed in the structure, you can also use `deriving Ord` on the structure
|
||||
definition for the same effect.)
|
||||
|
||||
```lean example
|
||||
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Dana", 50⟩
|
||||
```
|
||||
```output
|
||||
Ordering.gt
|
||||
```
|
||||
|
||||
```lean example
|
||||
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 50⟩
|
||||
```
|
||||
```output
|
||||
Ordering.gt
|
||||
```
|
||||
|
||||
```lean example
|
||||
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 20⟩
|
||||
```
|
||||
```output
|
||||
Ordering.lt
|
||||
```
|
||||
-/
|
||||
@[macro_inline] def «then» (a b : Ordering) : Ordering :=
|
||||
match a with
|
||||
@@ -44,42 +90,42 @@ definition for the same effect.)
|
||||
| a => a
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'equal'.
|
||||
Checks whether the ordering is `eq`.
|
||||
-/
|
||||
def isEq : Ordering → Bool
|
||||
| eq => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'not equal'.
|
||||
Checks whether the ordering is not `eq`.
|
||||
-/
|
||||
def isNe : Ordering → Bool
|
||||
| eq => false
|
||||
| _ => true
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'less than or equal to'.
|
||||
Checks whether the ordering is `lt` or `eq`.
|
||||
-/
|
||||
def isLE : Ordering → Bool
|
||||
| gt => false
|
||||
| _ => true
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'less than'.
|
||||
Checks whether the ordering is `lt`.
|
||||
-/
|
||||
def isLT : Ordering → Bool
|
||||
| lt => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'greater than'.
|
||||
Checks whether the ordering is `gt`.
|
||||
-/
|
||||
def isGT : Ordering → Bool
|
||||
| gt => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'greater than or equal'.
|
||||
Checks whether the ordering is `gt` or `eq`.
|
||||
-/
|
||||
def isGE : Ordering → Bool
|
||||
| lt => false
|
||||
@@ -263,8 +309,12 @@ end Lemmas
|
||||
end Ordering
|
||||
|
||||
/--
|
||||
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
|
||||
`x = y` corresponds to `Ordering.eq`.
|
||||
Uses decidable less-than and equality relations to find an `Ordering`.
|
||||
|
||||
In particular, if `x < y` then the result is `Ordering.lt`. If `x = y` then the result is
|
||||
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
|
||||
|
||||
`compareOfLessAndBEq` uses `BEq` instead of `DecidableEq`.
|
||||
-/
|
||||
@[inline] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
|
||||
if x < y then Ordering.lt
|
||||
@@ -272,8 +322,12 @@ Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` a
|
||||
else Ordering.gt
|
||||
|
||||
/--
|
||||
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
|
||||
`x == y` corresponds to `Ordering.eq`.
|
||||
Uses a decidable less-than relation and Boolean equality to find an `Ordering`.
|
||||
|
||||
In particular, if `x < y` then the result is `Ordering.lt`. If `x == y` then the result is
|
||||
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
|
||||
|
||||
`compareOfLessAndEq` uses `DecidableEq` instead of `BEq`.
|
||||
-/
|
||||
@[inline] def compareOfLessAndBEq {α} (x y : α) [LT α] [Decidable (x < y)] [BEq α] : Ordering :=
|
||||
if x < y then .lt
|
||||
@@ -281,9 +335,12 @@ Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` a
|
||||
else .gt
|
||||
|
||||
/--
|
||||
Compare `a` and `b` lexicographically by `cmp₁` and `cmp₂`. `a` and `b` are
|
||||
first compared by `cmp₁`. If this returns 'equal', `a` and `b` are compared
|
||||
Compares `a` and `b` lexicographically by `cmp₁` and `cmp₂`.
|
||||
|
||||
`a` and `b` are first compared by `cmp₁`. If this returns `Ordering.eq`, `a` and `b` are compared
|
||||
by `cmp₂` to break the tie.
|
||||
|
||||
To lexicographically combine two `Ordering`s, use `Ordering.then`.
|
||||
-/
|
||||
@[inline] def compareLex (cmp₁ cmp₂ : α → β → Ordering) (a : α) (b : β) : Ordering :=
|
||||
(cmp₁ a b).then (cmp₂ a b)
|
||||
@@ -306,7 +363,14 @@ export Ord (compare)
|
||||
|
||||
set_option linter.unusedVariables false in -- allow specifying `ord` explicitly
|
||||
/--
|
||||
Compare `x` and `y` by comparing `f x` and `f y`.
|
||||
Compares two values by comparing the results of applying a function.
|
||||
|
||||
In particular, `x` is compared to `y` by comparing `f x` and `f y`.
|
||||
|
||||
Examples:
|
||||
* `compareOn (·.length) "apple" "banana" = .lt`
|
||||
* `compareOn (· % 3) 5 6 = .gt`
|
||||
* `compareOn (·.foldl max 0) [1, 2, 3] [3, 2, 1] = .eq`
|
||||
-/
|
||||
@[inline] def compareOn [ord : Ord β] (f : α → β) (x y : α) : Ordering :=
|
||||
compare (f x) (f y)
|
||||
@@ -358,12 +422,20 @@ instance [Ord α] : Ord (Option α) where
|
||||
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
|
||||
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
|
||||
|
||||
/--
|
||||
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare` is
|
||||
`Ordering.lt`.
|
||||
-/
|
||||
def ltOfOrd [Ord α] : LT α where
|
||||
lt a b := compare a b = Ordering.lt
|
||||
|
||||
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||||
|
||||
/--
|
||||
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
|
||||
satisfies `Ordering.isLE`.
|
||||
-/
|
||||
def leOfOrd [Ord α] : LE α where
|
||||
le a b := (compare a b).isLE
|
||||
|
||||
@@ -373,56 +445,70 @@ instance [Ord α] : DecidableRel (@LE.le α leOfOrd) :=
|
||||
namespace Ord
|
||||
|
||||
/--
|
||||
Derive a `BEq` instance from an `Ord` instance.
|
||||
Constructs a `BEq` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toBEq (ord : Ord α) : BEq α where
|
||||
beq x y := ord.compare x y == .eq
|
||||
|
||||
/--
|
||||
Derive an `LT` instance from an `Ord` instance.
|
||||
Constructs an `LT` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toLT (_ : Ord α) : LT α :=
|
||||
protected def toLT (ord : Ord α) : LT α :=
|
||||
ltOfOrd
|
||||
|
||||
instance [i : Ord α] : DecidableRel (@LT.lt _ (Ord.toLT i)) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||||
|
||||
/--
|
||||
Derive an `LE` instance from an `Ord` instance.
|
||||
Constructs an `LE` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toLE (_ : Ord α) : LE α :=
|
||||
protected def toLE (ord : Ord α) : LE α :=
|
||||
leOfOrd
|
||||
|
||||
instance [i : Ord α] : DecidableRel (@LE.le _ (Ord.toLE i)) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => (compare a b).isLE))
|
||||
|
||||
/--
|
||||
Invert the order of an `Ord` instance.
|
||||
Inverts the order of an `Ord` instance.
|
||||
|
||||
The result is an `Ord α` instance that returns `Ordering.lt` when `ord` would return `Ordering.gt`
|
||||
and that returns `Ordering.gt` when `ord` would return `Ordering.lt`.
|
||||
-/
|
||||
protected def opposite (ord : Ord α) : Ord α where
|
||||
compare x y := ord.compare y x
|
||||
|
||||
/--
|
||||
`ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
|
||||
Constructs an `Ord` instance that compares values according to the results of `f`.
|
||||
|
||||
In particular, `ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
|
||||
|
||||
The function `compareOn` can be used to perform this comparison without constructing an intermediate
|
||||
`Ord` instance.
|
||||
-/
|
||||
protected def on (_ : Ord β) (f : α → β) : Ord α where
|
||||
compare := compareOn f
|
||||
|
||||
/--
|
||||
Derive the lexicographic order on products `α × β` from orders for `α` and `β`.
|
||||
Constructs the lexicographic order on products `α × β` from orders for `α` and `β`.
|
||||
-/
|
||||
protected def lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
|
||||
lexOrd
|
||||
|
||||
/--
|
||||
Create an order which compares elements first by `ord₁` and then, if this
|
||||
returns 'equal', by `ord₂`.
|
||||
Constructs an `Ord` instance from two existing instances by combining them lexicographically.
|
||||
|
||||
The resulting instance compares elements first by `ord₁` and then, if this returns `Ordering.eq`, by
|
||||
`ord₂`.
|
||||
|
||||
The function `compareLex` can be used to perform this comparison without constructing an
|
||||
intermediate `Ord` instance. `Ordering.then` can be used to lexicographically combine the results of
|
||||
comparisons.
|
||||
-/
|
||||
protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
|
||||
compare := compareLex ord₁.compare ord₂.compare
|
||||
|
||||
/--
|
||||
Creates an order which compares elements of an `Array` in lexicographic order.
|
||||
Constructs an order which compares elements of an `Array` in lexicographic order.
|
||||
-/
|
||||
protected def arrayOrd [a : Ord α] : Ord (Array α) where
|
||||
compare x y :=
|
||||
|
||||
@@ -43,7 +43,13 @@ theorem map_map (f : α → β) (f' : γ → δ) (g : β → ε) (g' : δ → ζ
|
||||
Prod.map g g' (Prod.map f f' x) = Prod.map (g ∘ f) (g' ∘ f') x :=
|
||||
rfl
|
||||
|
||||
/-- Swap the factors of a product. `swap (a, b) = (b, a)` -/
|
||||
/--
|
||||
Swaps the elements in a pair.
|
||||
|
||||
Examples:
|
||||
* `(1, 2).swap = (2, 1)`
|
||||
* `("orange", -87).swap = (-87, "orange")`
|
||||
-/
|
||||
def swap : α × β → β × α := fun p => (p.2, p.1)
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -79,6 +79,12 @@ instance [Repr α] : Repr (ULift.{v} α) where
|
||||
instance : Repr Unit where
|
||||
reprPrec _ _ := "()"
|
||||
|
||||
/--
|
||||
Returns a representation of an optional value that should be able to be parsed as an equivalent
|
||||
optional value.
|
||||
|
||||
This function is typically accessed through the `Repr (Option α)` instance.
|
||||
-/
|
||||
protected def Option.repr [Repr α] : Option α → Nat → Format
|
||||
| none, _ => "none"
|
||||
| some a, prec => Repr.addAppParen ("some " ++ reprArg a) prec
|
||||
@@ -251,6 +257,14 @@ where
|
||||
let d1 := n % 16;
|
||||
hexDigitRepr d2 ++ hexDigitRepr d1
|
||||
|
||||
/--
|
||||
Quotes the character to its representation as a character literal, surrounded by single quotes and
|
||||
escaped as necessary.
|
||||
|
||||
Examples:
|
||||
* `'L'.quote = "'L'"`
|
||||
* `'"'.quote = "'\\\"'"`
|
||||
-/
|
||||
def Char.quote (c : Char) : String :=
|
||||
"'" ++ Char.quoteCore c ++ "'"
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import Init.Data.SInt.Basic
|
||||
import Init.Data.SInt.Float
|
||||
import Init.Data.SInt.Float32
|
||||
import Init.Data.SInt.Lemmas
|
||||
import Init.Data.SInt.Bitwise
|
||||
|
||||
/-!
|
||||
This module contains the definitions and basic theory about signed fixed width integer types.
|
||||
|
||||
@@ -77,6 +77,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
|
||||
-/
|
||||
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
|
||||
|
||||
theorem Int8.toBitVec.inj : {x y : Int8} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
|
||||
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
|
||||
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
|
||||
@@ -110,8 +113,8 @@ instance : ReprAtom Int8 := ⟨⟩
|
||||
instance : Hashable Int8 where
|
||||
hash i := i.toUInt8.toUInt64
|
||||
|
||||
instance : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance : Neg Int8 where
|
||||
instance Int8.instOfNat : OfNat Int8 n := ⟨Int8.ofNat n⟩
|
||||
instance Int8.instNeg : Neg Int8 where
|
||||
neg := Int8.neg
|
||||
|
||||
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
|
||||
@@ -189,6 +192,9 @@ instance : ShiftLeft Int8 := ⟨Int8.shiftLeft⟩
|
||||
instance : ShiftRight Int8 := ⟨Int8.shiftRight⟩
|
||||
instance : DecidableEq Int8 := Int8.decEq
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_int8"]
|
||||
def Bool.toInt8 (b : Bool) : Int8 := if b then 1 else 0
|
||||
|
||||
@@ -213,6 +219,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
|
||||
-/
|
||||
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
|
||||
|
||||
theorem Int16.toBitVec.inj : {x y : Int16} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
|
||||
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
|
||||
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
|
||||
@@ -250,8 +259,8 @@ instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : Hashable Int16 where
|
||||
hash i := i.toUInt16.toUInt64
|
||||
|
||||
instance : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance : Neg Int16 where
|
||||
instance Int16.instOfNat : OfNat Int16 n := ⟨Int16.ofNat n⟩
|
||||
instance Int16.instNeg : Neg Int16 where
|
||||
neg := Int16.neg
|
||||
|
||||
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
|
||||
@@ -329,6 +338,9 @@ instance : ShiftLeft Int16 := ⟨Int16.shiftLeft⟩
|
||||
instance : ShiftRight Int16 := ⟨Int16.shiftRight⟩
|
||||
instance : DecidableEq Int16 := Int16.decEq
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_int16"]
|
||||
def Bool.toInt16 (b : Bool) : Int16 := if b then 1 else 0
|
||||
|
||||
@@ -353,6 +365,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
|
||||
-/
|
||||
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
|
||||
|
||||
theorem Int32.toBitVec.inj : {x y : Int32} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
|
||||
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
|
||||
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
|
||||
@@ -387,15 +402,15 @@ def Int32.neg (i : Int32) : Int32 := ⟨⟨-i.toBitVec⟩⟩
|
||||
|
||||
instance : ToString Int32 where
|
||||
toString i := toString i.toInt
|
||||
instance : Repr Int16 where
|
||||
instance : Repr Int32 where
|
||||
reprPrec i prec := reprPrec i.toInt prec
|
||||
instance : ReprAtom Int16 := ⟨⟩
|
||||
instance : ReprAtom Int32 := ⟨⟩
|
||||
|
||||
instance : Hashable Int32 where
|
||||
hash i := i.toUInt32.toUInt64
|
||||
|
||||
instance : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance : Neg Int32 where
|
||||
instance Int32.instOfNat : OfNat Int32 n := ⟨Int32.ofNat n⟩
|
||||
instance Int32.instNeg : Neg Int32 where
|
||||
neg := Int32.neg
|
||||
|
||||
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
|
||||
@@ -473,6 +488,9 @@ instance : ShiftLeft Int32 := ⟨Int32.shiftLeft⟩
|
||||
instance : ShiftRight Int32 := ⟨Int32.shiftRight⟩
|
||||
instance : DecidableEq Int32 := Int32.decEq
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_int32"]
|
||||
def Bool.toInt32 (b : Bool) : Int32 := if b then 1 else 0
|
||||
|
||||
@@ -497,6 +515,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
|
||||
-/
|
||||
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
|
||||
|
||||
theorem Int64.toBitVec.inj : {x y : Int64} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
|
||||
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
|
||||
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
|
||||
@@ -542,8 +563,8 @@ instance : ReprAtom Int64 := ⟨⟩
|
||||
instance : Hashable Int64 where
|
||||
hash i := i.toUInt64
|
||||
|
||||
instance : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance : Neg Int64 where
|
||||
instance Int64.instOfNat : OfNat Int64 n := ⟨Int64.ofNat n⟩
|
||||
instance Int64.instNeg : Neg Int64 where
|
||||
neg := Int64.neg
|
||||
|
||||
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
|
||||
@@ -621,6 +642,9 @@ instance : ShiftLeft Int64 := ⟨Int64.shiftLeft⟩
|
||||
instance : ShiftRight Int64 := ⟨Int64.shiftRight⟩
|
||||
instance : DecidableEq Int64 := Int64.decEq
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_int64"]
|
||||
def Bool.toInt64 (b : Bool) : Int64 := if b then 1 else 0
|
||||
|
||||
@@ -645,6 +669,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
|
||||
-/
|
||||
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
|
||||
|
||||
theorem ISize.toBitVec.inj : {x y : ISize} → x.toBitVec = y.toBitVec → x = y
|
||||
| ⟨⟨_⟩⟩, ⟨⟨_⟩⟩, rfl => rfl
|
||||
|
||||
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
|
||||
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
|
||||
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
|
||||
@@ -700,8 +727,8 @@ instance : ReprAtom ISize := ⟨⟩
|
||||
instance : Hashable ISize where
|
||||
hash i := i.toUSize.toUInt64
|
||||
|
||||
instance : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance : Neg ISize where
|
||||
instance ISize.instOfNat : OfNat ISize n := ⟨ISize.ofNat n⟩
|
||||
instance ISize.instNeg : Neg ISize where
|
||||
neg := ISize.neg
|
||||
|
||||
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
|
||||
@@ -780,6 +807,9 @@ instance : ShiftLeft ISize := ⟨ISize.shiftLeft⟩
|
||||
instance : ShiftRight ISize := ⟨ISize.shiftRight⟩
|
||||
instance : DecidableEq ISize := ISize.decEq
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_isize"]
|
||||
def Bool.toISize (b : Bool) : ISize := if b then 1 else 0
|
||||
|
||||
|
||||
57
src/Init/Data/SInt/Bitwise.lean
Normal file
57
src/Init/Data/SInt/Bitwise.lean
Normal file
@@ -0,0 +1,57 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.SInt.Lemmas
|
||||
|
||||
set_option hygiene false in
|
||||
macro "declare_bitwise_int_theorems" typeName:ident bits:term:arg : command =>
|
||||
`(
|
||||
namespace $typeName
|
||||
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec.sdiv b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec.srem b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_not {a : $typeName} : (~~~a).toBitVec = ~~~a.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec.sshiftRight' (b.toBitVec.smod $bits) := rfl
|
||||
@[simp, int_toBitVec] protected theorem toBitVec_abs (a : $typeName) : a.abs.toBitVec = a.toBitVec.abs := rfl
|
||||
|
||||
end $typeName
|
||||
)
|
||||
declare_bitwise_int_theorems Int8 8
|
||||
declare_bitwise_int_theorems Int16 16
|
||||
declare_bitwise_int_theorems Int32 32
|
||||
declare_bitwise_int_theorems Int64 64
|
||||
declare_bitwise_int_theorems ISize System.Platform.numBits
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt8 {b : Bool} : b.toInt8.toBitVec = (BitVec.ofBool b).setWidth 8 := by
|
||||
cases b <;> simp [toInt8]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt16 {b : Bool} : b.toInt16.toBitVec = (BitVec.ofBool b).setWidth 16 := by
|
||||
cases b <;> simp [toInt16]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt32 {b : Bool} : b.toInt32.toBitVec = (BitVec.ofBool b).setWidth 32 := by
|
||||
cases b <;> simp [toInt32]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toInt64 {b : Bool} : b.toInt64.toBitVec = (BitVec.ofBool b).setWidth 64 := by
|
||||
cases b <;> simp [toInt64]
|
||||
|
||||
@[simp, int_toBitVec]
|
||||
theorem Bool.toBitVec_toISize {b : Bool} :
|
||||
b.toISize.toBitVec = (BitVec.ofBool b).setWidth System.Platform.numBits := by
|
||||
cases b
|
||||
· simp [toISize]
|
||||
· apply BitVec.eq_of_toNat_eq
|
||||
simp [toISize]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,14 @@ import Init.Data.Char.Basic
|
||||
|
||||
universe u
|
||||
|
||||
/--
|
||||
Creates a string that contains the characters in a list, in order.
|
||||
|
||||
Examples:
|
||||
* `['L', '∃', '∀', 'N'].asString = "L∃∀N"`
|
||||
* `[].asString = ""`
|
||||
* `['a', 'a', 'a'].asString = "aaa"`
|
||||
-/
|
||||
def List.asString (s : List Char) : String :=
|
||||
⟨s⟩
|
||||
|
||||
@@ -1169,6 +1177,13 @@ end String
|
||||
|
||||
namespace Char
|
||||
|
||||
/--
|
||||
Constructs a singleton string that contains only the provided character.
|
||||
|
||||
Examples:
|
||||
* `'L'.toString = "L"`
|
||||
* `'"'.toString = "\""`
|
||||
-/
|
||||
@[inline] protected def toString (c : Char) : String :=
|
||||
String.singleton c
|
||||
|
||||
|
||||
@@ -47,30 +47,30 @@ deriving instance BEq for Sum
|
||||
|
||||
section get
|
||||
|
||||
/-- Check if a sum is `inl`. -/
|
||||
/-- Checks whether a sum is the left injection `inl`. -/
|
||||
def isLeft : α ⊕ β → Bool
|
||||
| inl _ => true
|
||||
| inr _ => false
|
||||
|
||||
/-- Check if a sum is `inr`. -/
|
||||
/-- Checks whether a sum is the right injection `inr`. -/
|
||||
def isRight : α ⊕ β → Bool
|
||||
| inl _ => false
|
||||
| inr _ => true
|
||||
|
||||
/-- Retrieve the contents from a sum known to be `inl`.-/
|
||||
/-- Retrieves the contents from a sum known to be `inl`.-/
|
||||
def getLeft : (ab : α ⊕ β) → ab.isLeft → α
|
||||
| inl a, _ => a
|
||||
|
||||
/-- Retrieve the contents from a sum known to be `inr`.-/
|
||||
/-- Retrieves the contents from a sum known to be `inr`.-/
|
||||
def getRight : (ab : α ⊕ β) → ab.isRight → β
|
||||
| inr b, _ => b
|
||||
|
||||
/-- Check if a sum is `inl` and if so, retrieve its contents. -/
|
||||
/-- Checks whether a sum is the left injection `inl` and, if so, retrieves its contents. -/
|
||||
def getLeft? : α ⊕ β → Option α
|
||||
| inl a => some a
|
||||
| inr _ => none
|
||||
|
||||
/-- Check if a sum is `inr` and if so, retrieve its contents. -/
|
||||
/-- Checks whether a sum is the right injection `inr` and, if so, retrieves its contents. -/
|
||||
def getRight? : α ⊕ β → Option β
|
||||
| inr b => some b
|
||||
| inl _ => none
|
||||
@@ -90,7 +90,10 @@ def getRight? : α ⊕ β → Option β
|
||||
|
||||
end get
|
||||
|
||||
/-- Define a function on `α ⊕ β` by giving separate definitions on `α` and `β`. -/
|
||||
/--
|
||||
Case analysis for sums that applies the appropriate function `f` or `g` after checking which
|
||||
constructor is present.
|
||||
-/
|
||||
protected def elim {α β γ} (f : α → γ) (g : β → γ) : α ⊕ β → γ :=
|
||||
fun x => Sum.casesOn x f g
|
||||
|
||||
@@ -100,7 +103,11 @@ protected def elim {α β γ} (f : α → γ) (g : β → γ) : α ⊕ β → γ
|
||||
@[simp] theorem elim_inr (f : α → γ) (g : β → γ) (x : β) :
|
||||
Sum.elim f g (inr x) = g x := rfl
|
||||
|
||||
/-- Map `α ⊕ β` to `α' ⊕ β'` sending `α` to `α'` and `β` to `β'`. -/
|
||||
/--
|
||||
Transforms a sum according to functions on each type.
|
||||
|
||||
This function maps `α ⊕ β` to `α' ⊕ β'`, sending `α` to `α'` and `β` to `β'`.
|
||||
-/
|
||||
protected def map (f : α → α') (g : β → β') : α ⊕ β → α' ⊕ β' :=
|
||||
Sum.elim (inl ∘ f) (inr ∘ g)
|
||||
|
||||
@@ -108,7 +115,11 @@ protected def map (f : α → α') (g : β → β') : α ⊕ β → α' ⊕ β'
|
||||
|
||||
@[simp] theorem map_inr (f : α → α') (g : β → β') (x : β) : (inr x).map f g = inr (g x) := rfl
|
||||
|
||||
/-- Swap the factors of a sum type -/
|
||||
/--
|
||||
Swaps the factors of a sum type.
|
||||
|
||||
The constructor `Sum.inl` is replaced with `Sum.inr`, and vice versa.
|
||||
-/
|
||||
def swap : α ⊕ β → β ⊕ α := Sum.elim inr inl
|
||||
|
||||
@[simp] theorem swap_inl : swap (inl x : α ⊕ β) = inr x := rfl
|
||||
|
||||
@@ -40,6 +40,20 @@ instance {p : Prop} : ToString (Decidable p) := ⟨fun h =>
|
||||
| Decidable.isTrue _ => "true"
|
||||
| Decidable.isFalse _ => "false"⟩
|
||||
|
||||
/--
|
||||
Converts a list into a string, using `ToString.toString` to convert its elements.
|
||||
|
||||
The resulting string resembles list literal syntax, with the elements separated by `", "` and
|
||||
enclosed in square brackets.
|
||||
|
||||
The resulting string may not be valid Lean syntax, because there's no such expectation for
|
||||
`ToString` instances.
|
||||
|
||||
Examples:
|
||||
* `[1, 2, 3].toString = "[1, 2, 3]"`
|
||||
* `["cat", "dog"].toString = "[cat, dog]"`
|
||||
* `["cat", "dog", ""].toString = "[cat, dog, ]"`
|
||||
-/
|
||||
protected def List.toString [ToString α] : List α → String
|
||||
| [] => "[]"
|
||||
| [x] => "[" ++ toString x ++ "]"
|
||||
|
||||
@@ -65,6 +65,9 @@ instance : Xor UInt8 := ⟨UInt8.xor⟩
|
||||
instance : ShiftLeft UInt8 := ⟨UInt8.shiftLeft⟩
|
||||
instance : ShiftRight UInt8 := ⟨UInt8.shiftRight⟩
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_uint8"]
|
||||
def Bool.toUInt8 (b : Bool) : UInt8 := if b then 1 else 0
|
||||
|
||||
@@ -137,6 +140,9 @@ instance : Xor UInt16 := ⟨UInt16.xor⟩
|
||||
instance : ShiftLeft UInt16 := ⟨UInt16.shiftLeft⟩
|
||||
instance : ShiftRight UInt16 := ⟨UInt16.shiftRight⟩
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_uint16"]
|
||||
def Bool.toUInt16 (b : Bool) : UInt16 := if b then 1 else 0
|
||||
|
||||
@@ -211,6 +217,9 @@ instance : Xor UInt32 := ⟨UInt32.xor⟩
|
||||
instance : ShiftLeft UInt32 := ⟨UInt32.shiftLeft⟩
|
||||
instance : ShiftRight UInt32 := ⟨UInt32.shiftRight⟩
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_uint32"]
|
||||
def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
|
||||
|
||||
@@ -270,6 +279,9 @@ instance : Xor UInt64 := ⟨UInt64.xor⟩
|
||||
instance : ShiftLeft UInt64 := ⟨UInt64.shiftLeft⟩
|
||||
instance : ShiftRight UInt64 := ⟨UInt64.shiftRight⟩
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_uint64"]
|
||||
def Bool.toUInt64 (b : Bool) : UInt64 := if b then 1 else 0
|
||||
|
||||
@@ -376,6 +388,9 @@ instance : Xor USize := ⟨USize.xor⟩
|
||||
instance : ShiftLeft USize := ⟨USize.shiftLeft⟩
|
||||
instance : ShiftRight USize := ⟨USize.shiftRight⟩
|
||||
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
@[extern "lean_bool_to_usize"]
|
||||
def Bool.toUSize (b : Bool) : USize := if b then 1 else 0
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
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, François G. Dorais, Mario Carneiro, Mac Malone
|
||||
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone, Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.UInt.Basic
|
||||
@@ -27,7 +27,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
@[deprecated toNat_ofBitVec (since := "2025-02-12")]
|
||||
theorem toNat_mk : (ofBitVec a).toNat = a.toNat := rfl
|
||||
|
||||
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
|
||||
@[simp] theorem toNat_ofNat' {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
|
||||
|
||||
-- Not `simp` because we have simprocs which will avoid the modulo.
|
||||
theorem toNat_ofNat {n : Nat} : toNat (no_index (OfNat.ofNat n)) = n % 2 ^ $bits := toNat_ofNat'
|
||||
|
||||
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
|
||||
|
||||
@@ -55,11 +58,16 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
theorem mk_toBitVec_eq : ∀ (a : $typeName), ofBitVec a.toBitVec = a
|
||||
| ⟨_, _⟩ => rfl
|
||||
|
||||
@[deprecated "Use `toNat_toBitVec` and `toNat_ofNat_of_lt`." (since := "2025-03-05")]
|
||||
theorem toBitVec_eq_of_lt {a : Nat} : a < size → (ofNat a).toBitVec.toNat = a :=
|
||||
Nat.mod_eq_of_lt
|
||||
|
||||
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
rw [toNat, toBitVec_eq_of_lt h]
|
||||
theorem toBitVec_ofNat' (n : Nat) : (ofNat n).toBitVec = BitVec.ofNat _ n := rfl
|
||||
|
||||
theorem toNat_ofNat_of_lt' {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
|
||||
rw [toNat, toBitVec_ofNat', BitVec.toNat_ofNat, Nat.mod_eq_of_lt h]
|
||||
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : toNat (OfNat.ofNat n) = n :=
|
||||
toNat_ofNat_of_lt' h
|
||||
|
||||
@[int_toBitVec] theorem le_def {a b : $typeName} : a ≤ b ↔ a.toBitVec ≤ b.toBitVec := .rfl
|
||||
|
||||
@@ -151,10 +159,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
|
||||
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
|
||||
|
||||
open $typeName (toNat_mod toNat_lt_size) in
|
||||
protected theorem toNat_mod_lt {m : Nat} : ∀ (u : $typeName), m > 0 → toNat (u % ofNat m) < m := by
|
||||
protected theorem toNat_mod_lt {m : Nat} : ∀ (u : $typeName), 0 < m → toNat (u % ofNat m) < m := by
|
||||
intro u h1
|
||||
by_cases h2 : m < size
|
||||
· rw [toNat_mod, toNat_ofNat_of_lt h2]
|
||||
· rw [toNat_mod, toNat_ofNat_of_lt' h2]
|
||||
apply Nat.mod_lt _ h1
|
||||
· apply Nat.lt_of_lt_of_le
|
||||
· apply toNat_lt_size
|
||||
@@ -258,16 +266,20 @@ theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat
|
||||
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
|
||||
|
||||
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m → n.toNat < m := by
|
||||
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
|
||||
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
|
||||
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n → m < n.toNat := by
|
||||
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
|
||||
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
|
||||
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ≤ ofNat m → n.toNat ≤ m := by
|
||||
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
|
||||
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
|
||||
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m ≤ n → m ≤ n.toNat := by
|
||||
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
|
||||
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
|
||||
exact id
|
||||
|
||||
@[simp] theorem UInt8.toNat_lt (n : UInt8) : n.toNat < 2 ^ 8 := n.toFin.isLt
|
||||
@[simp] theorem UInt16.toNat_lt (n : UInt16) : n.toNat < 2 ^ 16 := n.toFin.isLt
|
||||
@@ -287,6 +299,8 @@ theorem UInt32.size_le_usizeSize : UInt32.size ≤ USize.size := by
|
||||
theorem USize.size_eq_two_pow : USize.size = 2 ^ System.Platform.numBits := rfl
|
||||
theorem USize.toNat_lt_two_pow_numBits (n : USize) : n.toNat < 2 ^ System.Platform.numBits := n.toFin.isLt
|
||||
@[simp] theorem USize.toNat_lt (n : USize) : n.toNat < 2 ^ 64 := Nat.lt_of_lt_of_le n.toFin.isLt size_le
|
||||
theorem USize.size_le_uint64Size : USize.size ≤ UInt64.size := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
theorem UInt8.toNat_lt_usizeSize (n : UInt8) : n.toNat < USize.size :=
|
||||
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
|
||||
@@ -309,6 +323,15 @@ theorem USize.size_dvd_uInt64Size : USize.size ∣ UInt64.size := by cases USize
|
||||
@[simp] theorem mod_uInt64Size_uSizeSize (n : Nat) : n % UInt64.size % USize.size = n % USize.size :=
|
||||
Nat.mod_mod_of_dvd _ USize.size_dvd_uInt64Size
|
||||
|
||||
@[simp] theorem USize.size_sub_one_mod_uint8Size : (USize.size - 1) % UInt8.size = UInt8.size - 1 := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
@[simp] theorem USize.size_sub_one_mod_uint16Size : (USize.size - 1) % UInt16.size = UInt16.size - 1 := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
@[simp] theorem USize.size_sub_one_mod_uint32Size : (USize.size - 1) % UInt32.size = UInt32.size - 1 := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
@[simp] theorem UInt64.size_sub_one_mod_uSizeSize : 18446744073709551615 % USize.size = USize.size - 1 := by
|
||||
cases USize.size_eq <;> simp_all +decide
|
||||
|
||||
@[simp] theorem UInt8.toNat_mod_size (n : UInt8) : n.toNat % UInt8.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
|
||||
@[simp] theorem UInt8.toNat_mod_uInt16Size (n : UInt8) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@[simp] theorem UInt8.toNat_mod_uInt32Size (n : UInt8) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
|
||||
@@ -373,7 +396,7 @@ theorem USize.size_dvd_uInt64Size : USize.size ∣ UInt64.size := by cases USize
|
||||
@[simp] theorem UInt32.toFin_toUSize (n : UInt32) :
|
||||
n.toUSize.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_usizeSize := rfl
|
||||
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_uint64Size := rfl
|
||||
|
||||
@[simp] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
@[simp] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
|
||||
@@ -783,3 +806,541 @@ theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
|
||||
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
|
||||
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
|
||||
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
|
||||
|
||||
@[simp] theorem UInt8.toNat_ofFin (x : Fin UInt8.size) : (UInt8.ofFin x).toNat = x.val := rfl
|
||||
@[simp] theorem UInt16.toNat_ofFin (x : Fin UInt16.size) : (UInt16.ofFin x).toNat = x.val := rfl
|
||||
@[simp] theorem UInt32.toNat_ofFin (x : Fin UInt32.size) : (UInt32.ofFin x).toNat = x.val := rfl
|
||||
@[simp] theorem UInt64.toNat_ofFin (x : Fin UInt64.size) : (UInt64.ofFin x).toNat = x.val := rfl
|
||||
@[simp] theorem USize.toNat_ofFin (x : Fin USize.size) : (USize.ofFin x).toNat = x.val := rfl
|
||||
|
||||
theorem UInt8.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toNat = n := by rw [UInt8.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
|
||||
theorem UInt16.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toNat = n := by rw [UInt16.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
|
||||
theorem UInt32.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toNat = n := by rw [UInt32.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
|
||||
theorem UInt64.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toNat = n := by rw [UInt64.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
|
||||
theorem USize.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toNat = n := by rw [USize.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
|
||||
|
||||
theorem UInt8.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toNat = UInt8.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
|
||||
theorem UInt16.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toNat = UInt16.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
|
||||
theorem UInt32.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toNat = UInt32.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
|
||||
theorem UInt64.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toNat = UInt64.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
|
||||
theorem USize.toNat_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toNat = USize.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
|
||||
|
||||
@[simp] theorem UInt8.toFin_ofNatLT {n : Nat} (hn) : (UInt8.ofNatLT n hn).toFin = ⟨n, hn⟩ := rfl
|
||||
@[simp] theorem UInt16.toFin_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toFin = ⟨n, hn⟩ := rfl
|
||||
@[simp] theorem UInt32.toFin_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toFin = ⟨n, hn⟩ := rfl
|
||||
@[simp] theorem UInt64.toFin_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toFin = ⟨n, hn⟩ := rfl
|
||||
@[simp] theorem USize.toFin_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toFin = ⟨n, hn⟩ := rfl
|
||||
|
||||
@[simp] theorem UInt8.toFin_ofNat' {n : Nat} : (UInt8.ofNat n).toFin = Fin.ofNat' _ n := rfl
|
||||
@[simp] theorem UInt16.toFin_ofNat' {n : Nat} : (UInt16.ofNat n).toFin = Fin.ofNat' _ n := rfl
|
||||
@[simp] theorem UInt32.toFin_ofNat' {n : Nat} : (UInt32.ofNat n).toFin = Fin.ofNat' _ n := rfl
|
||||
@[simp] theorem UInt64.toFin_ofNat' {n : Nat} : (UInt64.ofNat n).toFin = Fin.ofNat' _ n := rfl
|
||||
@[simp] theorem USize.toFin_ofNat' {n : Nat} : (USize.ofNat n).toFin = Fin.ofNat' _ n := rfl
|
||||
|
||||
@[simp] theorem UInt8.toFin_ofBitVec {b} : (UInt8.ofBitVec b).toFin = b.toFin := rfl
|
||||
@[simp] theorem UInt16.toFin_ofBitVec {b} : (UInt16.ofBitVec b).toFin = b.toFin := rfl
|
||||
@[simp] theorem UInt32.toFin_ofBitVec {b} : (UInt32.ofBitVec b).toFin = b.toFin := rfl
|
||||
@[simp] theorem UInt64.toFin_ofBitVec {b} : (UInt64.ofBitVec b).toFin = b.toFin := rfl
|
||||
@[simp] theorem USize.toFin_ofBitVec {b} : (USize.ofBitVec b).toFin = b.toFin := rfl
|
||||
|
||||
theorem UInt8.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toFin = ⟨n, hn⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt16.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toFin = ⟨n, hn⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt32.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toFin = ⟨n, hn⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt64.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toFin = ⟨n, hn⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem USize.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toFin = ⟨n, hn⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem UInt8.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toFin = ⟨UInt8.size - 1, by decide⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt16.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toFin = ⟨UInt16.size - 1, by decide⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt32.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toFin = ⟨UInt32.size - 1, by decide⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt64.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toFin = ⟨UInt64.size - 1, by decide⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem USize.toFin_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toFin = ⟨USize.size - 1, by cases USize.size_eq <;> simp_all⟩ :=
|
||||
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_ofNatLT {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt16.toBitVec_ofNatLT {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt32.toBitVec_ofNatLT {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt64.toBitVec_ofNatLT {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
|
||||
@[simp] theorem USize.toBitVec_ofNatLT {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_ofFin (n : Fin UInt8.size) : (UInt8.ofFin n).toBitVec = BitVec.ofFin n := rfl
|
||||
@[simp] theorem UInt16.toBitVec_ofFin (n : Fin UInt16.size) : (UInt16.ofFin n).toBitVec = BitVec.ofFin n := rfl
|
||||
@[simp] theorem UInt32.toBitVec_ofFin (n : Fin UInt32.size) : (UInt32.ofFin n).toBitVec = BitVec.ofFin n := rfl
|
||||
@[simp] theorem UInt64.toBitVec_ofFin (n : Fin UInt64.size) : (UInt64.ofFin n).toBitVec = BitVec.ofFin n := rfl
|
||||
@[simp] theorem USize.toBitVec_ofFin (n : Fin USize.size) : (USize.ofFin n).toBitVec = BitVec.ofFin n := rfl
|
||||
|
||||
@[simp] theorem UInt8.toBitVec_ofBitVec (n) : (UInt8.ofBitVec n).toBitVec = n := rfl
|
||||
@[simp] theorem UInt16.toBitVec_ofBitVec (n) : (UInt16.ofBitVec n).toBitVec = n := rfl
|
||||
@[simp] theorem UInt32.toBitVec_ofBitVec (n) : (UInt32.ofBitVec n).toBitVec = n := rfl
|
||||
@[simp] theorem UInt64.toBitVec_ofBitVec (n) : (UInt64.ofBitVec n).toBitVec = n := rfl
|
||||
@[simp] theorem USize.toBitVec_ofBitVec (n) : (USize.ofBitVec n).toBitVec = n := rfl
|
||||
|
||||
theorem UInt8.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt16.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt32.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt64.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem USize.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem UInt8.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt16.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt32.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt32.size - 1) (by decide) :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt64.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt64.size - 1) (by decide) :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem USize.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
|
||||
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
|
||||
@[simp] theorem UInt32.toUInt8_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
|
||||
@[simp] theorem UInt64.toUInt8_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
|
||||
@[simp] theorem USize.toUInt8_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_ofFin (n) : (UInt16.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
|
||||
@[simp] theorem UInt32.toUInt8_ofFin (n) : (UInt32.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
|
||||
@[simp] theorem UInt64.toUInt8_ofFin (n) : (UInt64.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
|
||||
@[simp] theorem USize.toUInt8_ofFin (n) : (USize.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_ofBitVec (b) : (UInt16.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt32.toUInt8_ofBitVec (b) : (UInt32.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt64.toUInt8_ofBitVec (b) : (UInt64.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem USize.toUInt8_ofBitVec (b) : (USize.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) :=
|
||||
UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_ofNat' (n : Nat) : (UInt16.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt32.toUInt8_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt8_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt8_ofNat' (n : Nat) : (USize.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt16.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
|
||||
@[simp] theorem UInt32.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
|
||||
@[simp] theorem UInt64.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
|
||||
@[simp] theorem USize.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
|
||||
|
||||
theorem UInt16.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
|
||||
theorem UInt32.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
|
||||
theorem UInt64.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
|
||||
theorem USize.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
|
||||
|
||||
theorem UInt16.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt32.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt64.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem USize.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
|
||||
@[simp] theorem UInt64.toUInt16_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
|
||||
@[simp] theorem USize.toUInt16_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_ofFin (n) : (UInt32.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
|
||||
@[simp] theorem UInt64.toUInt16_ofFin (n) : (UInt64.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
|
||||
@[simp] theorem USize.toUInt16_ofFin (n) : (USize.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_ofBitVec (b) : (UInt32.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt64.toUInt16_ofBitVec (b) : (UInt64.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem USize.toUInt16_ofBitVec (b) : (USize.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) :=
|
||||
UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem UInt64.toUInt16_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt16_ofNat' (n : Nat) : (USize.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt32.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt32.toUInt16_ofNat' _
|
||||
@[simp] theorem UInt64.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt16_ofNat' _
|
||||
@[simp] theorem USize.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt16_ofNat' _
|
||||
|
||||
theorem UInt32.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
|
||||
theorem UInt64.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
|
||||
theorem USize.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
|
||||
|
||||
theorem UInt32.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt64.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem USize.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
|
||||
@[simp] theorem USize.toUInt32_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_ofFin (n) : (UInt64.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
|
||||
@[simp] theorem USize.toUInt32_ofFin (n) : (USize.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_ofBitVec (b) : (UInt64.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem USize.toUInt32_ofBitVec (b) : (USize.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) :=
|
||||
UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
|
||||
@[simp] theorem USize.toUInt32_ofNat' (n : Nat) : (USize.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt32_ofNat' _
|
||||
@[simp] theorem USize.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt32_ofNat' _
|
||||
|
||||
theorem UInt64.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
|
||||
theorem USize.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
|
||||
|
||||
theorem UInt64.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem USize.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt64.toUSize_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUSize = USize.ofNat n := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUSize_ofFin (n) : (UInt64.ofFin n).toUSize = USize.ofNat n.val := rfl
|
||||
|
||||
@[simp] theorem UInt64.toUSize_ofBitVec (b) : (UInt64.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
|
||||
USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUSize_ofNat' (n : Nat) : (UInt64.ofNat n).toUSize = USize.ofNat n := USize.toNat.inj (by simp)
|
||||
|
||||
@[simp] theorem UInt64.toUSize_ofNat {n : Nat} : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUSize_ofNat' _
|
||||
|
||||
theorem UInt64.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
|
||||
(UInt64.ofNatTruncate n).toUSize = USize.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUSize_ofNatLT]
|
||||
|
||||
theorem UInt64.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size ≤ n) :
|
||||
(UInt64.ofNatTruncate n).toUSize = USize.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
theorem UInt8.toUInt16_ofNatLT {n : Nat} (h) :
|
||||
(UInt8.ofNatLT n h).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt8.toUInt32_ofNatLT {n : Nat} (h) :
|
||||
(UInt8.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt8.toUInt64_ofNatLT {n : Nat} (h) :
|
||||
(UInt8.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt8.toUSize_ofNatLT {n : Nat} (h) :
|
||||
(UInt8.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
|
||||
|
||||
theorem UInt8.toUInt16_ofFin {n} :
|
||||
(UInt8.ofFin n).toUInt16 = UInt16.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt8.toUInt32_ofFin {n} :
|
||||
(UInt8.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt8.toUInt64_ofFin {n} :
|
||||
(UInt8.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt8.toUSize_ofFin {n} :
|
||||
(UInt8.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_ofBitVec {b} : (UInt8.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt8.toUInt32_ofBitVec {b} : (UInt8.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt8.toUInt64_ofBitVec {b} : (UInt8.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt8.toUSize_ofBitVec {b} : (UInt8.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
|
||||
USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
theorem UInt8.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt8.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt8.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt8.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
|
||||
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem UInt8.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt8.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt8.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt8.size - 1) (by decide) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt8.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size ≤ n) :
|
||||
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT (UInt8.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
theorem UInt16.toUInt32_ofNatLT {n : Nat} (h) :
|
||||
(UInt16.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt16.toUInt64_ofNatLT {n : Nat} (h) :
|
||||
(UInt16.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt16.toUSize_ofNatLT {n : Nat} (h) :
|
||||
(UInt16.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
|
||||
|
||||
theorem UInt16.toUInt32_ofFin {n} :
|
||||
(UInt16.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt16.toUInt64_ofFin {n} :
|
||||
(UInt16.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt16.toUSize_ofFin {n} :
|
||||
(UInt16.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_ofBitVec {b} : (UInt16.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt16.toUInt64_ofBitVec {b} : (UInt16.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt16.toUSize_ofBitVec {b} : (UInt16.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
|
||||
USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
theorem UInt16.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt16.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt16.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
|
||||
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem UInt16.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt16.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt16.size - 1) (by decide) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt16.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size ≤ n) :
|
||||
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT (UInt16.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
theorem UInt32.toUInt64_ofNatLT {n : Nat} (h) :
|
||||
(UInt32.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
|
||||
theorem UInt32.toUSize_ofNatLT {n : Nat} (h) :
|
||||
(UInt32.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
|
||||
|
||||
theorem UInt32.toUInt64_ofFin {n} :
|
||||
(UInt32.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
|
||||
theorem UInt32.toUSize_ofFin {n} :
|
||||
(UInt32.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_ofBitVec {b} : (UInt32.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
|
||||
@[simp] theorem UInt32.toUSize_ofBitVec {b} : (UInt32.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
|
||||
USize.toBitVec_inj.1 (by simp)
|
||||
|
||||
theorem UInt32.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
theorem UInt32.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
|
||||
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem UInt32.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt32.size - 1) (by decide) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
theorem UInt32.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size ≤ n) :
|
||||
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT (UInt32.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
|
||||
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
theorem USize.toUInt64_ofNatLT {n : Nat} (h) :
|
||||
(USize.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h size_le_uint64Size) := rfl
|
||||
|
||||
theorem USize.toUInt64_ofFin {n} :
|
||||
(USize.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_uint64Size) := rfl
|
||||
|
||||
@[simp] theorem USize.toUInt64_ofBitVec {b} : (USize.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) :=
|
||||
UInt64.toBitVec_inj.1 (by simp)
|
||||
|
||||
theorem USize.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
|
||||
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_uint64Size) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
|
||||
|
||||
theorem USize.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
|
||||
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all +decide) :=
|
||||
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt16 = UInt16.ofNat n := by
|
||||
rw [← UInt8.ofNatLT_eq_ofNat (h := hn), toUInt16_ofNatLT, UInt16.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt8.toUInt32_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt32 = UInt32.ofNat n := by
|
||||
rw [← UInt8.ofNatLT_eq_ofNat (h := hn), toUInt32_ofNatLT, UInt32.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt8.toUInt64_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt64 = UInt64.ofNat n := by
|
||||
rw [← UInt8.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt8.toUSize_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUSize = USize.ofNat n := by
|
||||
rw [← UInt8.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUInt32 = UInt32.ofNat n := by
|
||||
rw [← UInt16.ofNatLT_eq_ofNat (h := hn), toUInt32_ofNatLT, UInt32.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt16.toUInt64_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUInt64 = UInt64.ofNat n := by
|
||||
rw [← UInt16.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt16.toUSize_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUSize = USize.ofNat n := by
|
||||
rw [← UInt16.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_ofNat' {n : Nat} (hn : n < UInt32.size) : (UInt32.ofNat n).toUInt64 = UInt64.ofNat n := by
|
||||
rw [← UInt32.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
|
||||
@[simp] theorem UInt32.toUSize_ofNat' {n : Nat} (hn : n < UInt32.size) : (UInt32.ofNat n).toUSize = USize.ofNat n := by
|
||||
rw [← UInt32.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem USize.toUInt64_ofNat' {n : Nat} (hn : n < USize.size) : (USize.ofNat n).toUInt64 = UInt64.ofNat n := by
|
||||
rw [← USize.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
|
||||
|
||||
@[simp] theorem UInt8.toUInt16_ofNat {n : Nat} (hn : n < 256) : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt8.toUInt16_ofNat' hn
|
||||
@[simp] theorem UInt8.toUInt32_ofNat {n : Nat} (hn : n < 256) : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt8.toUInt32_ofNat' hn
|
||||
@[simp] theorem UInt8.toUInt64_ofNat {n : Nat} (hn : n < 256) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt8.toUInt64_ofNat' hn
|
||||
@[simp] theorem UInt8.toUSize_ofNat {n : Nat} (hn : n < 256) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt8.toUSize_ofNat' hn
|
||||
|
||||
@[simp] theorem UInt16.toUInt32_ofNat {n : Nat} (hn : n < 65536) : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt16.toUInt32_ofNat' hn
|
||||
@[simp] theorem UInt16.toUInt64_ofNat {n : Nat} (hn : n < 65536) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt16.toUInt64_ofNat' hn
|
||||
@[simp] theorem UInt16.toUSize_ofNat {n : Nat} (hn : n < 65536) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt16.toUSize_ofNat' hn
|
||||
|
||||
@[simp] theorem UInt32.toUInt64_ofNat {n : Nat} (hn : n < 4294967296) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt32.toUInt64_ofNat' hn
|
||||
@[simp] theorem UInt32.toUSize_ofNat {n : Nat} (hn : n < 4294967296) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
UInt32.toUSize_ofNat' hn
|
||||
|
||||
@[simp] theorem USize.toUInt64_ofNat {n : Nat} (hn : n < 4294967296) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
|
||||
USize.toUInt64_ofNat' (Nat.lt_of_lt_of_le hn UInt32.size_le_usizeSize)
|
||||
|
||||
@[simp] theorem UInt8.ofNatLT_finVal (n : Fin UInt8.size) : UInt8.ofNatLT n.val n.isLt = UInt8.ofFin n := rfl
|
||||
@[simp] theorem UInt16.ofNatLT_finVal (n : Fin UInt16.size) : UInt16.ofNatLT n.val n.isLt = UInt16.ofFin n := rfl
|
||||
@[simp] theorem UInt32.ofNatLT_finVal (n : Fin UInt32.size) : UInt32.ofNatLT n.val n.isLt = UInt32.ofFin n := rfl
|
||||
@[simp] theorem UInt64.ofNatLT_finVal (n : Fin UInt64.size) : UInt64.ofNatLT n.val n.isLt = UInt64.ofFin n := rfl
|
||||
@[simp] theorem USize.ofNatLT_finVal (n : Fin USize.size) : USize.ofNatLT n.val n.isLt = USize.ofFin n := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofNatLT_bitVecToNat (n : BitVec 8) : UInt8.ofNatLT n.toNat n.isLt = UInt8.ofBitVec n := rfl
|
||||
@[simp] theorem UInt16.ofNatLT_bitVecToNat (n : BitVec 16) : UInt16.ofNatLT n.toNat n.isLt = UInt16.ofBitVec n := rfl
|
||||
@[simp] theorem UInt32.ofNatLT_bitVecToNat (n : BitVec 32) : UInt32.ofNatLT n.toNat n.isLt = UInt32.ofBitVec n := rfl
|
||||
@[simp] theorem UInt64.ofNatLT_bitVecToNat (n : BitVec 64) : UInt64.ofNatLT n.toNat n.isLt = UInt64.ofBitVec n := rfl
|
||||
@[simp] theorem USize.ofNatLT_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNatLT n.toNat n.isLt = USize.ofBitVec n := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofNat_finVal (n : Fin UInt8.size) : UInt8.ofNat n.val = UInt8.ofFin n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
|
||||
@[simp] theorem UInt16.ofNat_finVal (n : Fin UInt16.size) : UInt16.ofNat n.val = UInt16.ofFin n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
|
||||
@[simp] theorem UInt32.ofNat_finVal (n : Fin UInt32.size) : UInt32.ofNat n.val = UInt32.ofFin n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
|
||||
@[simp] theorem UInt64.ofNat_finVal (n : Fin UInt64.size) : UInt64.ofNat n.val = UInt64.ofFin n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
|
||||
@[simp] theorem USize.ofNat_finVal (n : Fin USize.size) : USize.ofNat n.val = USize.ofFin n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
|
||||
|
||||
@[simp] theorem UInt8.ofNat_bitVecToNat (n : BitVec 8) : UInt8.ofNat n.toNat = UInt8.ofBitVec n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
|
||||
@[simp] theorem UInt16.ofNat_bitVecToNat (n : BitVec 16) : UInt16.ofNat n.toNat = UInt16.ofBitVec n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
|
||||
@[simp] theorem UInt32.ofNat_bitVecToNat (n : BitVec 32) : UInt32.ofNat n.toNat = UInt32.ofBitVec n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
|
||||
@[simp] theorem UInt64.ofNat_bitVecToNat (n : BitVec 64) : UInt64.ofNat n.toNat = UInt64.ofBitVec n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
|
||||
@[simp] theorem USize.ofNat_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNat n.toNat = USize.ofBitVec n := by
|
||||
rw [← ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
|
||||
|
||||
@[simp] theorem UInt8.ofNatTruncate_finVal (n : Fin UInt8.size) : UInt8.ofNatTruncate n.val = UInt8.ofFin n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt8.ofNat_finVal]
|
||||
@[simp] theorem UInt16.ofNatTruncate_finVal (n : Fin UInt16.size) : UInt16.ofNatTruncate n.val = UInt16.ofFin n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt16.ofNat_finVal]
|
||||
@[simp] theorem UInt32.ofNatTruncate_finVal (n : Fin UInt32.size) : UInt32.ofNatTruncate n.val = UInt32.ofFin n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt32.ofNat_finVal]
|
||||
@[simp] theorem UInt64.ofNatTruncate_finVal (n : Fin UInt64.size) : UInt64.ofNatTruncate n.val = UInt64.ofFin n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt64.ofNat_finVal]
|
||||
@[simp] theorem USize.ofNatTruncate_finVal (n : Fin USize.size) : USize.ofNatTruncate n.val = USize.ofFin n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, USize.ofNat_finVal]
|
||||
|
||||
@[simp] theorem UInt8.ofNatTruncate_bitVecToNat (n : BitVec 8) : UInt8.ofNatTruncate n.toNat = UInt8.ofBitVec n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
|
||||
@[simp] theorem UInt16.ofNatTruncate_bitVecToNat (n : BitVec 16) : UInt16.ofNatTruncate n.toNat = UInt16.ofBitVec n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
|
||||
@[simp] theorem UInt32.ofNatTruncate_bitVecToNat (n : BitVec 32) : UInt32.ofNatTruncate n.toNat = UInt32.ofBitVec n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
|
||||
@[simp] theorem UInt64.ofNatTruncate_bitVecToNat (n : BitVec 64) : UInt64.ofNatTruncate n.toNat = UInt64.ofBitVec n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
|
||||
@[simp] theorem USize.ofNatTruncate_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNatTruncate n.toNat = USize.ofBitVec n := by
|
||||
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
|
||||
|
||||
@[simp] theorem UInt8.ofFin_mk {n : Nat} (hn) : UInt8.ofFin (Fin.mk n hn) = UInt8.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt16.ofFin_mk {n : Nat} (hn) : UInt16.ofFin (Fin.mk n hn) = UInt16.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt32.ofFin_mk {n : Nat} (hn) : UInt32.ofFin (Fin.mk n hn) = UInt32.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt64.ofFin_mk {n : Nat} (hn) : UInt64.ofFin (Fin.mk n hn) = UInt64.ofNatLT n hn := rfl
|
||||
@[simp] theorem USize.ofFin_mk {n : Nat} (hn) : USize.ofFin (Fin.mk n hn) = USize.ofNatLT n hn := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofFin_bitVecToFin (n : BitVec 8) : UInt8.ofFin n.toFin = UInt8.ofBitVec n := rfl
|
||||
@[simp] theorem UInt16.ofFin_bitVecToFin (n : BitVec 16) : UInt16.ofFin n.toFin = UInt16.ofBitVec n := rfl
|
||||
@[simp] theorem UInt32.ofFin_bitVecToFin (n : BitVec 32) : UInt32.ofFin n.toFin = UInt32.ofBitVec n := rfl
|
||||
@[simp] theorem UInt64.ofFin_bitVecToFin (n : BitVec 64) : UInt64.ofFin n.toFin = UInt64.ofBitVec n := rfl
|
||||
@[simp] theorem USize.ofFin_bitVecToFin (n : BitVec System.Platform.numBits) : USize.ofFin n.toFin = USize.ofBitVec n := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofBitVec_ofNatLT {n : Nat} (hn) : UInt8.ofBitVec (BitVec.ofNatLT n hn) = UInt8.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_ofNatLT {n : Nat} (hn) : UInt16.ofBitVec (BitVec.ofNatLT n hn) = UInt16.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_ofNatLT {n : Nat} (hn) : UInt32.ofBitVec (BitVec.ofNatLT n hn) = UInt32.ofNatLT n hn := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_ofNatLT {n : Nat} (hn) : UInt64.ofBitVec (BitVec.ofNatLT n hn) = UInt64.ofNatLT n hn := rfl
|
||||
@[simp] theorem USize.ofBitVec_ofNatLT {n : Nat} (hn) : USize.ofBitVec (BitVec.ofNatLT n hn) = USize.ofNatLT n hn := rfl
|
||||
|
||||
@[simp] theorem UInt8.ofBitVec_ofFin (n) : UInt8.ofBitVec (BitVec.ofFin n) = UInt8.ofFin n := rfl
|
||||
@[simp] theorem UInt16.ofBitVec_ofFin (n) : UInt16.ofBitVec (BitVec.ofFin n) = UInt16.ofFin n := rfl
|
||||
@[simp] theorem UInt32.ofBitVec_ofFin (n) : UInt32.ofBitVec (BitVec.ofFin n) = UInt32.ofFin n := rfl
|
||||
@[simp] theorem UInt64.ofBitVec_ofFin (n) : UInt64.ofBitVec (BitVec.ofFin n) = UInt64.ofFin n := rfl
|
||||
@[simp] theorem USize.ofBitVec_ofFin (n) : USize.ofBitVec (BitVec.ofFin n) = USize.ofFin n := rfl
|
||||
|
||||
@[simp] theorem BitVec.ofNat_uInt8ToNat (n : UInt8) : BitVec.ofNat 8 n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem BitVec.ofNat_uInt16ToNat (n : UInt16) : BitVec.ofNat 16 n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem BitVec.ofNat_uInt32ToNat (n : UInt32) : BitVec.ofNat 32 n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem BitVec.ofNat_uInt64ToNat (n : UInt64) : BitVec.ofNat 64 n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
@[simp] theorem BitVec.ofNat_uSizeToNat (n : USize) : BitVec.ofNat System.Platform.numBits n.toNat = n.toBitVec :=
|
||||
BitVec.eq_of_toNat_eq (by simp)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user