mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-20 03:44:10 +00:00
Compare commits
148 Commits
fold_attac
...
unattach_a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e095aa340b | ||
|
|
721617d734 | ||
|
|
532c782e20 | ||
|
|
683fa8a794 | ||
|
|
9322d8d639 | ||
|
|
9dcd2ad2a3 | ||
|
|
e3811fd838 | ||
|
|
867e67b9f3 | ||
|
|
6cd80c28b7 | ||
|
|
f202469c8a | ||
|
|
e417a2331c | ||
|
|
5eb6c67a78 | ||
|
|
499c58796b | ||
|
|
863e9c073b | ||
|
|
60096e7d15 | ||
|
|
e90c3cf15a | ||
|
|
d4195c2605 | ||
|
|
4932dbc65d | ||
|
|
d0ee9d0127 | ||
|
|
3e2bca7309 | ||
|
|
ddec5336e5 | ||
|
|
37baa89d9b | ||
|
|
949feb25a4 | ||
|
|
bfb73c4a5e | ||
|
|
b22dee8816 | ||
|
|
4771741fa2 | ||
|
|
ffb4c5becf | ||
|
|
d3f7ed434b | ||
|
|
6bd0d9d73b | ||
|
|
db79d9e5ce | ||
|
|
5e8718dff9 | ||
|
|
4f2c4c7bd1 | ||
|
|
56ba39d68a | ||
|
|
1fca66b8c9 | ||
|
|
36c29bee31 | ||
|
|
cf14178929 | ||
|
|
a4dfa83af5 | ||
|
|
c5fd652765 | ||
|
|
4cd4bcc9be | ||
|
|
7d26a1604f | ||
|
|
3a46fd0fde | ||
|
|
994cfa4c74 | ||
|
|
cf3e7de143 | ||
|
|
2ace579438 | ||
|
|
40d6a6def0 | ||
|
|
d96b7a7d98 | ||
|
|
40e97bd566 | ||
|
|
3bd01de384 | ||
|
|
8835ab46ad | ||
|
|
96adf04a62 | ||
|
|
0db6daa8f1 | ||
|
|
130b465aaf | ||
|
|
ccdf07b6a1 | ||
|
|
5605e0198a | ||
|
|
5f22ba7789 | ||
|
|
16a16898d5 | ||
|
|
4ea76aadd1 | ||
|
|
ef71f0beab | ||
|
|
9f4075be72 | ||
|
|
1b6572726f | ||
|
|
56b78a0ed1 | ||
|
|
e28bfedae2 | ||
|
|
e7691f37c6 | ||
|
|
48711ce6eb | ||
|
|
0733273a78 | ||
|
|
2221296d3c | ||
|
|
f22998edfe | ||
|
|
3817b16c35 | ||
|
|
9eef726204 | ||
|
|
9460f79d28 | ||
|
|
c38c07e1a1 | ||
|
|
062ecb5eae | ||
|
|
13969ad667 | ||
|
|
91a033488c | ||
|
|
1fb75b68ab | ||
|
|
26f508db87 | ||
|
|
3d1ac7cfa2 | ||
|
|
0196bca784 | ||
|
|
b320dcfef9 | ||
|
|
5dea30f169 | ||
|
|
90cb6e5da8 | ||
|
|
a3ca15d2b2 | ||
|
|
c2f6297554 | ||
|
|
1defa2028f | ||
|
|
78c40f380c | ||
|
|
3e2a465b13 | ||
|
|
1ec0c64c7b | ||
|
|
604bcf50ef | ||
|
|
145c9efb32 | ||
|
|
e4f2de0a53 | ||
|
|
7845a05cf1 | ||
|
|
57679eeff5 | ||
|
|
974cc3306c | ||
|
|
c7819bd6eb | ||
|
|
a4fb740d2f | ||
|
|
ea75c924a1 | ||
|
|
65f4b92505 | ||
|
|
a6f0112fc5 | ||
|
|
eee0553318 | ||
|
|
5d2c7fc1d9 | ||
|
|
94de4ae964 | ||
|
|
1129160d80 | ||
|
|
8cc62940e0 | ||
|
|
b612403980 | ||
|
|
6b0d4e50c0 | ||
|
|
0cae7165aa | ||
|
|
ba43ce18c3 | ||
|
|
3190be3058 | ||
|
|
a108644461 | ||
|
|
4b47a10bef | ||
|
|
2d5ebf3705 | ||
|
|
5017b2bfbf | ||
|
|
1b4ee185e8 | ||
|
|
27c4c366b4 | ||
|
|
405b5aa047 | ||
|
|
edf2327229 | ||
|
|
cda6733f97 | ||
|
|
624f1b9963 | ||
|
|
cf94f793a2 | ||
|
|
9185955692 | ||
|
|
9d583ab4ec | ||
|
|
50339e38d9 | ||
|
|
8b61dda964 | ||
|
|
fc20b5dfb4 | ||
|
|
7fba7ed7b6 | ||
|
|
2f2142ab37 | ||
|
|
e551a366a0 | ||
|
|
f4afbc2f8b | ||
|
|
8c8585536c | ||
|
|
c825b5a560 | ||
|
|
738435b90a | ||
|
|
1883c9b7eb | ||
|
|
fc52015841 | ||
|
|
a6830f90ab | ||
|
|
eceba0faf4 | ||
|
|
fc963ffceb | ||
|
|
d8e0fa425b | ||
|
|
e43664c405 | ||
|
|
c50bc845c2 | ||
|
|
b41019e8e8 | ||
|
|
0a2d121e45 | ||
|
|
152ca85fa9 | ||
|
|
0ecf2a030a | ||
|
|
7b8f2fe54c | ||
|
|
34f85bee02 | ||
|
|
590c725943 | ||
|
|
9193196208 | ||
|
|
c3f384d6a5 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -25,7 +25,7 @@ Please put an X between the brackets as you perform the following steps:
|
||||
|
||||
### Context
|
||||
|
||||
[Broader context that the issue occured in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.]
|
||||
[Broader context that the issue occurred in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.]
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -316,7 +316,7 @@ jobs:
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overriden)
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
|
||||
20
.github/workflows/pr-release.yml
vendored
20
.github/workflows/pr-release.yml
vendored
@@ -134,7 +134,7 @@ jobs:
|
||||
MESSAGE=""
|
||||
|
||||
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
else
|
||||
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-mathlib")"
|
||||
MESSAGE="- ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`."
|
||||
fi
|
||||
@@ -164,10 +164,10 @@ jobs:
|
||||
|
||||
# Use GitHub API to check if a comment already exists
|
||||
existing_comment="$(curl --retry 3 --location --silent \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-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 }}/comments" \
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-mathlib4-bot"))')"
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-bot"))')"
|
||||
existing_comment_id="$(echo "$existing_comment" | jq -r .id)"
|
||||
existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
|
||||
|
||||
@@ -177,14 +177,14 @@ jobs:
|
||||
echo "Posting message to the comments: $MESSAGE"
|
||||
|
||||
# Append new result to the existing comment or post a new comment
|
||||
# It's essential we use the MATHLIB4_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
# It's essential we use the MATHLIB4_COMMENT_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
if [ -z "$existing_comment_id" ]; then
|
||||
INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):"
|
||||
# Post new comment with a bullet point
|
||||
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $val)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
@@ -193,7 +193,7 @@ jobs:
|
||||
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X PATCH \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
|
||||
@@ -329,16 +329,18 @@ jobs:
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "nightly-testing-'"${MOST_RECENT_NIGHTLY}"'",' lakefile.lean
|
||||
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean
|
||||
lake update batteries
|
||||
git add lakefile.lean lake-manifest.json
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
echo "Branch already exists, merging $BASE and bumping Batteries."
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
lake update batteries
|
||||
get add lake-manifest.json
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
|
||||
334
RELEASES.md
334
RELEASES.md
@@ -10,7 +10,317 @@ of each version.
|
||||
|
||||
v4.12.0
|
||||
----------
|
||||
Development in progress.
|
||||
|
||||
### Language features, tactics, and metaprograms
|
||||
|
||||
* `bv_decide` tactic. This release introduces a new tactic for proving goals involving `BitVec` and `Bool`. It reduces the goal to a SAT instance that is refuted by an external solver, and the resulting LRAT proof is checked in Lean. This is used to synthesize a proof of the goal by reflection. As this process uses verified algorithms, proofs generated by this tactic use `Lean.ofReduceBool`, so this tactic includes the Lean compiler as part of the trusted code base. The external solver CaDiCaL is included with Lean and does not need to be installed separately to make use of `bv_decide`.
|
||||
|
||||
For example, we can use `bv_decide` to verify that a bit twiddling formula leaves at most one bit set:
|
||||
```lean
|
||||
def popcount (x : BitVec 64) : BitVec 64 :=
|
||||
let rec go (x pop : BitVec 64) : Nat → BitVec 64
|
||||
| 0 => pop
|
||||
| n + 1 => go (x >>> 2) (pop + (x &&& 1)) n
|
||||
go x 0 64
|
||||
|
||||
example (x : BitVec 64) : popcount ((x &&& (x - 1)) ^^^ x) ≤ 1 := by
|
||||
simp only [popcount, popcount.go]
|
||||
bv_decide
|
||||
```
|
||||
When the external solver fails to refute the SAT instance generated by `bv_decide`, it can report a counterexample:
|
||||
```lean
|
||||
/--
|
||||
error: The prover found a counterexample, consider the following assignment:
|
||||
x = 0xffffffffffffffff#64
|
||||
-/
|
||||
#guard_msgs in
|
||||
example (x : BitVec 64) : x < x + 1 := by
|
||||
bv_decide
|
||||
```
|
||||
|
||||
See `Lean.Elab.Tactic.BVDecide` for a more detailed overview, and look in `tests/lean/run/bv_*` for examples.
|
||||
|
||||
[#5013](https://github.com/leanprover/lean4/pull/5013), [#5074](https://github.com/leanprover/lean4/pull/5074), [#5100](https://github.com/leanprover/lean4/pull/5100), [#5113](https://github.com/leanprover/lean4/pull/5113), [#5137](https://github.com/leanprover/lean4/pull/5137), [#5203](https://github.com/leanprover/lean4/pull/5203), [#5212](https://github.com/leanprover/lean4/pull/5212), [#5220](https://github.com/leanprover/lean4/pull/5220).
|
||||
|
||||
* `simp` tactic
|
||||
* [#4988](https://github.com/leanprover/lean4/pull/4988) fixes a panic in the `reducePow` simproc.
|
||||
* [#5071](https://github.com/leanprover/lean4/pull/5071) exposes the `index` option to the `dsimp` tactic, introduced to `simp` in [#4202](https://github.com/leanprover/lean4/pull/4202).
|
||||
* [#5159](https://github.com/leanprover/lean4/pull/5159) fixes a panic at `Fin.isValue` simproc.
|
||||
* [#5167](https://github.com/leanprover/lean4/pull/5167) and [#5175](https://github.com/leanprover/lean4/pull/5175) rename the `simpCtorEq` simproc to `reduceCtorEq` and makes it optional. (See breaking changes.)
|
||||
* [#5187](https://github.com/leanprover/lean4/pull/5187) ensures `reduceCtorEq` is enabled in the `norm_cast` tactic.
|
||||
* [#5073](https://github.com/leanprover/lean4/pull/5073) modifies the simp debug trace messages to tag with "dpre" and "dpost" instead of "pre" and "post" when in definitional rewrite mode. [#5054](https://github.com/leanprover/lean4/pull/5054) explains the `reduce` steps for `trace.Debug.Meta.Tactic.simp` trace messages.
|
||||
* `ext` tactic
|
||||
* [#4996](https://github.com/leanprover/lean4/pull/4996) reduces default maximum iteration depth from 1000000 to 100.
|
||||
* `induction` tactic
|
||||
* [#5117](https://github.com/leanprover/lean4/pull/5117) fixes a bug where `let` bindings in minor premises wouldn't be counted correctly.
|
||||
|
||||
* `omega` tactic
|
||||
* [#5157](https://github.com/leanprover/lean4/pull/5157) fixes a panic.
|
||||
|
||||
* `conv` tactic
|
||||
* [#5149](https://github.com/leanprover/lean4/pull/5149) improves `arg n` to handle subsingleton instance arguments.
|
||||
|
||||
* [#5044](https://github.com/leanprover/lean4/pull/5044) upstreams the `#time` command.
|
||||
* [#5079](https://github.com/leanprover/lean4/pull/5079) makes `#check` and `#reduce` typecheck the elaborated terms.
|
||||
|
||||
* **Incrementality**
|
||||
* [#4974](https://github.com/leanprover/lean4/pull/4974) fixes regression where we would not interrupt elaboration of previous document versions.
|
||||
* [#5004](https://github.com/leanprover/lean4/pull/5004) fixes a performance regression.
|
||||
* [#5001](https://github.com/leanprover/lean4/pull/5001) disables incremental body elaboration in presence of `where` clauses in declarations.
|
||||
* [#5018](https://github.com/leanprover/lean4/pull/5018) enables infotrees on the command line for ilean generation.
|
||||
* [#5040](https://github.com/leanprover/lean4/pull/5040) and [#5056](https://github.com/leanprover/lean4/pull/5056) improve performance of info trees.
|
||||
* [#5090](https://github.com/leanprover/lean4/pull/5090) disables incrementality in the `case .. | ..` tactic.
|
||||
* [#5312](https://github.com/leanprover/lean4/pull/5312) fixes a bug where changing whitespace after the module header could break subsequent commands.
|
||||
|
||||
* **Definitions**
|
||||
* [#5016](https://github.com/leanprover/lean4/pull/5016) and [#5066](https://github.com/leanprover/lean4/pull/5066) add `clean_wf` tactic to clean up tactic state in `decreasing_by`. This can be disabled with `set_option debug.rawDecreasingByGoal false`.
|
||||
* [#5055](https://github.com/leanprover/lean4/pull/5055) unifies equational theorems between structural and well-founded recursion.
|
||||
* [#5041](https://github.com/leanprover/lean4/pull/5041) allows mutually recursive functions to use different parameter names among the “fixed parameter prefix”
|
||||
* [#4154](https://github.com/leanprover/lean4/pull/4154) and [#5109](https://github.com/leanprover/lean4/pull/5109) add fine-grained equational lemmas for non-recursive functions. See breaking changes.
|
||||
* [#5129](https://github.com/leanprover/lean4/pull/5129) unifies equation lemmas for recursive and non-recursive definitions. The `backward.eqns.deepRecursiveSplit` option can be set to `false` to get the old behavior. See breaking changes.
|
||||
* [#5141](https://github.com/leanprover/lean4/pull/5141) adds `f.eq_unfold` lemmas. Now Lean produces the following zoo of rewrite rules:
|
||||
```
|
||||
Option.map.eq_1 : Option.map f none = none
|
||||
Option.map.eq_2 : Option.map f (some x) = some (f x)
|
||||
Option.map.eq_def : Option.map f p = match o with | none => none | (some x) => some (f x)
|
||||
Option.map.eq_unfold : Option.map = fun f p => match o with | none => none | (some x) => some (f x)
|
||||
```
|
||||
The `f.eq_unfold` variant is especially useful to rewrite with `rw` under binders.
|
||||
* [#5136](https://github.com/leanprover/lean4/pull/5136) fixes bugs in recursion over predicates.
|
||||
|
||||
* **Variable inclusion**
|
||||
* [#5206](https://github.com/leanprover/lean4/pull/5206) documents that `include` currently only applies to theorems.
|
||||
|
||||
* **Elaboration**
|
||||
* [#4926](https://github.com/leanprover/lean4/pull/4926) fixes a bug where autoparam errors were associated to an incorrect source position.
|
||||
* [#4833](https://github.com/leanprover/lean4/pull/4833) fixes an issue where cdot anonymous functions (e.g. `(· + ·)`) would not handle ambiguous notation correctly. Numbers the parameters, making this example expand as `fun x1 x2 => x1 + x2` rather than `fun x x_1 => x + x_1`.
|
||||
* [#5037](https://github.com/leanprover/lean4/pull/5037) improves strength of the tactic that proves array indexing is in bounds.
|
||||
* [#5119](https://github.com/leanprover/lean4/pull/5119) fixes a bug in the tactic that proves indexing is in bounds where it could loop in the presence of mvars.
|
||||
* [#5072](https://github.com/leanprover/lean4/pull/5072) makes the structure type clickable in "not a field of structure" errors for structure instance notation.
|
||||
* [#4717](https://github.com/leanprover/lean4/pull/4717) fixes a bug where mutual `inductive` commands could create terms that the kernel rejects.
|
||||
* [#5142](https://github.com/leanprover/lean4/pull/5142) fixes a bug where `variable` could fail when mixing binder updates and declarations.
|
||||
|
||||
* **Other fixes or improvements**
|
||||
* [#5118](https://github.com/leanprover/lean4/pull/5118) changes the definition of the `syntheticHole` parser so that hovering over `_` in `?_` gives the docstring for synthetic holes.
|
||||
* [#5173](https://github.com/leanprover/lean4/pull/5173) uses the emoji variant selector for ✅️,❌️,💥️ in messages, improving fonts selection.
|
||||
* [#5183](https://github.com/leanprover/lean4/pull/5183) fixes a bug in `rename_i` where implementation detail hypotheses could be renamed.
|
||||
|
||||
### Language server, widgets, and IDE extensions
|
||||
|
||||
* [#4821](https://github.com/leanprover/lean4/pull/4821) resolves two language server bugs that especially affect Windows users. (1) Editing the header could result in the watchdog not correctly restarting the file worker, which would lead to the file seemingly being processed forever. (2) On an especially slow Windows machine, we found that starting the language server would sometimes not succeed at all. This PR also resolves an issue where we would not correctly emit messages that we received while the file worker is being restarted to the corresponding file worker after the restart.
|
||||
* [#5006](https://github.com/leanprover/lean4/pull/5006) updates the user widget manual.
|
||||
* [#5193](https://github.com/leanprover/lean4/pull/5193) updates the quickstart guide with the new display name for the Lean 4 extension ("Lean 4").
|
||||
* [#5185](https://github.com/leanprover/lean4/pull/5185) fixes a bug where over time "import out of date" messages would accumulate.
|
||||
* [#4900](https://github.com/leanprover/lean4/pull/4900) improves ilean loading performance by about a factor of two. Optimizes the JSON parser and the conversion from JSON to Lean data structures; see PR description for details.
|
||||
* **Other fixes or improvements**
|
||||
* [#5031](https://github.com/leanprover/lean4/pull/5031) localizes an instance in `Lsp.Diagnostics`.
|
||||
|
||||
### Pretty printing
|
||||
|
||||
* [#4976](https://github.com/leanprover/lean4/pull/4976) introduces `@[app_delab]`, a macro for creating delaborators for particular constants. The `@[app_delab ident]` syntax resolves `ident` to its constant name `name` and then expands to `@[delab app.name]`.
|
||||
* [#4982](https://github.com/leanprover/lean4/pull/4982) fixes a bug where the pretty printer assumed structure projections were type correct (such terms can appear in type mismatch errors). Improves hoverability of `#print` output for structures.
|
||||
* [#5218](https://github.com/leanprover/lean4/pull/5218) and [#5239](https://github.com/leanprover/lean4/pull/5239) add `pp.exprSizes` debugging option. When true, each pretty printed expression is prefixed with `[size a/b/c]`, where `a` is the size without sharing, `b` is the actual size, and `c` is the size with the maximum possible sharing.
|
||||
|
||||
### Library
|
||||
|
||||
* [#5020](https://github.com/leanprover/lean4/pull/5020) swaps the parameters to `Membership.mem`. A purpose of this change is to make set-like `CoeSort` coercions to refer to the eta-expanded function `fun x => Membership.mem s x`, which can reduce in many computations. Another is that having the `s` argument first leads to better discrimination tree keys. (See breaking changes.)
|
||||
* `Array`
|
||||
* [#4970](https://github.com/leanprover/lean4/pull/4970) adds `@[ext]` attribute to `Array.ext`.
|
||||
* [#4957](https://github.com/leanprover/lean4/pull/4957) deprecates `Array.get_modify`.
|
||||
* `List`
|
||||
* [#4995](https://github.com/leanprover/lean4/pull/4995) upstreams `List.findIdx` lemmas.
|
||||
* [#5029](https://github.com/leanprover/lean4/pull/5029), [#5048](https://github.com/leanprover/lean4/pull/5048) and [#5132](https://github.com/leanprover/lean4/pull/5132) add `List.Sublist` lemmas, some upstreamed. [#5077](https://github.com/leanprover/lean4/pull/5077) fixes implicitness in refl/rfl lemma binders. add `List.Sublist` theorems.
|
||||
* [#5047](https://github.com/leanprover/lean4/pull/5047) upstreams `List.Pairwise` lemmas.
|
||||
* [#5053](https://github.com/leanprover/lean4/pull/5053), [#5124](https://github.com/leanprover/lean4/pull/5124), and [#5161](https://github.com/leanprover/lean4/pull/5161) add `List.find?/findSome?/findIdx?` theorems.
|
||||
* [#5039](https://github.com/leanprover/lean4/pull/5039) adds `List.foldlRecOn` and `List.foldrRecOn` recursion principles to prove things about `List.foldl` and `List.foldr`.
|
||||
* [#5069](https://github.com/leanprover/lean4/pull/5069) upstreams `List.Perm`.
|
||||
* [#5092](https://github.com/leanprover/lean4/pull/5092) and [#5107](https://github.com/leanprover/lean4/pull/5107) add `List.mergeSort` and a fast `@[csimp]` implementation.
|
||||
* [#5103](https://github.com/leanprover/lean4/pull/5103) makes the simp lemmas for `List.subset` more aggressive.
|
||||
* [#5106](https://github.com/leanprover/lean4/pull/5106) changes the statement of `List.getLast?_cons`.
|
||||
* [#5123](https://github.com/leanprover/lean4/pull/5123) and [#5158](https://github.com/leanprover/lean4/pull/5158) add `List.range` and `List.iota` lemmas.
|
||||
* [#5130](https://github.com/leanprover/lean4/pull/5130) adds `List.join` lemmas.
|
||||
* [#5131](https://github.com/leanprover/lean4/pull/5131) adds `List.append` lemmas.
|
||||
* [#5152](https://github.com/leanprover/lean4/pull/5152) adds `List.erase(|P|Idx)` lemmas.
|
||||
* [#5127](https://github.com/leanprover/lean4/pull/5127) makes miscellaneous lemma updates.
|
||||
* [#5153](https://github.com/leanprover/lean4/pull/5153) and [#5160](https://github.com/leanprover/lean4/pull/5160) add lemmas about `List.attach` and `List.pmap`.
|
||||
* [#5164](https://github.com/leanprover/lean4/pull/5164), [#5177](https://github.com/leanprover/lean4/pull/5177), and [#5215](https://github.com/leanprover/lean4/pull/5215) add `List.find?` and `List.range'/range/iota` lemmas.
|
||||
* [#5196](https://github.com/leanprover/lean4/pull/5196) adds `List.Pairwise_erase` and related lemmas.
|
||||
* [#5151](https://github.com/leanprover/lean4/pull/5151) and [#5163](https://github.com/leanprover/lean4/pull/5163) improve confluence of `List` simp lemmas. [#5105](https://github.com/leanprover/lean4/pull/5105) and [#5102](https://github.com/leanprover/lean4/pull/5102) adjust `List` simp lemmas.
|
||||
* [#5178](https://github.com/leanprover/lean4/pull/5178) removes `List.getLast_eq_iff_getLast_eq_some` as a simp lemma.
|
||||
* [#5210](https://github.com/leanprover/lean4/pull/5210) reverses the meaning of `List.getElem_drop` and `List.getElem_drop'`.
|
||||
* [#5214](https://github.com/leanprover/lean4/pull/5214) moves `@[csimp]` lemmas earlier where possible.
|
||||
* `Nat` and `Int`
|
||||
* [#5104](https://github.com/leanprover/lean4/pull/5104) adds `Nat.add_left_eq_self` and relatives.
|
||||
* [#5146](https://github.com/leanprover/lean4/pull/5146) adds missing `Nat.and_xor_distrib_(left|right)`.
|
||||
* [#5148](https://github.com/leanprover/lean4/pull/5148) and [#5190](https://github.com/leanprover/lean4/pull/5190) improve `Nat` and `Int` simp lemma confluence.
|
||||
* [#5165](https://github.com/leanprover/lean4/pull/5165) adjusts `Int` simp lemmas.
|
||||
* [#5166](https://github.com/leanprover/lean4/pull/5166) adds `Int` lemmas relating `neg` and `emod`/`mod`.
|
||||
* [#5208](https://github.com/leanprover/lean4/pull/5208) reverses the direction of the `Int.toNat_sub` simp lemma.
|
||||
* [#5209](https://github.com/leanprover/lean4/pull/5209) adds `Nat.bitwise` lemmas.
|
||||
* [#5230](https://github.com/leanprover/lean4/pull/5230) corrects the docstrings for integer division and modulus.
|
||||
* `Option`
|
||||
* [#5128](https://github.com/leanprover/lean4/pull/5128) and [#5154](https://github.com/leanprover/lean4/pull/5154) add `Option` lemmas.
|
||||
* `BitVec`
|
||||
* [#4889](https://github.com/leanprover/lean4/pull/4889) adds `sshiftRight` bitblasting.
|
||||
* [#4981](https://github.com/leanprover/lean4/pull/4981) adds `Std.Associative` and `Std.Commutative` instances for `BitVec.[and|or|xor]`.
|
||||
* [#4913](https://github.com/leanprover/lean4/pull/4913) enables `missingDocs` error for `BitVec` modules.
|
||||
* [#4930](https://github.com/leanprover/lean4/pull/4930) makes parameter names for `BitVec` more consistent.
|
||||
* [#5098](https://github.com/leanprover/lean4/pull/5098) adds `BitVec.intMin`. Introduces `boolToPropSimps` simp set for converting from boolean to propositional expressions.
|
||||
* [#5200](https://github.com/leanprover/lean4/pull/5200) and [#5217](https://github.com/leanprover/lean4/pull/5217) rename `BitVec.getLsb` to `BitVec.getLsbD`, etc., to bring naming in line with `List`/`Array`/etc.
|
||||
* **Theorems:** [#4977](https://github.com/leanprover/lean4/pull/4977), [#4951](https://github.com/leanprover/lean4/pull/4951), [#4667](https://github.com/leanprover/lean4/pull/4667), [#5007](https://github.com/leanprover/lean4/pull/5007), [#4997](https://github.com/leanprover/lean4/pull/4997), [#5083](https://github.com/leanprover/lean4/pull/5083), [#5081](https://github.com/leanprover/lean4/pull/5081), [#4392](https://github.com/leanprover/lean4/pull/4392)
|
||||
* `UInt`
|
||||
* [#4514](https://github.com/leanprover/lean4/pull/4514) fixes naming convention for `UInt` lemmas.
|
||||
* `Std.HashMap` and `Std.HashSet`
|
||||
* [#4943](https://github.com/leanprover/lean4/pull/4943) deprecates variants of hash map query methods. (See breaking changes.)
|
||||
* [#4917](https://github.com/leanprover/lean4/pull/4917) switches the library and Lean to `Std.HashMap` and `Std.HashSet` almost everywhere.
|
||||
* [#4954](https://github.com/leanprover/lean4/pull/4954) deprecates `Lean.HashMap` and `Lean.HashSet`.
|
||||
* [#5023](https://github.com/leanprover/lean4/pull/5023) cleans up lemma parameters.
|
||||
|
||||
* `Std.Sat` (for `bv_decide`)
|
||||
* [#4933](https://github.com/leanprover/lean4/pull/4933) adds definitions of SAT and CNF.
|
||||
* [#4953](https://github.com/leanprover/lean4/pull/4953) defines "and-inverter graphs" (AIGs) as described in section 3 of [Davis-Swords 2013](https://arxiv.org/pdf/1304.7861.pdf).
|
||||
|
||||
* **Parsec**
|
||||
* [#4774](https://github.com/leanprover/lean4/pull/4774) generalizes the `Parsec` library, allowing parsing of iterable data beyong `String` such as `ByteArray`. (See breaking changes.)
|
||||
* [#5115](https://github.com/leanprover/lean4/pull/5115) moves `Lean.Data.Parsec` to `Std.Internal.Parsec` for bootstrappng reasons.
|
||||
|
||||
* `Thunk`
|
||||
* [#4969](https://github.com/leanprover/lean4/pull/4969) upstreams `Thunk.ext`.
|
||||
|
||||
* **IO**
|
||||
* [#4973](https://github.com/leanprover/lean4/pull/4973) modifies `IO.FS.lines` to handle `\r\n` on all operating systems instead of just on Windows.
|
||||
* [#5125](https://github.com/leanprover/lean4/pull/5125) adds `createTempFile` and `withTempFile` for creating temporary files that can only be read and written by the current user.
|
||||
|
||||
* **Other fixes or improvements**
|
||||
* [#4945](https://github.com/leanprover/lean4/pull/4945) adds `Array`, `Bool` and `Prod` utilities from LeanSAT.
|
||||
* [#4960](https://github.com/leanprover/lean4/pull/4960) adds `Relation.TransGen.trans`.
|
||||
* [#5012](https://github.com/leanprover/lean4/pull/5012) states `WellFoundedRelation Nat` using `<`, not `Nat.lt`.
|
||||
* [#5011](https://github.com/leanprover/lean4/pull/5011) uses `≠` instead of `Not (Eq ...)` in `Fin.ne_of_val_ne`.
|
||||
* [#5197](https://github.com/leanprover/lean4/pull/5197) upstreams `Fin.le_antisymm`.
|
||||
* [#5042](https://github.com/leanprover/lean4/pull/5042) reduces usage of `refine'`.
|
||||
* [#5101](https://github.com/leanprover/lean4/pull/5101) adds about `if-then-else` and `Option`.
|
||||
* [#5112](https://github.com/leanprover/lean4/pull/5112) adds basic instances for `ULift` and `PLift`.
|
||||
* [#5133](https://github.com/leanprover/lean4/pull/5133) and [#5168](https://github.com/leanprover/lean4/pull/5168) make fixes from running the simpNF linter over Lean.
|
||||
* [#5156](https://github.com/leanprover/lean4/pull/5156) removes a bad simp lemma in `omega` theory.
|
||||
* [#5155](https://github.com/leanprover/lean4/pull/5155) improves confluence of `Bool` simp lemmas.
|
||||
* [#5162](https://github.com/leanprover/lean4/pull/5162) improves confluence of `Function.comp` simp lemmas.
|
||||
* [#5191](https://github.com/leanprover/lean4/pull/5191) improves confluence of `if-then-else` simp lemmas.
|
||||
* [#5147](https://github.com/leanprover/lean4/pull/5147) adds `@[elab_as_elim]` to `Quot.rec`, `Nat.strongInductionOn` and `Nat.casesStrongInductionOn`, and also renames the latter two to `Nat.strongRecOn` and `Nat.casesStrongRecOn` (deprecated in [#5179](https://github.com/leanprover/lean4/pull/5179)).
|
||||
* [#5180](https://github.com/leanprover/lean4/pull/5180) disables some simp lemmas with bad discrimination tree keys.
|
||||
* [#5189](https://github.com/leanprover/lean4/pull/5189) cleans up internal simp lemmas that had leaked.
|
||||
* [#5198](https://github.com/leanprover/lean4/pull/5198) cleans up `allowUnsafeReducibility`.
|
||||
* [#5229](https://github.com/leanprover/lean4/pull/5229) removes unused lemmas from some `simp` tactics.
|
||||
* [#5199](https://github.com/leanprover/lean4/pull/5199) removes >6 month deprecations.
|
||||
|
||||
### Lean internals
|
||||
|
||||
* **Performance**
|
||||
* Some core algorithms have been rewritten in C++ for performance.
|
||||
* [#4910](https://github.com/leanprover/lean4/pull/4910) and [#4912](https://github.com/leanprover/lean4/pull/4912) reimplement `instantiateLevelMVars`.
|
||||
* [#4915](https://github.com/leanprover/lean4/pull/4915), [#4922](https://github.com/leanprover/lean4/pull/4922), and [#4931](https://github.com/leanprover/lean4/pull/4931) reimplement `instantiateExprMVars`, 30% faster on a benchmark.
|
||||
* [#4934](https://github.com/leanprover/lean4/pull/4934) has optimizations for the kernel's `Expr` equality test.
|
||||
* [#4990](https://github.com/leanprover/lean4/pull/4990) fixes bug in hashing for the kernel's `Expr` equality test.
|
||||
* [#4935](https://github.com/leanprover/lean4/pull/4935) and [#4936](https://github.com/leanprover/lean4/pull/4936) skip some `PreDefinition` transformations if they are not needed.
|
||||
* [#5225](https://github.com/leanprover/lean4/pull/5225) adds caching for visited exprs at `CheckAssignmentQuick` in `ExprDefEq`.
|
||||
* [#5226](https://github.com/leanprover/lean4/pull/5226) maximizes term sharing at `instantiateMVarDeclMVars`, used by `runTactic`.
|
||||
* **Diagnostics and profiling**
|
||||
* [#4923](https://github.com/leanprover/lean4/pull/4923) adds profiling for `instantiateMVars` in `Lean.Elab.MutualDef`, which can be a bottleneck there.
|
||||
* [#4924](https://github.com/leanprover/lean4/pull/4924) adds diagnostics for large theorems, controlled by the `diagnostics.threshold.proofSize` option.
|
||||
* [#4897](https://github.com/leanprover/lean4/pull/4897) improves display of diagnostic results.
|
||||
* **Other fixes or improvements**
|
||||
* [#4921](https://github.com/leanprover/lean4/pull/4921) cleans up `Expr.betaRev`.
|
||||
* [#4940](https://github.com/leanprover/lean4/pull/4940) fixes tests by not writing directly to stdout, which is unreliable now that elaboration and reporting are executed in separate threads.
|
||||
* [#4955](https://github.com/leanprover/lean4/pull/4955) documents that `stderrAsMessages` is now the default on the command line as well.
|
||||
* [#4647](https://github.com/leanprover/lean4/pull/4647) adjusts documentation for building on macOS.
|
||||
* [#4987](https://github.com/leanprover/lean4/pull/4987) makes regular mvar assignments take precedence over delayed ones in `instantiateMVars`. Normally delayed assignment metavariables are never directly assigned, but on errors Lean assigns `sorry` to unassigned metavariables.
|
||||
* [#4967](https://github.com/leanprover/lean4/pull/4967) adds linter name to errors when a linter crashes.
|
||||
* [#5043](https://github.com/leanprover/lean4/pull/5043) cleans up command line snapshots logic.
|
||||
* [#5067](https://github.com/leanprover/lean4/pull/5067) minimizes some imports.
|
||||
* [#5068](https://github.com/leanprover/lean4/pull/5068) generalizes the monad for `addMatcherInfo`.
|
||||
* [f71a1f](https://github.com/leanprover/lean4/commit/f71a1fb4ae958fccb3ad4d48786a8f47ced05c15) adds missing test for [#5126](https://github.com/leanprover/lean4/issues/5126).
|
||||
* [#5201](https://github.com/leanprover/lean4/pull/5201) restores a test.
|
||||
* [#3698](https://github.com/leanprover/lean4/pull/3698) fixes a bug where label attributes did not pass on the attribute kind.
|
||||
* Typos: [#5080](https://github.com/leanprover/lean4/pull/5080), [#5150](https://github.com/leanprover/lean4/pull/5150), [#5202](https://github.com/leanprover/lean4/pull/5202)
|
||||
|
||||
### Compiler, runtime, and FFI
|
||||
|
||||
* [#3106](https://github.com/leanprover/lean4/pull/3106) moves frontend to new snapshot architecture. Note that `Frontend.processCommand` and `FrontendM` are no longer used by Lean core, but they will be preserved.
|
||||
* [#4919](https://github.com/leanprover/lean4/pull/4919) adds missing include in runtime for `AUTO_THREAD_FINALIZATION` feature on Windows.
|
||||
* [#4941](https://github.com/leanprover/lean4/pull/4941) adds more `LEAN_EXPORT`s for Windows.
|
||||
* [#4911](https://github.com/leanprover/lean4/pull/4911) improves formatting of CLI help text for the frontend.
|
||||
* [#4950](https://github.com/leanprover/lean4/pull/4950) improves file reading and writing.
|
||||
* `readBinFile` and `readFile` now only require two system calls (`stat` + `read`) instead of one `read` per 1024 byte chunk.
|
||||
* `Handle.getLine` and `Handle.putStr` no longer get tripped up by NUL characters.
|
||||
* [#4971](https://github.com/leanprover/lean4/pull/4971) handles the SIGBUS signal when detecting stack overflows.
|
||||
* [#5062](https://github.com/leanprover/lean4/pull/5062) avoids overwriting existing signal handlers, like in [rust-lang/rust#69685](https://github.com/rust-lang/rust/pull/69685).
|
||||
* [#4860](https://github.com/leanprover/lean4/pull/4860) improves workarounds for building on Windows. Splits `libleanshared` on Windows to avoid symbol limit, removes the `LEAN_EXPORT` denylist workaround, adds missing `LEAN_EXPORT`s.
|
||||
* [#4952](https://github.com/leanprover/lean4/pull/4952) output panics into Lean's redirected stderr, ensuring panics ARE visible as regular messages in the language server and properly ordered in relation to other messages on the command line.
|
||||
* [#4963](https://github.com/leanprover/lean4/pull/4963) links LibUV.
|
||||
|
||||
### Lake
|
||||
|
||||
* [#5030](https://github.com/leanprover/lean4/pull/5030) removes dead code.
|
||||
* [#4770](https://github.com/leanprover/lean4/pull/4770) adds additional fields to the package configuration which will be used by Reservoir. See the PR description for details.
|
||||
|
||||
|
||||
### DevOps/CI
|
||||
* [#4914](https://github.com/leanprover/lean4/pull/4914) and [#4937](https://github.com/leanprover/lean4/pull/4937) improve the release checklist.
|
||||
* [#4925](https://github.com/leanprover/lean4/pull/4925) ignores stale leanpkg tests.
|
||||
* [#5003](https://github.com/leanprover/lean4/pull/5003) upgrades `actions/cache` in CI.
|
||||
* [#5010](https://github.com/leanprover/lean4/pull/5010) sets `save-always` in cache actions in CI.
|
||||
* [#5008](https://github.com/leanprover/lean4/pull/5008) adds more libuv search patterns for the speedcenter.
|
||||
* [#5009](https://github.com/leanprover/lean4/pull/5009) reduce number of runs in the speedcenter for "fast" benchmarks from 10 to 3.
|
||||
* [#5014](https://github.com/leanprover/lean4/pull/5014) adjusts lakefile editing to use new `git` syntax in `pr-release` workflow.
|
||||
* [#5025](https://github.com/leanprover/lean4/pull/5025) has `pr-release` workflow pass `--retry` to `curl`.
|
||||
* [#5022](https://github.com/leanprover/lean4/pull/5022) builds MacOS Aarch64 release for PRs by default.
|
||||
* [#5045](https://github.com/leanprover/lean4/pull/5045) adds libuv to the required packages heading in macos docs.
|
||||
* [#5034](https://github.com/leanprover/lean4/pull/5034) fixes the install name of `libleanshared_1` on macOS.
|
||||
* [#5051](https://github.com/leanprover/lean4/pull/5051) fixes Windows stage 0.
|
||||
* [#5052](https://github.com/leanprover/lean4/pull/5052) fixes 32bit stage 0 builds in CI.
|
||||
* [#5057](https://github.com/leanprover/lean4/pull/5057) avoids rebuilding `leanmanifest` in each build.
|
||||
* [#5099](https://github.com/leanprover/lean4/pull/5099) makes `restart-on-label` workflow also filter by commit SHA.
|
||||
* [#4325](https://github.com/leanprover/lean4/pull/4325) adds CaDiCaL.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* [LibUV](https://libuv.org/) is now required to build Lean. This change only affects developers who compile Lean themselves instead of obtaining toolchains via `elan`. We have updated the official build instructions with information on how to obtain LibUV on our supported platforms. ([#4963](https://github.com/leanprover/lean4/pull/4963))
|
||||
|
||||
* Recursive definitions with a `decreasing_by` clause that begins with `simp_wf` may break. Try removing `simp_wf` or replacing it with `simp`. ([#5016](https://github.com/leanprover/lean4/pull/5016))
|
||||
|
||||
* The behavior of `rw [f]` where `f` is a non-recursive function defined by pattern matching changed.
|
||||
|
||||
For example, preciously, `rw [Option.map]` would rewrite `Option.map f o` to `match o with … `. Now this rewrite fails because it will use the equational lemmas, and these require constructors – just like for `List.map`.
|
||||
|
||||
Remedies:
|
||||
* Split on `o` before rewriting.
|
||||
* Use `rw [Option.map.eq_def]`, which rewrites any (saturated) application of `Option.map`.
|
||||
* Use `set_option backward.eqns.nonrecursive false` when *defining* the function in question.
|
||||
([#4154](https://github.com/leanprover/lean4/pull/4154))
|
||||
|
||||
* The unified handling of equation lemmas for recursive and non-recursive functions can break existing code, as there now can be extra equational lemmas:
|
||||
|
||||
* Explicit uses of `f.eq_2` might have to be adjusted if the numbering changed.
|
||||
|
||||
* Uses of `rw [f]` or `simp [f]` may no longer apply if they previously matched (and introduced a `match` statement), when the equational lemmas got more fine-grained.
|
||||
|
||||
In this case either case analysis on the parameters before rewriting helps, or setting the option `backward.eqns.deepRecursiveSplit false` while *defining* the function.
|
||||
|
||||
([#5129](https://github.com/leanprover/lean4/pull/5129), [#5207](https://github.com/leanprover/lean4/pull/5207))
|
||||
|
||||
* The `reduceCtorEq` simproc is now optional, and it might need to be included in lists of simp lemmas, like `simp only [reduceCtorEq]`. This simproc is responsible for reducing equalities of constructors. ([#5167](https://github.com/leanprover/lean4/pull/5167))
|
||||
|
||||
* `Nat.strongInductionOn` is now `Nat.strongRecOn` and `Nat.caseStrongInductionOn` to `Nat.caseStrongRecOn`. ([#5147](https://github.com/leanprover/lean4/pull/5147))
|
||||
|
||||
* The parameters to `Membership.mem` have been swapped, which affects all `Membership` instances. ([#5020](https://github.com/leanprover/lean4/pull/5020))
|
||||
|
||||
* The meanings of `List.getElem_drop` and `List.getElem_drop'` have been reversed and the first is now a simp lemma. ([#5210](https://github.com/leanprover/lean4/pull/5210))
|
||||
|
||||
* The `Parsec` library has moved from `Lean.Data.Parsec` to `Std.Internal.Parsec`. The `Parsec` type is now more general with a parameter for an iterable. Users parsing strings can migrate to `Parser` in the `Std.Internal.Parsec.String` namespace, which also includes string-focused parsing combinators. ([#4774](https://github.com/leanprover/lean4/pull/4774))
|
||||
|
||||
* The `Lean` module has switched from `Lean.HashMap` and `Lean.HashSet` to `Std.HashMap` and `Std.HashSet` ([#4943](https://github.com/leanprover/lean4/pull/4943)). `Lean.HashMap` and `Lean.HashSet` are now deprecated ([#4954](https://github.com/leanprover/lean4/pull/4954)) and will be removed in a future release. Users of `Lean` APIs that interact with hash maps, for example `Lean.Environment.const2ModIdx`, might encounter minor breakage due to the following changes from `Lean.HashMap` to `Std.HashMap`:
|
||||
* query functions use the term `get` instead of `find`, ([#4943](https://github.com/leanprover/lean4/pull/4943))
|
||||
* the notation `map[key]` no longer returns an optional value but instead expects a proof that the key is present in the map. The previous behavior is available via the `map[key]?` notation.
|
||||
|
||||
|
||||
v4.11.0
|
||||
----------
|
||||
@@ -21,7 +331,7 @@ v4.11.0
|
||||
|
||||
See breaking changes below.
|
||||
|
||||
PRs: [#4883](https://github.com/leanprover/lean4/pull/4883), [1242ff](https://github.com/leanprover/lean4/commit/1242ffbfb5a79296041683682268e770fc3cf820), [#5000](https://github.com/leanprover/lean4/pull/5000), [#5036](https://github.com/leanprover/lean4/pull/5036), [#5138](https://github.com/leanprover/lean4/pull/5138), [0edf1b](https://github.com/leanprover/lean4/commit/0edf1bac392f7e2fe0266b28b51c498306363a84).
|
||||
PRs: [#4883](https://github.com/leanprover/lean4/pull/4883), [#4814](https://github.com/leanprover/lean4/pull/4814), [#5000](https://github.com/leanprover/lean4/pull/5000), [#5036](https://github.com/leanprover/lean4/pull/5036), [#5138](https://github.com/leanprover/lean4/pull/5138), [0edf1b](https://github.com/leanprover/lean4/commit/0edf1bac392f7e2fe0266b28b51c498306363a84).
|
||||
|
||||
* **Recursive definitions**
|
||||
* Structural recursion can now be explicitly requested using
|
||||
@@ -381,7 +691,7 @@ v4.10.0
|
||||
|
||||
* **Commands**
|
||||
* [#4370](https://github.com/leanprover/lean4/pull/4370) makes the `variable` command fully elaborate binders during validation, fixing an issue where some errors would be reported only at the next declaration.
|
||||
* [#4408](https://github.com/leanprover/lean4/pull/4408) fixes a discrepency in universe parameter order between `theorem` and `def` declarations.
|
||||
* [#4408](https://github.com/leanprover/lean4/pull/4408) fixes a discrepancy in universe parameter order between `theorem` and `def` declarations.
|
||||
* [#4493](https://github.com/leanprover/lean4/pull/4493) and
|
||||
[#4482](https://github.com/leanprover/lean4/pull/4482) fix a discrepancy in the elaborators for `theorem`, `def`, and `example`,
|
||||
making `Prop`-valued `example`s and other definition commands elaborate like `theorem`s.
|
||||
@@ -443,7 +753,7 @@ v4.10.0
|
||||
* [#4454](https://github.com/leanprover/lean4/pull/4454) adds public `Name.isInternalDetail` function for filtering declarations using naming conventions for internal names.
|
||||
|
||||
* **Other fixes or improvements**
|
||||
* [#4416](https://github.com/leanprover/lean4/pull/4416) sorts the ouput of `#print axioms` for determinism.
|
||||
* [#4416](https://github.com/leanprover/lean4/pull/4416) sorts the output of `#print axioms` for determinism.
|
||||
* [#4528](https://github.com/leanprover/lean4/pull/4528) fixes error message range for the cdot focusing tactic.
|
||||
|
||||
### Language server, widgets, and IDE extensions
|
||||
@@ -479,7 +789,7 @@ v4.10.0
|
||||
* [#4372](https://github.com/leanprover/lean4/pull/4372) fixes linearity in `HashMap.insert` and `HashMap.erase`, leading to a 40% speedup in a replace-heavy workload.
|
||||
* `Option`
|
||||
* [#4403](https://github.com/leanprover/lean4/pull/4403) generalizes type of `Option.forM` from `Unit` to `PUnit`.
|
||||
* [#4504](https://github.com/leanprover/lean4/pull/4504) remove simp attribute from `Option.elim` and instead adds it to individal reduction lemmas, making unfolding less aggressive.
|
||||
* [#4504](https://github.com/leanprover/lean4/pull/4504) remove simp attribute from `Option.elim` and instead adds it to individual reduction lemmas, making unfolding less aggressive.
|
||||
* `Nat`
|
||||
* [#4242](https://github.com/leanprover/lean4/pull/4242) adds missing theorems for `n + 1` and `n - 1` normal forms.
|
||||
* [#4486](https://github.com/leanprover/lean4/pull/4486) makes `Nat.min_assoc` be a simp lemma.
|
||||
@@ -940,7 +1250,7 @@ While most changes could be considered to be a breaking change, this section mak
|
||||
In particular, tactics embedded in the type will no longer make use of the type of `value` in expressions such as `let x : type := value; body`.
|
||||
* Now functions defined by well-founded recursion are marked with `@[irreducible]` by default ([#4061](https://github.com/leanprover/lean4/pull/4061)).
|
||||
Existing proofs that hold by definitional equality (e.g. `rfl`) can be
|
||||
rewritten to explictly unfold the function definition (using `simp`,
|
||||
rewritten to explicitly unfold the function definition (using `simp`,
|
||||
`unfold`, `rw`), or the recursive function can be temporarily made
|
||||
semireducible (using `unseal f in` before the command), or the function
|
||||
definition itself can be marked as `@[semireducible]` to get the previous
|
||||
@@ -1559,7 +1869,7 @@ v4.7.0
|
||||
and `BitVec` as we begin making the APIs and simp normal forms for these types
|
||||
more complete and consistent.
|
||||
4. Laying the groundwork for the Std roadmap, as a library focused on
|
||||
essential datatypes not provided by the core langauge (e.g. `RBMap`)
|
||||
essential datatypes not provided by the core language (e.g. `RBMap`)
|
||||
and utilities such as basic IO.
|
||||
While we have achieved most of our initial aims in `v4.7.0-rc1`,
|
||||
some upstreaming will continue over the coming months.
|
||||
@@ -1570,7 +1880,7 @@ v4.7.0
|
||||
There is now kernel support for these functions.
|
||||
[#3376](https://github.com/leanprover/lean4/pull/3376).
|
||||
|
||||
* `omega`, our integer linear arithmetic tactic, is now availabe in the core langauge.
|
||||
* `omega`, our integer linear arithmetic tactic, is now available in the core language.
|
||||
* It is supplemented by a preprocessing tactic `bv_omega` which can solve goals about `BitVec`
|
||||
which naturally translate into linear arithmetic problems.
|
||||
[#3435](https://github.com/leanprover/lean4/pull/3435).
|
||||
@@ -1663,11 +1973,11 @@ v4.6.0
|
||||
/-
|
||||
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
|
||||
* The constructor `.done` instructs `simp` that the result does
|
||||
not need to be simplied further.
|
||||
not need to be simplified further.
|
||||
* The constructor `.visit` instructs `simp` to visit the resulting expression.
|
||||
* The constructor `.continue` instructs `simp` to try other simplification procedures.
|
||||
|
||||
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
|
||||
All three constructors take a `Result`. The `.continue` constructor may also take `none`.
|
||||
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
|
||||
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
|
||||
-/
|
||||
@@ -1879,7 +2189,7 @@ v4.5.0
|
||||
---------
|
||||
|
||||
* Modify the lexical syntax of string literals to have string gaps, which are escape sequences of the form `"\" newline whitespace*`.
|
||||
These have the interpetation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
|
||||
These have the interpretation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
|
||||
The following is equivalent to `"this is a string"`.
|
||||
```lean
|
||||
"this is \
|
||||
@@ -1902,7 +2212,7 @@ v4.5.0
|
||||
|
||||
If the well-founded relation you want to use is not the one that the
|
||||
`WellFoundedRelation` type class would infer for your termination argument,
|
||||
you can use `WellFounded.wrap` from the std libarary to explicitly give one:
|
||||
you can use `WellFounded.wrap` from the std library to explicitly give one:
|
||||
```diff
|
||||
-termination_by' ⟨r, hwf⟩
|
||||
+termination_by x => hwf.wrap x
|
||||
|
||||
@@ -73,7 +73,7 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
|
||||
The github repository will automatically update stage0 on `master` once
|
||||
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
|
||||
|
||||
If you have write access to the lean4 repository, you can also also manually
|
||||
If you have write access to the lean4 repository, you can also manually
|
||||
trigger that process, for example to be able to use new features in the compiler itself.
|
||||
You can do that on <https://github.com/leanprover/lean4/actions/workflows/update-stage0.yml>
|
||||
or using Github CLI with
|
||||
|
||||
@@ -71,6 +71,12 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Verso](https://github.com/leanprover/verso)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- The `SubVerso` dependency should be compatible with _every_ Lean release simultaneously, rather than following this workflow
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [import-graph](https://github.com/leanprover-community/import-graph)
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
|
||||
@@ -18,7 +18,7 @@ def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do
|
||||
else if h : idx - 1 < ctors.length then
|
||||
mvarId.apply (.const ctors[idx - 1] us)
|
||||
else
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} constructors"
|
||||
|
||||
open Elab Tactic
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ We now define the constant folding optimization that traverses a term if replace
|
||||
/-!
|
||||
The correctness of the `Term.constFold` is proved using induction, case-analysis, and the term simplifier.
|
||||
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
|
||||
use hypotheses such as `a = b` as rewriting/simplications rules.
|
||||
use hypotheses such as `a = b` as rewriting/simplifications rules.
|
||||
We use the `split` to break the nested `match` expression in the `plus` case into two cases.
|
||||
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
|
||||
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in
|
||||
|
||||
@@ -225,7 +225,7 @@ We now define the constant folding optimization that traverses a term if replace
|
||||
/-!
|
||||
The correctness of the `constFold` is proved using induction, case-analysis, and the term simplifier.
|
||||
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
|
||||
use hypotheses such as `a = b` as rewriting/simplications rules.
|
||||
use hypotheses such as `a = b` as rewriting/simplifications rules.
|
||||
We use the `split` to break the nested `match` expression in the `plus` case into two cases.
|
||||
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
|
||||
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in
|
||||
|
||||
@@ -29,7 +29,7 @@ inductive HasType : Expr → Ty → Prop
|
||||
|
||||
/-!
|
||||
We can easily show that if `e` has type `t₁` and type `t₂`, then `t₁` and `t₂` must be equal
|
||||
by using the the `cases` tactic. This tactic creates a new subgoal for every constructor,
|
||||
by using the `cases` tactic. This tactic creates a new subgoal for every constructor,
|
||||
and automatically discharges unreachable cases. The tactic combinator `tac₁ <;> tac₂` applies
|
||||
`tac₂` to each subgoal produced by `tac₁`. Then, the tactic `rfl` is used to close all produced
|
||||
goals using reflexivity.
|
||||
@@ -82,7 +82,7 @@ theorem Expr.typeCheck_correct (h₁ : HasType e ty) (h₂ : e.typeCheck ≠ .un
|
||||
/-!
|
||||
Now, we prove that if `Expr.typeCheck e` returns `Maybe.unknown`, then forall `ty`, `HasType e ty` does not hold.
|
||||
The notation `e.typeCheck` is sugar for `Expr.typeCheck e`. Lean can infer this because we explicitly said that `e` has type `Expr`.
|
||||
The proof is by induction on `e` and case analysis. The tactic `rename_i` is used to to rename "inaccessible" variables.
|
||||
The proof is by induction on `e` and case analysis. The tactic `rename_i` is used to rename "inaccessible" variables.
|
||||
We say a variable is inaccessible if it is introduced by a tactic (e.g., `cases`) or has been shadowed by another variable introduced
|
||||
by the user. Note that the tactic `simp [typeCheck]` is applied to all goal generated by the `induction` tactic, and closes
|
||||
the cases corresponding to the constructors `Expr.nat` and `Expr.bool`.
|
||||
|
||||
@@ -93,7 +93,7 @@ Meaning "Remote Procedure Call",this is a Lean function callable from widget cod
|
||||
Our method will take in the `name : Name` of a constant in the environment and return its type.
|
||||
By convention, we represent the input data as a `structure`.
|
||||
Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson` instnace.
|
||||
we need `FromJson` and `ToJson` instance.
|
||||
We'll see why the position field is needed later.
|
||||
-/
|
||||
|
||||
|
||||
@@ -396,7 +396,7 @@ Every expression in Lean has a natural computational interpretation, unless it i
|
||||
|
||||
* *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``.
|
||||
* *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``.
|
||||
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to to ``t``.
|
||||
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to ``t``.
|
||||
* *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md).
|
||||
|
||||
The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations:
|
||||
|
||||
@@ -171,7 +171,7 @@ of data contained in the container resulting in a new container that has the sam
|
||||
|
||||
`u <*> pure y = pure (. y) <*> u`.
|
||||
|
||||
This law is is a little more complicated, so don't sweat it too much. It states that the order that
|
||||
This law is a little more complicated, so don't sweat it too much. It states that the order that
|
||||
you wrap things shouldn't matter. One the left, you apply any applicative `u` over a pure wrapped
|
||||
object. On the right, you first wrap a function applying the object as an argument. Note that `(·
|
||||
y)` is short hand for: `fun f => f y`. Then you apply this to the first applicative `u`. These
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
* The `Lean` module has switched from `Lean.HashMap` and `Lean.HashSet` to `Std.HashMap` and `Std.HashSet`. `Lean.HashMap` and `Lean.HashSet` are now deprecated and will be removed in a future release. Users of `Lean` APIs that interact with hash maps, for example `Lean.Environment.const2ModIdx`, might encounter minor breakage due to the following breaking changes from `Lean.HashMap` to `Std.HashMap`:
|
||||
* query functions use the term `get` instead of `find`,
|
||||
* the notation `map[key]` no longer returns an optional value but expects a proof that the key is present in the map instead. The previous behavior is available via the `map[key]?` notation.
|
||||
@@ -1 +0,0 @@
|
||||
* #4963 [LibUV](https://libuv.org/) is now required to build Lean. This change only affects developers who compile Lean themselves instead of obtaining toolchains via `elan`. We have updated the official build instructions with information on how to obtain LibUV on our supported platforms.
|
||||
@@ -17,7 +17,7 @@ for f in $(git ls-files src ':!:src/lake/*' ':!:src/Leanc.lean'); do
|
||||
done
|
||||
|
||||
# special handling for Lake files due to its nested directory
|
||||
# copy the README to ensure the `stage0/src/lake` directory is comitted
|
||||
# copy the README to ensure the `stage0/src/lake` directory is committed
|
||||
for f in $(git ls-files 'src/lake/Lake/*' src/lake/Lake.lean src/lake/LakeMain.lean src/lake/README.md ':!:src/lakefile.toml'); do
|
||||
if [[ $f == *.lean ]]; then
|
||||
f=${f#src/lake}
|
||||
|
||||
@@ -80,6 +80,8 @@ noncomputable scoped instance (priority := low) propDecidable (a : Prop) : Decid
|
||||
noncomputable def decidableInhabited (a : Prop) : Inhabited (Decidable a) where
|
||||
default := inferInstance
|
||||
|
||||
instance (a : Prop) : Nonempty (Decidable a) := ⟨propDecidable a⟩
|
||||
|
||||
noncomputable def typeDecidableEq (α : Sort u) : DecidableEq α :=
|
||||
fun _ _ => inferInstance
|
||||
|
||||
@@ -121,11 +123,11 @@ theorem propComplete (a : Prop) : a = True ∨ a = False :=
|
||||
| Or.inl ha => Or.inl (eq_true ha)
|
||||
| Or.inr hn => Or.inr (eq_false hn)
|
||||
|
||||
-- this supercedes byCases in Decidable
|
||||
-- this supersedes byCases in Decidable
|
||||
theorem byCases {p q : Prop} (hpq : p → q) (hnpq : ¬p → q) : q :=
|
||||
Decidable.byCases (dec := propDecidable _) hpq hnpq
|
||||
|
||||
-- this supercedes byContradiction in Decidable
|
||||
-- this supersedes byContradiction in Decidable
|
||||
theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
|
||||
Decidable.byContradiction (dec := propDecidable _) h
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ attribute [simp] id_map
|
||||
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
|
||||
id_map x
|
||||
|
||||
@[simp] theorem Functor.map_map [Functor f] [LawfulFunctor f] (m : α → β) (g : β → γ) (x : f α) :
|
||||
g <$> m <$> x = (fun a => g (m a)) <$> x :=
|
||||
(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:
|
||||
@@ -83,12 +87,16 @@ class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m
|
||||
seq_assoc x g h := (by simp [← bind_pure_comp, ← bind_map, bind_assoc, pure_bind])
|
||||
|
||||
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
|
||||
attribute [simp] pure_bind bind_assoc
|
||||
attribute [simp] pure_bind bind_assoc bind_pure_comp
|
||||
|
||||
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
|
||||
show x >>= (fun a => pure (id a)) = x
|
||||
rw [bind_pure_comp, id_map]
|
||||
|
||||
/--
|
||||
Use `simp [← bind_pure_comp]` rather than `simp [map_eq_pure_bind]`,
|
||||
as `bind_pure_comp` is in the default simp set, so also using `map_eq_pure_bind` would cause a loop.
|
||||
-/
|
||||
theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α → β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
|
||||
rw [← bind_pure_comp]
|
||||
|
||||
@@ -109,10 +117,24 @@ theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α →
|
||||
|
||||
theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by
|
||||
rw [seqRight_eq]
|
||||
simp [map_eq_pure_bind, seq_eq_bind_map, const]
|
||||
simp only [map_eq_pure_bind, const, seq_eq_bind_map, bind_assoc, pure_bind, id_eq, bind_pure]
|
||||
|
||||
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
|
||||
rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
|
||||
rw [seqLeft_eq]
|
||||
simp only [map_eq_pure_bind, seq_eq_bind_map, bind_assoc, pure_bind, const_apply]
|
||||
|
||||
@[simp] theorem map_bind [Monad m] [LawfulMonad m] (f : β → γ) (x : m α) (g : α → m β) :
|
||||
f <$> (x >>= g) = x >>= fun a => f <$> g a := by
|
||||
rw [← bind_pure_comp, LawfulMonad.bind_assoc]
|
||||
simp [bind_pure_comp]
|
||||
|
||||
@[simp] theorem bind_map_left [Monad m] [LawfulMonad m] (f : α → β) (x : m α) (g : β → m γ) :
|
||||
((f <$> x) >>= fun b => g b) = (x >>= fun a => g (f a)) := by
|
||||
rw [← bind_pure_comp]
|
||||
simp only [bind_assoc, pure_bind]
|
||||
|
||||
@[simp] theorem Functor.map_unit [Monad m] [LawfulMonad m] {a : m PUnit} : (fun _ => PUnit.unit) <$> a = a := by
|
||||
simp [map]
|
||||
|
||||
/--
|
||||
An alternative constructor for `LawfulMonad` which has more
|
||||
@@ -161,9 +183,9 @@ end Id
|
||||
|
||||
instance : LawfulMonad Option := LawfulMonad.mk'
|
||||
(id_map := fun x => by cases x <;> rfl)
|
||||
(pure_bind := fun x f => rfl)
|
||||
(bind_assoc := fun x f g => by cases x <;> rfl)
|
||||
(bind_pure_comp := fun f x => by cases x <;> rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(bind_assoc := fun x _ _ => by cases x <;> rfl)
|
||||
(bind_pure_comp := fun _ x => by cases x <;> rfl)
|
||||
|
||||
instance : LawfulApplicative Option := inferInstance
|
||||
instance : LawfulFunctor Option := inferInstance
|
||||
|
||||
@@ -25,7 +25,7 @@ theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
|
||||
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
|
||||
|
||||
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α → ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
|
||||
simp[ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont, map_eq_pure_bind]
|
||||
simp [ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont]
|
||||
|
||||
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α → ExceptT ε m β) : (throw e >>= f) = throw e := by
|
||||
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
|
||||
@@ -43,7 +43,7 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
|
||||
|
||||
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α → β) (x : ExceptT ε m α)
|
||||
: (f <$> x).run = Except.map f <$> x.run := by
|
||||
simp [Functor.map, ExceptT.map, map_eq_pure_bind]
|
||||
simp [Functor.map, ExceptT.map, ←bind_pure_comp]
|
||||
apply bind_congr
|
||||
intro a; cases a <;> simp [Except.map]
|
||||
|
||||
@@ -62,7 +62,7 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
|
||||
intro
|
||||
| Except.error _ => simp
|
||||
| Except.ok _ =>
|
||||
simp [map_eq_pure_bind]; apply bind_congr; intro b;
|
||||
simp [←bind_pure_comp]; apply bind_congr; intro b;
|
||||
cases b <;> simp [comp, Except.map, const]
|
||||
|
||||
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
|
||||
@@ -84,14 +84,19 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
|
||||
pure_bind := by intros; apply ext; simp [run_bind]
|
||||
bind_assoc := by intros; apply ext; simp [run_bind]; apply bind_congr; intro a; cases a <;> simp
|
||||
|
||||
@[simp] theorem map_throw [Monad m] [LawfulMonad m] {α β : Type _} (f : α → β) (e : ε) :
|
||||
f <$> (throw e : ExceptT ε m α) = (throw e : ExceptT ε m β) := by
|
||||
simp only [ExceptT.instMonad, ExceptT.map, ExceptT.mk, throw, throwThe, MonadExceptOf.throw,
|
||||
pure_bind]
|
||||
|
||||
end ExceptT
|
||||
|
||||
/-! # Except -/
|
||||
|
||||
instance : LawfulMonad (Except ε) := LawfulMonad.mk'
|
||||
(id_map := fun x => by cases x <;> rfl)
|
||||
(pure_bind := fun a f => rfl)
|
||||
(bind_assoc := fun a f g => by cases a <;> rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(bind_assoc := fun a _ _ => by cases a <;> rfl)
|
||||
|
||||
instance : LawfulApplicative (Except ε) := inferInstance
|
||||
instance : LawfulFunctor (Except ε) := inferInstance
|
||||
@@ -175,7 +180,7 @@ theorem ext {x y : StateT σ m α} (h : ∀ s, x.run s = y.run s) : x = y :=
|
||||
simp [bind, StateT.bind, run]
|
||||
|
||||
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α → β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
|
||||
simp [Functor.map, StateT.map, run, map_eq_pure_bind]
|
||||
simp [Functor.map, StateT.map, run, ←bind_pure_comp]
|
||||
|
||||
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
|
||||
|
||||
@@ -210,13 +215,13 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
|
||||
|
||||
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by
|
||||
apply ext; intro s
|
||||
simp [map_eq_pure_bind, const]
|
||||
simp [←bind_pure_comp, const]
|
||||
apply bind_congr; intro p; cases p
|
||||
simp [Prod.eta]
|
||||
|
||||
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
|
||||
apply ext; intro s
|
||||
simp [map_eq_pure_bind]
|
||||
simp [←bind_pure_comp]
|
||||
|
||||
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
|
||||
id_map := by intros; apply ext; intros; simp[Prod.eta]
|
||||
@@ -224,7 +229,7 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
|
||||
seqLeft_eq := seqLeft_eq
|
||||
seqRight_eq := seqRight_eq
|
||||
pure_seq := by intros; apply ext; intros; simp
|
||||
bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
|
||||
bind_pure_comp := by intros; apply ext; intros; simp
|
||||
bind_map := by intros; rfl
|
||||
pure_bind := by intros; apply ext; intros; simp
|
||||
bind_assoc := by intros; apply ext; intros; simp
|
||||
|
||||
@@ -823,6 +823,7 @@ theorem iff_iff_implies_and_implies {a b : Prop} : (a ↔ b) ↔ (a → b) ∧ (
|
||||
protected theorem Iff.rfl {a : Prop} : a ↔ a :=
|
||||
Iff.refl a
|
||||
|
||||
-- And, also for backward compatibility, we try `Iff.rfl.` using `exact` (see #5366)
|
||||
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
|
||||
|
||||
theorem Iff.of_eq (h : a = b) : a ↔ b := h ▸ Iff.rfl
|
||||
@@ -837,6 +838,9 @@ instance : Trans Iff Iff Iff where
|
||||
theorem Eq.comm {a b : α} : a = b ↔ b = a := Iff.intro Eq.symm Eq.symm
|
||||
theorem eq_comm {a b : α} : a = b ↔ b = a := Eq.comm
|
||||
|
||||
theorem HEq.comm {a : α} {b : β} : HEq a b ↔ HEq b a := Iff.intro HEq.symm HEq.symm
|
||||
theorem heq_comm {a : α} {b : β} : HEq a b ↔ HEq b a := HEq.comm
|
||||
|
||||
@[symm] theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp
|
||||
theorem Iff.comm: (a ↔ b) ↔ (b ↔ a) := Iff.intro Iff.symm Iff.symm
|
||||
theorem iff_comm : (a ↔ b) ↔ (b ↔ a) := Iff.comm
|
||||
@@ -1892,7 +1896,8 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
|
||||
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
|
||||
exact congrArg extfunApp (Quot.sound h)
|
||||
|
||||
instance {α : Sort u} {β : α → Sort v} [∀ a, Subsingleton (β a)] : Subsingleton (∀ a, β a) where
|
||||
instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingleton (β a)] :
|
||||
Subsingleton (∀ a, β a) where
|
||||
allEq f g := funext fun a => Subsingleton.elim (f a) (g a)
|
||||
|
||||
/-! # Squash -/
|
||||
@@ -2055,7 +2060,7 @@ class IdempotentOp (op : α → α → α) : Prop where
|
||||
`LeftIdentify op o` indicates `o` is a left identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and
|
||||
is used primarily for infering the identity using class resoluton.
|
||||
is used primarily for inferring the identity using class resolution.
|
||||
-/
|
||||
class LeftIdentity (op : α → β → β) (o : outParam α) : Prop
|
||||
|
||||
@@ -2071,7 +2076,7 @@ class LawfulLeftIdentity (op : α → β → β) (o : outParam α) extends LeftI
|
||||
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
primarily for inferring the identity using class resolution.
|
||||
-/
|
||||
class RightIdentity (op : α → β → α) (o : outParam β) : Prop
|
||||
|
||||
@@ -2087,7 +2092,7 @@ class LawfulRightIdentity (op : α → β → α) (o : outParam β) extends Righ
|
||||
`Identity op o` indicates `o` is a left and right identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
primarily for inferring the identity using class resolution.
|
||||
-/
|
||||
class Identity (op : α → α → α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import Init.Data.Prod
|
||||
import Init.Data.AC
|
||||
import Init.Data.Queue
|
||||
import Init.Data.Channel
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
import Init.Data.BEq
|
||||
import Init.Data.Subtype
|
||||
|
||||
@@ -15,3 +15,4 @@ import Init.Data.Array.BasicAux
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.TakeDrop
|
||||
import Init.Data.Array.Bootstrap
|
||||
import Init.Data.Array.GetLit
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Joachim Breitner, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Attach
|
||||
|
||||
namespace Array
|
||||
@@ -26,4 +27,154 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
@[inline] def attach (xs : Array α) : Array {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||||
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem _root_.List.attach_toArray {l : List α} :
|
||||
l.toArray.attach = (l.attachWith (· ∈ l.toArray) (by simp)).toArray := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toList_attachWith {l : Array α} {P : α → Prop} {H : ∀ x ∈ l, P x} :
|
||||
(l.attachWith P H).toList = l.toList.attachWith P (by simpa [mem_toList] using H) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem toList_attach {α : Type _} {l : Array α} :
|
||||
l.attach.toList = l.toList.attachWith (· ∈ l) (by simp [mem_toList]) := by
|
||||
simp [attach]
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
|
||||
|
||||
We use it by providing a simp lemma `l.attach.unattach = l`, and simp lemmas which recognize higher order
|
||||
functions applied to `l : Array { x // p x }` which only depend on the value, not the predicate, and rewrite these
|
||||
in terms of a simpler function applied to `l.unattach`.
|
||||
|
||||
Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as in intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) := l.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {α : Type _} {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
|
||||
@[simp] theorem unattach_push {α : Type _} {p : α → Prop} {a : { x // p x }} {l : Array { x // p x }} :
|
||||
(l.push a).unattach = l.unattach.push a.1 := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem size_unattach {α : Type _} {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.unattach.size = l.size := by
|
||||
unfold unattach
|
||||
simp
|
||||
|
||||
@[simp] theorem _root_.List.unattach_toArray {α : Type _} {p : α → Prop} {l : List { x // p x }} :
|
||||
l.toArray.unattach = l.unattach.toArray := by
|
||||
simp [unattach, List.unattach]
|
||||
|
||||
@[simp] theorem toList_unattach {α : Type _} {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.unattach.toList = l.toList.unattach := by
|
||||
simp [unattach, List.unattach]
|
||||
|
||||
@[simp] theorem unattach_attach {α : Type _} (l : Array α) : l.attach.unattach = l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_attachWith {α : Type _} {p : α → Prop} {l : Array α}
|
||||
{H : ∀ a ∈ l, p a} :
|
||||
(l.attachWith p H).unattach = l := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||||
|
||||
/--
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
theorem foldl_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} :
|
||||
l.foldl f x = l.unattach.foldl g x := by
|
||||
cases l
|
||||
simp only [List.foldl_toArray', List.unattach_toArray]
|
||||
rw [List.foldl_subtype] -- Why can't simp do this?
|
||||
simp [hf]
|
||||
|
||||
/-- Variant of `foldl_subtype` with side condition to check `stop = l.size`. -/
|
||||
@[simp] theorem foldl_subtype' {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} (h : stop = l.size) :
|
||||
l.foldl f x 0 stop = l.unattach.foldl g x := by
|
||||
subst h
|
||||
rwa [foldl_subtype]
|
||||
|
||||
/--
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
theorem foldr_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} :
|
||||
l.foldr f x = l.unattach.foldr g x := by
|
||||
cases l
|
||||
simp only [List.foldr_toArray', List.unattach_toArray]
|
||||
rw [List.foldr_subtype]
|
||||
simp [hf]
|
||||
|
||||
/-- Variant of `foldr_subtype` with side condition to check `stop = l.size`. -/
|
||||
@[simp] theorem foldr_subtype' {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} (h : start = l.size) :
|
||||
l.foldr f x start 0 = l.unattach.foldr g x := by
|
||||
subst h
|
||||
rwa [foldr_subtype]
|
||||
|
||||
/--
|
||||
This lemma identifies maps over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem map_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → β} {g : α → β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.map f = l.unattach.map g := by
|
||||
cases l
|
||||
simp only [List.map_toArray, List.unattach_toArray]
|
||||
rw [List.map_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem filterMap_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → Option β} {g : α → Option β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.filterMap f = l.unattach.filterMap g := by
|
||||
cases l
|
||||
simp only [size_toArray, List.filterMap_toArray', List.unattach_toArray, List.length_unattach,
|
||||
mk.injEq]
|
||||
rw [List.filterMap_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
(l.filter f).unattach = l.unattach.filter g := by
|
||||
cases l
|
||||
simp [hf]
|
||||
rw [List.unattach_filter]
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.reverse.unattach = l.unattach.reverse := by
|
||||
cases l
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_append {p : α → Prop} {l₁ l₂ : Array { x // p x }} :
|
||||
(l₁ ++ l₂).unattach = l₁.unattach ++ l₂.unattach := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
simp
|
||||
|
||||
end Array
|
||||
|
||||
@@ -13,43 +13,75 @@ import Init.Data.ToString.Basic
|
||||
import Init.GetElem
|
||||
universe u v w
|
||||
|
||||
namespace Array
|
||||
/-! ### Array literal syntax -/
|
||||
|
||||
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
|
||||
|
||||
macro_rules
|
||||
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
|
||||
|
||||
variable {α : Type u}
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### Preliminary theorems -/
|
||||
|
||||
@[simp] theorem size_set (a : Array α) (i : Fin a.size) (v : α) : (set a i v).size = a.size :=
|
||||
List.length_set ..
|
||||
|
||||
@[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 :=
|
||||
List.length_concat ..
|
||||
|
||||
theorem ext (a b : Array α)
|
||||
(h₁ : a.size = b.size)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.size) → (hi₂ : i < b.size) → a[i] = b[i])
|
||||
: a = b := by
|
||||
let rec extAux (a b : List α)
|
||||
(h₁ : a.length = b.length)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.length) → (hi₂ : i < b.length) → a.get ⟨i, hi₁⟩ = b.get ⟨i, hi₂⟩)
|
||||
: a = b := by
|
||||
induction a generalizing b with
|
||||
| nil =>
|
||||
cases b with
|
||||
| nil => rfl
|
||||
| cons b bs => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons a as ih =>
|
||||
cases b with
|
||||
| nil => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons b bs =>
|
||||
have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have headEq : a = b := h₂ 0 hz₁ hz₂
|
||||
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
|
||||
have h₂' : (i : Nat) → (hi₁ : i < as.length) → (hi₂ : i < bs.length) → as.get ⟨i, hi₁⟩ = bs.get ⟨i, hi₂⟩ := by
|
||||
intro i hi₁ hi₂
|
||||
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have : (a::as).get ⟨i+1, hi₁'⟩ = (b::bs).get ⟨i+1, hi₂'⟩ := h₂ (i+1) hi₁' hi₂'
|
||||
apply this
|
||||
have tailEq : as = bs := ih bs h₁' h₂'
|
||||
rw [headEq, tailEq]
|
||||
cases a; cases b
|
||||
apply congrArg
|
||||
apply extAux
|
||||
assumption
|
||||
assumption
|
||||
|
||||
theorem ext' {as bs : Array α} (h : as.toList = bs.toList) : as = bs := by
|
||||
cases as; cases bs; simp at h; rw [h]
|
||||
|
||||
@[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).toList = acc.toList ++ as := by
|
||||
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toList_toArray (as : List α) : as.toArray.toList = as := rfl
|
||||
|
||||
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @toList_toArray
|
||||
|
||||
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
|
||||
|
||||
@[extern "lean_mk_array"]
|
||||
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
|
||||
toList := List.replicate n v
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
termination_by n - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
n.fold (flip Array.push) (mkEmpty n)
|
||||
|
||||
@[simp] theorem size_mkArray (n : Nat) (v : α) : (mkArray n v).size = n :=
|
||||
List.length_replicate ..
|
||||
|
||||
instance : EmptyCollection (Array α) := ⟨Array.empty⟩
|
||||
instance : Inhabited (Array α) where
|
||||
default := Array.empty
|
||||
|
||||
@[simp] def isEmpty (a : Array α) : Bool :=
|
||||
a.size = 0
|
||||
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
/-! ### Externs -/
|
||||
|
||||
/-- Low-level version of `size` that directly queries the C array object cached size.
|
||||
While this is not provable, `usize` always returns the exact size of the array since
|
||||
@@ -65,29 +97,6 @@ def usize (a : @& Array α) : USize := a.size.toUSize
|
||||
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
|
||||
a[i.toNat]
|
||||
|
||||
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
def back [Inhabited α] (a : Array α) : α :=
|
||||
a.get! (a.size - 1)
|
||||
|
||||
def get? (a : Array α) (i : Nat) : Option α :=
|
||||
if h : i < a.size then some a[i] else none
|
||||
|
||||
def back? (a : Array α) : Option α :=
|
||||
a.get? (a.size - 1)
|
||||
|
||||
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
|
||||
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
|
||||
have := h₁.symm ▸ h₂
|
||||
a[i]
|
||||
|
||||
@[simp] theorem size_set (a : Array α) (i : Fin a.size) (v : α) : (set a i v).size = a.size :=
|
||||
List.length_set ..
|
||||
|
||||
@[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 :=
|
||||
List.length_concat ..
|
||||
|
||||
/-- Low-level version of `fset` which is as fast as a C array fset.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fset` may be slightly slower than `uset`. -/
|
||||
@@ -95,6 +104,19 @@ abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size =
|
||||
def uset (a : Array α) (i : USize) (v : α) (h : i.toNat < a.size) : Array α :=
|
||||
a.set ⟨i.toNat, h⟩ v
|
||||
|
||||
@[extern "lean_array_pop"]
|
||||
def pop (a : Array α) : Array α where
|
||||
toList := a.toList.dropLast
|
||||
|
||||
@[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by
|
||||
match a with
|
||||
| ⟨[]⟩ => rfl
|
||||
| ⟨a::as⟩ => simp [pop, Nat.succ_sub_succ_eq_sub, size]
|
||||
|
||||
@[extern "lean_mk_array"]
|
||||
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
|
||||
toList := List.replicate n v
|
||||
|
||||
/--
|
||||
Swaps two entries in an array.
|
||||
|
||||
@@ -108,6 +130,10 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let a' := a.set i v₂
|
||||
a'.set (size_set a i v₂ ▸ j) v₁
|
||||
|
||||
@[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by
|
||||
show ((a.set i (a.get j)).set (size_set a i _ ▸ j) (a.get i)).size = a.size
|
||||
rw [size_set, size_set]
|
||||
|
||||
/--
|
||||
Swaps two entries in an array, or returns the array unchanged if either index is out of bounds.
|
||||
|
||||
@@ -121,6 +147,64 @@ def swap! (a : Array α) (i j : @& Nat) : Array α :=
|
||||
else a
|
||||
else a
|
||||
|
||||
/-! ### GetElem instance for `USize`, backed by `uget` -/
|
||||
|
||||
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
/-! ### Definitions -/
|
||||
|
||||
instance : EmptyCollection (Array α) := ⟨Array.empty⟩
|
||||
instance : Inhabited (Array α) where
|
||||
default := Array.empty
|
||||
|
||||
@[simp] def isEmpty (a : Array α) : Bool :=
|
||||
a.size = 0
|
||||
|
||||
@[specialize]
|
||||
def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) :
|
||||
∀ (i : Nat) (_ : i ≤ a.size), Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
p a[i] (b[i]'(hsz ▸ h)) && isEqvAux a b hsz p i (Nat.le_trans (Nat.le_add_right i 1) h)
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
isEqvAux a b h p a.size (Nat.le_refl a.size)
|
||||
else
|
||||
false
|
||||
|
||||
instance [BEq α] : BEq (Array α) :=
|
||||
⟨fun a b => isEqv a b BEq.beq⟩
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty 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 α :=
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
n.fold (flip Array.push) (mkEmpty n)
|
||||
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
|
||||
def back [Inhabited α] (a : Array α) : α :=
|
||||
a.get! (a.size - 1)
|
||||
|
||||
def get? (a : Array α) (i : Nat) : Option α :=
|
||||
if h : i < a.size then some a[i] else none
|
||||
|
||||
def back? (a : Array α) : Option α :=
|
||||
a.get? (a.size - 1)
|
||||
|
||||
@[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α :=
|
||||
let e := a.get i
|
||||
let a := a.set i v
|
||||
@@ -134,10 +218,6 @@ def swapAt! (a : Array α) (i : Nat) (v : α) : α × Array α :=
|
||||
have : Inhabited α := ⟨v⟩
|
||||
panic! ("index " ++ toString i ++ " out of bounds")
|
||||
|
||||
@[extern "lean_array_pop"]
|
||||
def pop (a : Array α) : Array α where
|
||||
toList := a.toList.dropLast
|
||||
|
||||
def shrink (a : Array α) (n : Nat) : Array α :=
|
||||
let rec loop
|
||||
| 0, a => a
|
||||
@@ -306,12 +386,12 @@ unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
|
||||
def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) :=
|
||||
-- Note: we cannot use `foldlM` here for the reference implementation because this calls
|
||||
-- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`)
|
||||
let rec map (i : Nat) (r : Array β) : m (Array β) := do
|
||||
if hlt : i < as.size then
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
termination_by as.size - i
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
map (i : Nat) (r : Array β) : m (Array β) := do
|
||||
if hlt : i < as.size then
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
map 0 (mkEmpty as.size)
|
||||
|
||||
@@ -375,7 +455,8 @@ unsafe def anyMUnsafe {α : Type u} {m : Type → Type w} [Monad m] (p : α →
|
||||
@[implemented_by anyMUnsafe]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
|
||||
let any (stop : Nat) (h : stop ≤ as.size) :=
|
||||
let rec loop (j : Nat) : m Bool := do
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) : m Bool := do
|
||||
if hlt : j < stop then
|
||||
have : j < as.size := Nat.lt_of_lt_of_le hlt h
|
||||
if (← p as[j]) then
|
||||
@@ -384,7 +465,6 @@ def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
loop (j+1)
|
||||
else
|
||||
pure false
|
||||
termination_by stop - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop start
|
||||
if h : stop ≤ as.size then
|
||||
@@ -466,16 +546,28 @@ def findRev? {α : Type} (as : Array α) (p : α → Bool) : Option α :=
|
||||
|
||||
@[inline]
|
||||
def findIdx? {α : Type u} (as : Array α) (p : α → Bool) : Option Nat :=
|
||||
let rec loop (j : Nat) :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
if p as[j] then some j else loop (j + 1)
|
||||
else none
|
||||
termination_by as.size - j
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
loop 0
|
||||
|
||||
def getIdx? [BEq α] (a : Array α) (v : α) : Option Nat :=
|
||||
a.findIdx? fun a => a == v
|
||||
a.findIdx? fun a => a == v
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) :=
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩;
|
||||
if a.get idx == v then some idx
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
|
||||
@[inline]
|
||||
def any (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
@@ -491,13 +583,6 @@ def contains [BEq α] (as : Array α) (a : α) : Bool :=
|
||||
def elem [BEq α] (a : α) (as : Array α) : Bool :=
|
||||
as.contains a
|
||||
|
||||
@[inline] def getEvenElems (as : Array α) : Array α :=
|
||||
(·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a =>
|
||||
if even then
|
||||
(false, r.push a)
|
||||
else
|
||||
(true, r)
|
||||
|
||||
/-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/
|
||||
-- This function is exported to C, where it is called by `Array.toList`
|
||||
-- (the projection) to implement this functionality.
|
||||
@@ -510,17 +595,6 @@ def toListImpl (as : Array α) : List α :=
|
||||
def toListAppend (as : Array α) (l : List α) : List α :=
|
||||
as.foldr List.cons l
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec a _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if a.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList a) ("," ++ Std.Format.line)) "]"
|
||||
|
||||
instance [ToString α] : ToString (Array α) where
|
||||
toString a := "#" ++ toString a.toList
|
||||
|
||||
protected def append (as : Array α) (bs : Array α) : Array α :=
|
||||
bs.foldl (init := as) fun r v => r.push v
|
||||
|
||||
@@ -543,47 +617,16 @@ def concatMap (f : α → Array β) (as : Array α) : Array β :=
|
||||
|
||||
`flatten #[#[a₁, a₂, ⋯], #[b₁, b₂, ⋯], ⋯]` = `#[a₁, a₂, ⋯, b₁, b₂, ⋯]`
|
||||
-/
|
||||
def flatten (as : Array (Array α)) : Array α :=
|
||||
@[inline] def flatten (as : Array (Array α)) : Array α :=
|
||||
as.foldl (init := empty) fun r a => r ++ a
|
||||
|
||||
end Array
|
||||
|
||||
export Array (mkArray)
|
||||
|
||||
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
|
||||
|
||||
macro_rules
|
||||
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
|
||||
|
||||
namespace Array
|
||||
|
||||
-- TODO(Leo): cleanup
|
||||
@[specialize]
|
||||
def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) (i : Nat) : Bool :=
|
||||
if h : i < a.size then
|
||||
have : i < b.size := hsz ▸ h
|
||||
p a[i] b[i] && isEqvAux a b hsz p (i+1)
|
||||
else
|
||||
true
|
||||
termination_by a.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
isEqvAux a b h p 0
|
||||
else
|
||||
false
|
||||
|
||||
instance [BEq α] : BEq (Array α) :=
|
||||
⟨fun a b => isEqv a b BEq.beq⟩
|
||||
|
||||
@[inline]
|
||||
def filter (p : α → Bool) (as : Array α) (start := 0) (stop := as.size) : Array α :=
|
||||
as.foldl (init := #[]) (start := start) (stop := stop) fun r a =>
|
||||
if p a then r.push a else r
|
||||
|
||||
@[inline]
|
||||
def filterM [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m (Array α) :=
|
||||
def filterM {α : Type} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m (Array α) :=
|
||||
as.foldlM (init := #[]) (start := start) (stop := stop) fun r a => do
|
||||
if (← p a) then return r.push a else return r
|
||||
|
||||
@@ -618,93 +661,25 @@ def partition (p : α → Bool) (as : Array α) : Array α × Array α := Id.run
|
||||
cs := cs.push a
|
||||
return (bs, cs)
|
||||
|
||||
theorem ext (a b : Array α)
|
||||
(h₁ : a.size = b.size)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.size) → (hi₂ : i < b.size) → a[i] = b[i])
|
||||
: a = b := by
|
||||
let rec extAux (a b : List α)
|
||||
(h₁ : a.length = b.length)
|
||||
(h₂ : (i : Nat) → (hi₁ : i < a.length) → (hi₂ : i < b.length) → a.get ⟨i, hi₁⟩ = b.get ⟨i, hi₂⟩)
|
||||
: a = b := by
|
||||
induction a generalizing b with
|
||||
| nil =>
|
||||
cases b with
|
||||
| nil => rfl
|
||||
| cons b bs => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons a as ih =>
|
||||
cases b with
|
||||
| nil => rw [List.length_cons] at h₁; injection h₁
|
||||
| cons b bs =>
|
||||
have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
|
||||
have headEq : a = b := h₂ 0 hz₁ hz₂
|
||||
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
|
||||
have h₂' : (i : Nat) → (hi₁ : i < as.length) → (hi₂ : i < bs.length) → as.get ⟨i, hi₁⟩ = bs.get ⟨i, hi₂⟩ := by
|
||||
intro i hi₁ hi₂
|
||||
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
|
||||
have : (a::as).get ⟨i+1, hi₁'⟩ = (b::bs).get ⟨i+1, hi₂'⟩ := h₂ (i+1) hi₁' hi₂'
|
||||
apply this
|
||||
have tailEq : as = bs := ih bs h₁' h₂'
|
||||
rw [headEq, tailEq]
|
||||
cases a; cases b
|
||||
apply congrArg
|
||||
apply extAux
|
||||
assumption
|
||||
assumption
|
||||
|
||||
theorem extLit {n : Nat}
|
||||
(a b : Array α)
|
||||
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
|
||||
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
end Array
|
||||
|
||||
-- CLEANUP the following code
|
||||
namespace Array
|
||||
|
||||
def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size) :=
|
||||
if h : i < a.size then
|
||||
let idx : Fin a.size := ⟨i, h⟩;
|
||||
if a.get idx == v then some idx
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
termination_by a.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
|
||||
@[simp] theorem size_swap (a : Array α) (i j : Fin a.size) : (a.swap i j).size = a.size := by
|
||||
show ((a.set i (a.get j)).set (size_set a i _ ▸ j) (a.get i)).size = a.size
|
||||
rw [size_set, size_set]
|
||||
|
||||
@[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by
|
||||
match a with
|
||||
| ⟨[]⟩ => rfl
|
||||
| ⟨a::as⟩ => simp [pop, Nat.succ_sub_succ_eq_sub, size]
|
||||
|
||||
theorem reverse.termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by
|
||||
rw [Nat.sub_sub, Nat.add_comm]
|
||||
exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h)
|
||||
|
||||
def reverse (as : Array α) : Array α :=
|
||||
if h : as.size ≤ 1 then
|
||||
as
|
||||
else
|
||||
loop as 0 ⟨as.size - 1, Nat.pred_lt (mt (fun h : as.size = 0 => h ▸ by decide) h)⟩
|
||||
where
|
||||
termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by
|
||||
rw [Nat.sub_sub, Nat.add_comm]
|
||||
exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h)
|
||||
loop (as : Array α) (i : Nat) (j : Fin as.size) :=
|
||||
if h : i < j then
|
||||
have := reverse.termination h
|
||||
have := termination h
|
||||
let as := as.swap ⟨i, Nat.lt_trans h j.2⟩ j
|
||||
have : j-1 < as.size := by rw [size_swap]; exact Nat.lt_of_le_of_lt (Nat.pred_le _) j.2
|
||||
loop as (i+1) ⟨j-1, this⟩
|
||||
else
|
||||
as
|
||||
termination_by j - i
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
if p (as.get ⟨as.size - 1, Nat.sub_lt h (by decide)⟩) then
|
||||
@@ -713,11 +688,11 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
as
|
||||
else
|
||||
as
|
||||
termination_by as.size
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec go (i : Nat) (r : Array α) : Array α :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
go (i : Nat) (r : Array α) : Array α :=
|
||||
if h : i < as.size then
|
||||
let a := as.get ⟨i, h⟩
|
||||
if p a then
|
||||
@@ -726,7 +701,6 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
r
|
||||
else
|
||||
r
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
go 0 #[]
|
||||
|
||||
@@ -734,6 +708,7 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
|
||||
This function takes worst case O(n) time because
|
||||
it has to backshift all elements at positions greater than `i`.-/
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
if h : i.val + 1 < a.size then
|
||||
let a' := a.swap ⟨i.val + 1, h⟩ i
|
||||
@@ -744,7 +719,8 @@ def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
termination_by a.size - i.val
|
||||
decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ i.isLt
|
||||
|
||||
theorem size_feraseIdx (a : Array α) (i : Fin a.size) : (a.feraseIdx i).size = a.size - 1 := by
|
||||
-- This is required in `Lean.Data.PersistentHashMap`.
|
||||
@[simp] theorem size_feraseIdx (a : Array α) (i : Fin a.size) : (a.feraseIdx i).size = a.size - 1 := by
|
||||
induction a, i using Array.feraseIdx.induct with
|
||||
| @case1 a i h a' _ ih =>
|
||||
unfold feraseIdx
|
||||
@@ -767,14 +743,14 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
|
||||
|
||||
/-- Insert element `a` at position `i`. -/
|
||||
@[inline] def insertAt (as : Array α) (i : Fin (as.size + 1)) (a : α) : Array α :=
|
||||
let rec loop (as : Array α) (j : Fin as.size) :=
|
||||
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
loop (as : Array α) (j : Fin as.size) :=
|
||||
if i.1 < j then
|
||||
let j' := ⟨j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2⟩
|
||||
let as := as.swap j' j
|
||||
loop as ⟨j', by rw [size_swap]; exact j'.2⟩
|
||||
else
|
||||
as
|
||||
termination_by j.1
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
let j := as.size
|
||||
let as := as.push a
|
||||
@@ -786,41 +762,7 @@ def insertAt! (as : Array α) (i : Nat) (a : α) : Array α :=
|
||||
insertAt as ⟨i, Nat.lt_succ_of_le h⟩ a
|
||||
else panic! "invalid index"
|
||||
|
||||
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i ≤ a.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux a n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem ext' {as bs : Array α} (h : as.toList = bs.toList) : as = bs := by
|
||||
cases as; cases bs; simp at h; rw [h]
|
||||
|
||||
@[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).toList = acc.toList ++ as := by
|
||||
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toList_toArray (as : List α) : as.toArray.toList = as := by
|
||||
simp [List.toArray, Array.mkEmpty]
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @toList_toArray
|
||||
|
||||
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
|
||||
|
||||
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
|
||||
apply ext'
|
||||
simp [toArrayLit, toList_toArray]
|
||||
have hle : n ≤ as.size := hsz ▸ Nat.le_refl _
|
||||
have hge : as.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
have := go n hle
|
||||
rw [List.drop_eq_nil_of_le hge] at this
|
||||
rw [this]
|
||||
where
|
||||
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) ▸ h₂) :=
|
||||
rfl
|
||||
|
||||
go (i : Nat) (hi : i ≤ as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
|
||||
induction i <;> simp [getLit_eq, List.get_drop_eq_drop, toListLitAux, List.drop, *]
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
@@ -832,7 +774,6 @@ def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : N
|
||||
false
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
/-- Return true iff `as` is a prefix of `bs`.
|
||||
@@ -843,24 +784,8 @@ def isPrefixOf [BEq α] (as bs : Array α) : Bool :=
|
||||
else
|
||||
false
|
||||
|
||||
private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat), i < as.size → Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
|
||||
a != as[i] && allDiffAuxAux as a i this
|
||||
|
||||
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
|
||||
@[specialize] def zipWithAux (f : α → β → γ) (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) : Array γ :=
|
||||
@[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
def zipWithAux (f : α → β → γ) (as : Array α) (bs : Array β) (i : Nat) (cs : Array γ) : Array γ :=
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
if h : i < bs.size then
|
||||
@@ -870,7 +795,6 @@ def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
cs
|
||||
else
|
||||
cs
|
||||
termination_by as.size - i
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
@[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ :=
|
||||
@@ -886,4 +810,66 @@ def split (as : Array α) (p : α → Bool) : Array α × Array α :=
|
||||
as.foldl (init := (#[], #[])) fun (as, bs) a =>
|
||||
if p a then (as.push a, bs) else (as, bs.push a)
|
||||
|
||||
/-! ## Auxiliary functions used in metaprogramming.
|
||||
|
||||
We do not intend to provide verification theorems for these functions.
|
||||
-/
|
||||
|
||||
/-! ### eraseReps -/
|
||||
|
||||
/--
|
||||
`O(|l|)`. Erase repeated adjacent elements. Keeps the first occurrence of each run.
|
||||
* `eraseReps #[1, 3, 2, 2, 2, 3, 5] = #[1, 3, 2, 3, 5]`
|
||||
-/
|
||||
def eraseReps {α} [BEq α] (as : Array α) : Array α :=
|
||||
if h : 0 < as.size then
|
||||
let ⟨last, r⟩ := as.foldl (init := (as[0], #[])) fun ⟨last, r⟩ a =>
|
||||
if a == last then ⟨last, r⟩ else ⟨a, r.push last⟩
|
||||
r.push last
|
||||
else
|
||||
#[]
|
||||
|
||||
/-! ### allDiff -/
|
||||
|
||||
private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat), i < as.size → Bool
|
||||
| 0, _ => true
|
||||
| i+1, h =>
|
||||
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
|
||||
a != as[i] && allDiffAuxAux as a i this
|
||||
|
||||
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
|
||||
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
if h : i < as.size then
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
|
||||
/-! ### getEvenElems -/
|
||||
|
||||
@[inline] def getEvenElems (as : Array α) : Array α :=
|
||||
(·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a =>
|
||||
if even then
|
||||
(false, r.push a)
|
||||
else
|
||||
(true, r)
|
||||
|
||||
/-! ### Repr and ToString -/
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec a _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if a.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList a) ("," ++ Std.Format.line)) "]"
|
||||
|
||||
instance [ToString α] : ToString (Array α) where
|
||||
toString a := "#" ++ toString a.toList
|
||||
|
||||
end Array
|
||||
|
||||
export Array (mkArray)
|
||||
|
||||
@@ -34,7 +34,7 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
|
||||
|
||||
@[simp] theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
|
||||
apply propext; apply Iff.intro
|
||||
· intro h; simp [toArray] at h; have := of_toArrayAux_eq_toArrayAux h rfl; exact this.1
|
||||
· intro h; simpa [toArray] using h
|
||||
· intro h; rw [h]
|
||||
|
||||
def Array.mapM' [Monad m] (f : α → m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
|
||||
|
||||
@@ -73,7 +73,7 @@ theorem foldr_eq_foldr_toList (f : α → β → β) (init : β) (arr : Array α
|
||||
|
||||
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
|
||||
|
||||
@[simp] theorem append_toList (arr arr' : Array α) :
|
||||
@[simp] theorem toList_append (arr arr' : Array α) :
|
||||
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [foldl_eq_foldl_toList]
|
||||
@@ -111,8 +111,8 @@ abbrev toList_eq := @toListImpl_eq
|
||||
@[deprecated pop_toList (since := "2024-09-09")]
|
||||
abbrev pop_data := @pop_toList
|
||||
|
||||
@[deprecated append_toList (since := "2024-09-09")]
|
||||
abbrev append_data := @append_toList
|
||||
@[deprecated toList_append (since := "2024-09-09")]
|
||||
abbrev append_data := @toList_append
|
||||
|
||||
@[deprecated appendList_toList (since := "2024-09-09")]
|
||||
abbrev appendList_data := @appendList_toList
|
||||
|
||||
@@ -5,43 +5,49 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.BEq
|
||||
import Init.ByCases
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size) (i : Nat) (hi : i ≤ a.size) (heqv : Array.isEqvAux a b hsz (fun x y => x = y) i) (j : Nat) (low : i ≤ j) (high : j < a.size) : a[j] = b[j]'(hsz ▸ high) := by
|
||||
by_cases h : i < a.size
|
||||
· unfold Array.isEqvAux at heqv
|
||||
simp [h] at heqv
|
||||
have hind := eq_of_isEqvAux a b hsz (i+1) (Nat.succ_le_of_lt h) heqv.2
|
||||
by_cases heq : i = j
|
||||
· subst heq; exact heqv.1
|
||||
· exact hind j (Nat.succ_le_of_lt (Nat.lt_of_le_of_ne low heq)) high
|
||||
· have heq : i = a.size := Nat.le_antisymm hi (Nat.ge_of_not_lt h)
|
||||
subst heq
|
||||
exact absurd (Nat.lt_of_lt_of_le high low) (Nat.lt_irrefl j)
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
theorem rel_of_isEqvAux
|
||||
(r : α → α → Bool) (a b : Array α) (hsz : a.size = b.size) (i : Nat) (hi : i ≤ a.size)
|
||||
(heqv : Array.isEqvAux a b hsz r i hi)
|
||||
(j : Nat) (hj : j < i) : r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi))) := by
|
||||
induction i with
|
||||
| zero => contradiction
|
||||
| succ i ih =>
|
||||
simp only [Array.isEqvAux, Bool.and_eq_true, decide_eq_true_eq] at heqv
|
||||
by_cases hj' : j < i
|
||||
next =>
|
||||
exact ih _ heqv.right hj'
|
||||
next =>
|
||||
replace hj' : j = i := Nat.eq_of_le_of_lt_succ (Nat.not_lt.mp hj') hj
|
||||
subst hj'
|
||||
exact heqv.left
|
||||
|
||||
theorem rel_of_isEqv (r : α → α → Bool) (a b : Array α) :
|
||||
Array.isEqv a b r → ∃ h : a.size = b.size, ∀ (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h ▸ h')) := by
|
||||
simp only [isEqv]
|
||||
split <;> rename_i h
|
||||
· exact fun h' => ⟨h, rel_of_isEqvAux r a b h a.size (Nat.le_refl ..) h'⟩
|
||||
· intro; contradiction
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) → a = b := by
|
||||
simp [Array.isEqv]
|
||||
split
|
||||
next hsz =>
|
||||
intro h
|
||||
have aux := eq_of_isEqvAux a b hsz 0 (Nat.zero_le ..) h
|
||||
exact ext a b hsz fun i h _ => aux i (Nat.zero_le ..) _
|
||||
next => intro; contradiction
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) (h : Array.isEqv a b (fun x y => x = y)) : a = b := by
|
||||
have ⟨h, h'⟩ := rel_of_isEqv (fun x y => x = y) a b h
|
||||
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
|
||||
|
||||
theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux a a rfl (fun x y => x = y) i = true := by
|
||||
unfold Array.isEqvAux
|
||||
split
|
||||
next h => simp [h, isEqvAux_self a (i+1)]
|
||||
next h => simp [h]
|
||||
termination_by a.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
theorem isEqvAux_self (r : α → α → Bool) (hr : ∀ a, r a a) (a : Array α) (i : Nat) (h : i ≤ a.size) :
|
||||
Array.isEqvAux a a rfl r i h = true := by
|
||||
induction i with
|
||||
| zero => simp [Array.isEqvAux]
|
||||
| succ i ih =>
|
||||
simp_all only [isEqvAux, Bool.and_self]
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
|
||||
theorem isEqv_self_beq [BEq α] [ReflBEq α] (a : Array α) : Array.isEqv a a (· == ·) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (· = ·) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
instance [DecidableEq α] : DecidableEq (Array α) :=
|
||||
|
||||
46
src/Init/Data/Array/GetLit.lean
Normal file
46
src/Init/Data/Array/GetLit.lean
Normal file
@@ -0,0 +1,46 @@
|
||||
/-
|
||||
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### getLit -/
|
||||
|
||||
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
|
||||
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
|
||||
have := h₁.symm ▸ h₂
|
||||
a[i]
|
||||
|
||||
theorem extLit {n : Nat}
|
||||
(a b : Array α)
|
||||
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
|
||||
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i ≤ a.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux a n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
|
||||
apply ext'
|
||||
simp [toArrayLit, toList_toArray]
|
||||
have hle : n ≤ as.size := hsz ▸ Nat.le_refl _
|
||||
have hge : as.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
have := go n hle
|
||||
rw [List.drop_eq_nil_of_le hge] at this
|
||||
rw [this]
|
||||
where
|
||||
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) ▸ h₂) :=
|
||||
rfl
|
||||
go (i : Nat) (hi : i ≤ as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
|
||||
induction i <;> simp [getLit_eq, List.get_drop_eq_drop, toListLitAux, List.drop, *]
|
||||
|
||||
end Array
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Ord
|
||||
|
||||
namespace Array
|
||||
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
|
||||
@@ -44,4 +45,11 @@ def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat ×
|
||||
else as
|
||||
sort as low high
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
Sort an array using `compare` to compare elements.
|
||||
-/
|
||||
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
|
||||
xs.qsort fun x y => compare x y |>.isLT
|
||||
|
||||
end Array
|
||||
|
||||
@@ -59,6 +59,22 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
else
|
||||
s
|
||||
|
||||
/--
|
||||
The empty subarray.
|
||||
-/
|
||||
protected def empty : Subarray α where
|
||||
array := #[]
|
||||
start := 0
|
||||
stop := 0
|
||||
start_le_stop := Nat.le_refl 0
|
||||
stop_le_array_size := Nat.le_refl 0
|
||||
|
||||
instance : EmptyCollection (Subarray α) :=
|
||||
⟨Subarray.empty⟩
|
||||
|
||||
instance : Inhabited (Subarray α) :=
|
||||
⟨{}⟩
|
||||
|
||||
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Array
|
||||
theorem exists_of_uset (self : Array α) (i d h) :
|
||||
∃ l₁ l₂, self.toList = l₁ ++ self[i] :: l₂ ∧ List.length l₁ = i.toNat ∧
|
||||
(self.uset i d h).toList = l₁ ++ d :: l₂ := by
|
||||
simpa [Array.getElem_eq_toList_getElem] using List.exists_of_set _
|
||||
simpa only [ugetElem_eq_getElem, getElem_eq_getElem_toList, uset, toList_set] using
|
||||
List.exists_of_set _
|
||||
|
||||
end Array
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Basic
|
||||
|
||||
@@ -269,8 +269,8 @@ Return the absolute value of a signed bitvector.
|
||||
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
|
||||
|
||||
/--
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned
|
||||
multiplication modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvmul`.
|
||||
-/
|
||||
@@ -676,6 +676,13 @@ result of appending a single bit to the front in the naive implementation).
|
||||
That is, the new bit is the least significant bit. -/
|
||||
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
|
||||
|
||||
/--
|
||||
`x.shiftConcat b` shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
|
||||
It is a non-dependent version of `concat` that does not change the total bitwidth.
|
||||
-/
|
||||
def shiftConcat (x : BitVec n) (b : Bool) : BitVec n :=
|
||||
(x.concat b).truncate n
|
||||
|
||||
/-- Prepend a single bit to the front of a bitvector, using big endian order (see `append`).
|
||||
That is, the new bit is the most significant bit. -/
|
||||
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
|
||||
|
||||
@@ -164,6 +164,17 @@ theorem getLsbD_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
(getLsbD x i ^^ (getLsbD y i ^^ carry i x y false)) := by
|
||||
simpa using getLsbD_add_add_bool i_lt x y false
|
||||
|
||||
theorem getElem_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
|
||||
(x + y + setWidth w (ofBool c))[i] =
|
||||
(x[i] ^^ (y[i] ^^ carry i x y c)) := by
|
||||
simp only [← getLsbD_eq_getElem]
|
||||
rw [getLsbD_add_add_bool]
|
||||
omega
|
||||
|
||||
theorem getElem_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
(x + y)[i] = (x[i] ^^ (y[i] ^^ carry i x y false)) := by
|
||||
simpa using getElem_add_add_bool i_lt x y false
|
||||
|
||||
theorem adc_spec (x y : BitVec w) (c : Bool) :
|
||||
adc x y c = (carry w x y c, x + y + setWidth w (ofBool c)) := by
|
||||
simp only [adc]
|
||||
@@ -368,6 +379,10 @@ theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
· simp
|
||||
· omega
|
||||
|
||||
theorem getElem_mul {x y : BitVec w} {i : Nat} (h : i < w) :
|
||||
(x * y)[i] = (mulRec x y w)[i] := by
|
||||
simp [mulRec_eq_mul_signExtend_setWidth]
|
||||
|
||||
/-! ## shiftLeft recurrence for bitblasting -/
|
||||
|
||||
/--
|
||||
@@ -438,6 +453,385 @@ theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
· simp [of_length_zero]
|
||||
· simp [shiftLeftRec_eq]
|
||||
|
||||
/-! # udiv/urem recurrence for bitblasting
|
||||
|
||||
In order to prove the correctness of the division algorithm on the integers,
|
||||
one shows that `n.div d = q` and `n.mod d = r` iff `n = d * q + r` and `0 ≤ r < d`.
|
||||
Mnemonic: `n` is the numerator, `d` is the denominator, `q` is the quotient, and `r` the remainder.
|
||||
|
||||
This *uniqueness of decomposition* is not true for bitvectors.
|
||||
For `n = 0, d = 3, w = 3`, we can write:
|
||||
- `0 = 0 * 3 + 0` (`q = 0`, `r = 0 < 3`.)
|
||||
- `0 = 2 * 3 + 2 = 6 + 2 ≃ 0 (mod 8)` (`q = 2`, `r = 2 < 3`).
|
||||
|
||||
Such examples can be created by choosing different `(q, r)` for a fixed `(d, n)`
|
||||
such that `(d * q + r)` overflows and wraps around to equal `n`.
|
||||
|
||||
This tells us that the division algorithm must have more restrictions than just the ones
|
||||
we have for integers. These restrictions are captured in `DivModState.Lawful`.
|
||||
The key idea is to state the relationship in terms of the toNat values of {n, d, q, r}.
|
||||
If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.udiv d = q` and `n.umod d = r`.
|
||||
|
||||
Following this, we implement the division algorithm by repeated shift-subtract.
|
||||
|
||||
References:
|
||||
- Fast 32-bit Division on the DSP56800E: Minimized nonrestoring division algorithm by David Baca
|
||||
- Bitwuzla sources for bitblasting.h
|
||||
-/
|
||||
|
||||
private theorem Nat.div_add_eq_left_of_lt {x y z : Nat} (hx : z ∣ x) (hy : y < z) (hz : 0 < z) :
|
||||
(x + y) / z = x / z := by
|
||||
refine Nat.div_eq_of_lt_le ?lo ?hi
|
||||
· apply Nat.le_trans
|
||||
· exact div_mul_le_self x z
|
||||
· omega
|
||||
· simp only [succ_eq_add_one, Nat.add_mul, Nat.one_mul]
|
||||
apply Nat.add_lt_add_of_le_of_lt
|
||||
· apply Nat.le_of_eq
|
||||
exact (Nat.div_eq_iff_eq_mul_left hz hx).mp rfl
|
||||
· exact hy
|
||||
|
||||
/-- If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.udiv d = q`. -/
|
||||
theorem udiv_eq_of_mul_add_toNat {d n q r : BitVec w} (hd : 0 < d)
|
||||
(hrd : r < d)
|
||||
(hdqnr : d.toNat * q.toNat + r.toNat = n.toNat) :
|
||||
n.udiv d = q := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_udiv]
|
||||
replace hdqnr : (d.toNat * q.toNat + r.toNat) / d.toNat = n.toNat / d.toNat := by
|
||||
simp [hdqnr]
|
||||
rw [Nat.div_add_eq_left_of_lt] at hdqnr
|
||||
· rw [← hdqnr]
|
||||
exact mul_div_right q.toNat hd
|
||||
· exact Nat.dvd_mul_right d.toNat q.toNat
|
||||
· exact hrd
|
||||
· exact hd
|
||||
|
||||
/-- If the division equation `d.toNat * q.toNat + r.toNat = n.toNat` holds,
|
||||
then `n.umod d = r`. -/
|
||||
theorem umod_eq_of_mul_add_toNat {d n q r : BitVec w} (hrd : r < d)
|
||||
(hdqnr : d.toNat * q.toNat + r.toNat = n.toNat) :
|
||||
n.umod d = r := by
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [toNat_umod]
|
||||
replace hdqnr : (d.toNat * q.toNat + r.toNat) % d.toNat = n.toNat % d.toNat := by
|
||||
simp [hdqnr]
|
||||
rw [Nat.add_mod, Nat.mul_mod_right] at hdqnr
|
||||
simp only [Nat.zero_add, mod_mod] at hdqnr
|
||||
replace hrd : r.toNat < d.toNat := by
|
||||
simpa [BitVec.lt_def] using hrd
|
||||
rw [Nat.mod_eq_of_lt hrd] at hdqnr
|
||||
simp [hdqnr]
|
||||
|
||||
/-! ### DivModState -/
|
||||
|
||||
/-- `DivModState` is a structure that maintains the state of recursive `divrem` calls. -/
|
||||
structure DivModState (w : Nat) : Type where
|
||||
/-- The number of bits in the numerator that are not yet processed -/
|
||||
wn : Nat
|
||||
/-- The number of bits in the remainder (and quotient) -/
|
||||
wr : Nat
|
||||
/-- The current quotient. -/
|
||||
q : BitVec w
|
||||
/-- The current remainder. -/
|
||||
r : BitVec w
|
||||
|
||||
|
||||
/-- `DivModArgs` contains the arguments to a `divrem` call which remain constant throughout
|
||||
execution. -/
|
||||
structure DivModArgs (w : Nat) where
|
||||
/-- the numerator (aka, dividend) -/
|
||||
n : BitVec w
|
||||
/-- the denumerator (aka, divisor)-/
|
||||
d : BitVec w
|
||||
|
||||
/-- A `DivModState` is lawful if the remainder width `wr` plus the numerator width `wn` equals `w`,
|
||||
and the bitvectors `r` and `n` have values in the bounds given by bitwidths `wr`, resp. `wn`.
|
||||
|
||||
This is a proof engineering choice: an alternative world could have been
|
||||
`r : BitVec wr` and `n : BitVec wn`, but this required much more dependent typing coercions.
|
||||
|
||||
Instead, we choose to declare all involved bitvectors as length `w`, and then prove that
|
||||
the values are within their respective bounds.
|
||||
|
||||
We start with `wn = w` and `wr = 0`, and then in each step, we decrement `wn` and increment `wr`.
|
||||
In this way, we grow a legal remainder in each loop iteration.
|
||||
-/
|
||||
structure DivModState.Lawful {w : Nat} (args : DivModArgs w) (qr : DivModState w) : Prop where
|
||||
/-- The sum of widths of the dividend and remainder is `w`. -/
|
||||
hwrn : qr.wr + qr.wn = w
|
||||
/-- The denominator is positive. -/
|
||||
hdPos : 0 < args.d
|
||||
/-- The remainder is strictly less than the denominator. -/
|
||||
hrLtDivisor : qr.r.toNat < args.d.toNat
|
||||
/-- The remainder is morally a `Bitvec wr`, and so has value less than `2^wr`. -/
|
||||
hrWidth : qr.r.toNat < 2^qr.wr
|
||||
/-- The quotient is morally a `Bitvec wr`, and so has value less than `2^wr`. -/
|
||||
hqWidth : qr.q.toNat < 2^qr.wr
|
||||
/-- The low `(w - wn)` bits of `n` obey the invariant for division. -/
|
||||
hdiv : args.n.toNat >>> qr.wn = args.d.toNat * qr.q.toNat + qr.r.toNat
|
||||
|
||||
/-- A lawful DivModState implies `w > 0`. -/
|
||||
def DivModState.Lawful.hw {args : DivModArgs w} {qr : DivModState w}
|
||||
{h : DivModState.Lawful args qr} : 0 < w := by
|
||||
have hd := h.hdPos
|
||||
rcases w with rfl | w
|
||||
· have hcontra : args.d = 0#0 := by apply Subsingleton.elim
|
||||
rw [hcontra] at hd
|
||||
simp at hd
|
||||
· omega
|
||||
|
||||
/-- An initial value with both `q, r = 0`. -/
|
||||
def DivModState.init (w : Nat) : DivModState w := {
|
||||
wn := w
|
||||
wr := 0
|
||||
q := 0#w
|
||||
r := 0#w
|
||||
}
|
||||
|
||||
/-- The initial state is lawful. -/
|
||||
def DivModState.lawful_init {w : Nat} (args : DivModArgs w) (hd : 0#w < args.d) :
|
||||
DivModState.Lawful args (DivModState.init w) := by
|
||||
simp only [BitVec.DivModState.init]
|
||||
exact {
|
||||
hwrn := by simp only; omega,
|
||||
hdPos := by assumption
|
||||
hrLtDivisor := by simp [BitVec.lt_def] at hd ⊢; assumption
|
||||
hrWidth := by simp [DivModState.init],
|
||||
hqWidth := by simp [DivModState.init],
|
||||
hdiv := by
|
||||
simp only [DivModState.init, toNat_ofNat, zero_mod, Nat.mul_zero, Nat.add_zero];
|
||||
rw [Nat.shiftRight_eq_div_pow]
|
||||
apply Nat.div_eq_of_lt args.n.isLt
|
||||
}
|
||||
|
||||
/--
|
||||
A lawful DivModState with a fully consumed dividend (`wn = 0`) witnesses that the
|
||||
quotient has been correctly computed.
|
||||
-/
|
||||
theorem DivModState.udiv_eq_of_lawful {n d : BitVec w} {qr : DivModState w}
|
||||
(h_lawful : DivModState.Lawful {n, d} qr)
|
||||
(h_final : qr.wn = 0) :
|
||||
n.udiv d = qr.q := by
|
||||
apply udiv_eq_of_mul_add_toNat h_lawful.hdPos h_lawful.hrLtDivisor
|
||||
have hdiv := h_lawful.hdiv
|
||||
simp only [h_final] at *
|
||||
omega
|
||||
|
||||
/--
|
||||
A lawful DivModState with a fully consumed dividend (`wn = 0`) witnesses that the
|
||||
remainder has been correctly computed.
|
||||
-/
|
||||
theorem DivModState.umod_eq_of_lawful {qr : DivModState w}
|
||||
(h : DivModState.Lawful {n, d} qr)
|
||||
(h_final : qr.wn = 0) :
|
||||
n.umod d = qr.r := by
|
||||
apply umod_eq_of_mul_add_toNat h.hrLtDivisor
|
||||
have hdiv := h.hdiv
|
||||
simp only [shiftRight_zero] at hdiv
|
||||
simp only [h_final] at *
|
||||
exact hdiv.symm
|
||||
|
||||
/-! ### DivModState.Poised -/
|
||||
|
||||
/--
|
||||
A `Poised` DivModState is a state which is `Lawful` and furthermore, has at least
|
||||
one numerator bit left to process `(0 < wn)`
|
||||
|
||||
The input to the shift subtractor is a legal input to `divrem`, and we also need to have an
|
||||
input bit to perform shift subtraction on, and thus we need `0 < wn`.
|
||||
-/
|
||||
structure DivModState.Poised {w : Nat} (args : DivModArgs w) (qr : DivModState w)
|
||||
extends DivModState.Lawful args qr : Type where
|
||||
/-- Only perform a round of shift-subtract if we have dividend bits. -/
|
||||
hwn_lt : 0 < qr.wn
|
||||
|
||||
/--
|
||||
In the shift subtract input, the dividend is at least one bit long (`wn > 0`), so
|
||||
the remainder has bits to be computed (`wr < w`).
|
||||
-/
|
||||
def DivModState.wr_lt_w {qr : DivModState w} (h : qr.Poised args) : qr.wr < w := by
|
||||
have hwrn := h.hwrn
|
||||
have hwn_lt := h.hwn_lt
|
||||
omega
|
||||
|
||||
/-! ### Division shift subtractor -/
|
||||
|
||||
/--
|
||||
One round of the division algorithm, that tries to perform a subtract shift.
|
||||
Note that this should only be called when `r.msb = false`, so we will not overflow.
|
||||
-/
|
||||
def divSubtractShift (args : DivModArgs w) (qr : DivModState w) : DivModState w :=
|
||||
let {n, d} := args
|
||||
let wn := qr.wn - 1
|
||||
let wr := qr.wr + 1
|
||||
let r' := shiftConcat qr.r (n.getLsbD wn)
|
||||
if r' < d then {
|
||||
q := qr.q.shiftConcat false, -- If `r' < d`, then we do not have a quotient bit.
|
||||
r := r'
|
||||
wn, wr
|
||||
} else {
|
||||
q := qr.q.shiftConcat true, -- Otherwise, `r' ≥ d`, and we have a quotient bit.
|
||||
r := r' - d -- we subtract to maintain the invariant that `r < d`.
|
||||
wn, wr
|
||||
}
|
||||
|
||||
/-- The value of shifting right by `wn - 1` equals shifting by `wn` and grabbing the lsb at `(wn - 1)`. -/
|
||||
theorem DivModState.toNat_shiftRight_sub_one_eq
|
||||
{args : DivModArgs w} {qr : DivModState w} (h : qr.Poised args) :
|
||||
args.n.toNat >>> (qr.wn - 1)
|
||||
= (args.n.toNat >>> qr.wn) * 2 + (args.n.getLsbD (qr.wn - 1)).toNat := by
|
||||
show BitVec.toNat (args.n >>> (qr.wn - 1)) = _
|
||||
have {..} := h -- break the structure down for `omega`
|
||||
rw [shiftRight_sub_one_eq_shiftConcat args.n h.hwn_lt]
|
||||
rw [toNat_shiftConcat_eq_of_lt (k := w - qr.wn)]
|
||||
· simp
|
||||
· omega
|
||||
· apply BitVec.toNat_ushiftRight_lt
|
||||
omega
|
||||
|
||||
/--
|
||||
This is used when proving the correctness of the divison algorithm,
|
||||
where we know that `r < d`.
|
||||
We then want to show that `((r.shiftConcat b) - d) < d` as the loop invariant.
|
||||
In arithmetic, this is the same as showing that
|
||||
`r * 2 + 1 - d < d`, which this theorem establishes.
|
||||
-/
|
||||
private theorem two_mul_add_sub_lt_of_lt_of_lt_two (h : a < x) (hy : y < 2) :
|
||||
2 * a + y - x < x := by omega
|
||||
|
||||
/-- We show that the output of `divSubtractShift` is lawful, which tells us that it
|
||||
obeys the division equation. -/
|
||||
theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
|
||||
DivModState.Lawful args (divSubtractShift args qr) := by
|
||||
rcases args with ⟨n, d⟩
|
||||
simp only [divSubtractShift, decide_eq_true_eq]
|
||||
-- We add these hypotheses for `omega` to find them later.
|
||||
have ⟨⟨hrwn, hd, hrd, hr, hn, hrnd⟩, hwn_lt⟩ := h
|
||||
have : d.toNat * (qr.q.toNat * 2) = d.toNat * qr.q.toNat * 2 := by rw [Nat.mul_assoc]
|
||||
by_cases rltd : shiftConcat qr.r (n.getLsbD (qr.wn - 1)) < d
|
||||
· simp only [rltd, ↓reduceIte]
|
||||
constructor <;> try bv_omega
|
||||
case pos.hrWidth => apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case pos.hqWidth => apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case pos.hdiv =>
|
||||
simp [qr.toNat_shiftRight_sub_one_eq h, h.hdiv, this,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hqWidth]
|
||||
omega
|
||||
· simp only [rltd, ↓reduceIte]
|
||||
constructor <;> try bv_omega
|
||||
case neg.hrLtDivisor =>
|
||||
simp only [lt_def, Nat.not_lt] at rltd
|
||||
rw [BitVec.toNat_sub_of_le rltd,
|
||||
toNat_shiftConcat_eq_of_lt (hk := qr.wr_lt_w h) (hx := h.hrWidth),
|
||||
Nat.mul_comm]
|
||||
apply two_mul_add_sub_lt_of_lt_of_lt_two <;> bv_omega
|
||||
case neg.hrWidth =>
|
||||
simp only
|
||||
have hdr' : d ≤ (qr.r.shiftConcat (n.getLsbD (qr.wn - 1))) :=
|
||||
BitVec.not_lt_iff_le.mp rltd
|
||||
have hr' : ((qr.r.shiftConcat (n.getLsbD (qr.wn - 1)))).toNat < 2 ^ (qr.wr + 1) := by
|
||||
apply toNat_shiftConcat_lt_of_lt <;> bv_omega
|
||||
rw [BitVec.toNat_sub_of_le hdr']
|
||||
omega
|
||||
case neg.hqWidth =>
|
||||
apply toNat_shiftConcat_lt_of_lt <;> omega
|
||||
case neg.hdiv =>
|
||||
have rltd' := (BitVec.not_lt_iff_le.mp rltd)
|
||||
simp only [qr.toNat_shiftRight_sub_one_eq h,
|
||||
BitVec.toNat_sub_of_le rltd',
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth]
|
||||
simp only [BitVec.le_def,
|
||||
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth] at rltd'
|
||||
simp only [toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hqWidth, h.hdiv, Nat.mul_add]
|
||||
bv_omega
|
||||
|
||||
/-! ### Core division algorithm circuit -/
|
||||
|
||||
/-- A recursive definition of division for bitblasting, in terms of a shift-subtraction circuit. -/
|
||||
def divRec {w : Nat} (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
DivModState w :=
|
||||
match m with
|
||||
| 0 => qr
|
||||
| m + 1 => divRec m args <| divSubtractShift args qr
|
||||
|
||||
@[simp]
|
||||
theorem divRec_zero (qr : DivModState w) :
|
||||
divRec 0 args qr = qr := rfl
|
||||
|
||||
@[simp]
|
||||
theorem divRec_succ (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec (m + 1) args qr =
|
||||
divRec m args (divSubtractShift args qr) := rfl
|
||||
|
||||
/-- The output of `divRec` is a lawful state -/
|
||||
theorem lawful_divRec {args : DivModArgs w} {qr : DivModState w}
|
||||
(h : DivModState.Lawful args qr) :
|
||||
DivModState.Lawful args (divRec qr.wn args qr) := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
exact h
|
||||
case succ wn' ih =>
|
||||
simp only [divRec_succ]
|
||||
apply ih
|
||||
· apply lawful_divSubtractShift
|
||||
constructor
|
||||
· assumption
|
||||
· omega
|
||||
· simp only [divSubtractShift, hm]
|
||||
split <;> rfl
|
||||
|
||||
/-- The output of `divRec` has no more bits left to process (i.e., `wn = 0`) -/
|
||||
@[simp]
|
||||
theorem wn_divRec (args : DivModArgs w) (qr : DivModState w) :
|
||||
(divRec qr.wn args qr).wn = 0 := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
assumption
|
||||
case succ wn' ih =>
|
||||
apply ih
|
||||
simp only [divSubtractShift, hm]
|
||||
split <;> rfl
|
||||
|
||||
/-- The result of `udiv` agrees with the result of the division recurrence. -/
|
||||
theorem udiv_eq_divRec (hd : 0#w < d) :
|
||||
let out := divRec w {n, d} (DivModState.init w)
|
||||
n.udiv d = out.q := by
|
||||
have := DivModState.lawful_init {n, d} hd
|
||||
have := lawful_divRec this
|
||||
apply DivModState.udiv_eq_of_lawful this (wn_divRec ..)
|
||||
|
||||
/-- The result of `umod` agrees with the result of the division recurrence. -/
|
||||
theorem umod_eq_divRec (hd : 0#w < d) :
|
||||
let out := divRec w {n, d} (DivModState.init w)
|
||||
n.umod d = out.r := by
|
||||
have := DivModState.lawful_init {n, d} hd
|
||||
have := lawful_divRec this
|
||||
apply DivModState.umod_eq_of_lawful this (wn_divRec ..)
|
||||
|
||||
theorem divRec_succ' (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec (m+1) args qr =
|
||||
let wn := qr.wn - 1
|
||||
let wr := qr.wr + 1
|
||||
let r' := shiftConcat qr.r (args.n.getLsbD wn)
|
||||
let input : DivModState _ :=
|
||||
if r' < args.d then {
|
||||
q := qr.q.shiftConcat false,
|
||||
r := r'
|
||||
wn, wr
|
||||
} else {
|
||||
q := qr.q.shiftConcat true,
|
||||
r := r' - args.d
|
||||
wn, wr
|
||||
}
|
||||
divRec m args input := by
|
||||
simp [divRec_succ, divSubtractShift]
|
||||
|
||||
/- ### Arithmetic shift right (sshiftRight) recurrence -/
|
||||
|
||||
/--
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -368,13 +368,14 @@ theorem and_or_inj_left_iff :
|
||||
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
|
||||
def toNat (b : Bool) : Nat := cond b 1 0
|
||||
|
||||
@[simp] theorem toNat_false : false.toNat = 0 := rfl
|
||||
@[simp, bv_toNat] theorem toNat_false : false.toNat = 0 := rfl
|
||||
|
||||
@[simp] theorem toNat_true : true.toNat = 1 := rfl
|
||||
@[simp, bv_toNat] theorem toNat_true : true.toNat = 1 := rfl
|
||||
|
||||
theorem toNat_le (c : Bool) : c.toNat ≤ 1 := by
|
||||
cases c <;> trivial
|
||||
|
||||
@[bv_toNat]
|
||||
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
Nat.lt_succ_of_le (toNat_le _)
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ On an invalid position, returns `(default : UInt8)`. -/
|
||||
@[inline]
|
||||
def curr : Iterator → UInt8
|
||||
| ⟨arr, i⟩ =>
|
||||
if h:i < arr.size then
|
||||
if h : i < arr.size then
|
||||
arr[i]'h
|
||||
else
|
||||
default
|
||||
|
||||
@@ -14,7 +14,7 @@ instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is simlar to `Empty.elim`.
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
@@ -26,7 +26,7 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogenous iterative operation that applies a
|
||||
`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`.
|
||||
|
||||
@@ -35,7 +35,7 @@ Concretely, `hIterate start stop f init` is equal to
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
```
|
||||
|
||||
Because it is heterogenous and must return a value of type `P stop`,
|
||||
Because it is heterogeneous and must return a value of type `P stop`,
|
||||
`hIterate` requires proof that `start ≤ stop`.
|
||||
|
||||
One can prove properties of `hIterate` using the general theorem
|
||||
@@ -70,7 +70,7 @@ private theorem hIterateFrom_elim {P : Nat → Sort _}(Q : ∀(i : Nat), P i →
|
||||
|
||||
/-
|
||||
`hIterate_elim` provides a mechanism for showing that the result of
|
||||
`hIterate` satisifies a property `Q stop` by showing that the states
|
||||
`hIterate` satisfies a property `Q stop` by showing that the states
|
||||
at the intermediate indices `i : start ≤ i < stop` satisfy `Q i`.
|
||||
-/
|
||||
theorem hIterate_elim {P : Nat → Sort _} (Q : ∀(i : Nat), P i → Prop)
|
||||
|
||||
@@ -582,8 +582,8 @@ theorem rev_succ (k : Fin n) : rev (succ k) = castSucc (rev k) := k.rev_addNat 1
|
||||
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j ≠ 0) : (j.pred h : Nat) = j - 1 := rfl
|
||||
|
||||
@[simp] theorem succ_pred : ∀ (i : Fin (n + 1)) (h : i ≠ 0), (i.pred h).succ = i
|
||||
| ⟨0, h⟩, hi => by simp only [mk_zero, ne_eq, not_true] at hi
|
||||
| ⟨n + 1, h⟩, hi => rfl
|
||||
| ⟨0, _⟩, hi => by simp only [mk_zero, ne_eq, not_true] at hi
|
||||
| ⟨_ + 1, _⟩, _ => rfl
|
||||
|
||||
@[simp]
|
||||
theorem pred_succ (i : Fin n) {h : i.succ ≠ 0} : i.succ.pred h = i := by
|
||||
|
||||
@@ -72,21 +72,35 @@ instance floatDecLt (a b : Float) : Decidable (a < b) := Float.decLt a b
|
||||
instance floatDecLe (a b : Float) : Decidable (a ≤ b) := Float.decLe a b
|
||||
|
||||
@[extern "lean_float_to_string"] opaque Float.toString : Float → String
|
||||
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt8, returns 0. -/
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns `0`.
|
||||
If larger than the maximum value for `UInt8` (including Inf), returns the maximum value of `UInt8`
|
||||
(i.e. `UInt8.size - 1`).
|
||||
-/
|
||||
@[extern "lean_float_to_uint8"] opaque Float.toUInt8 : Float → UInt8
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt16, returns 0. -/
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns `0`.
|
||||
If larger than the maximum value for `UInt16` (including Inf), returns the maximum value of `UInt16`
|
||||
(i.e. `UInt16.size - 1`).
|
||||
-/
|
||||
@[extern "lean_float_to_uint16"] opaque Float.toUInt16 : Float → UInt16
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt32, returns 0. -/
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns `0`.
|
||||
If larger than the maximum value for `UInt32` (including Inf), returns the maximum value of `UInt32`
|
||||
(i.e. `UInt32.size - 1`).
|
||||
-/
|
||||
@[extern "lean_float_to_uint32"] opaque Float.toUInt32 : Float → UInt32
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt64, returns 0. -/
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns `0`.
|
||||
If larger than the maximum value for `UInt64` (including Inf), returns the maximum value of `UInt64`
|
||||
(i.e. `UInt64.size - 1`).
|
||||
-/
|
||||
@[extern "lean_float_to_uint64"] opaque Float.toUInt64 : Float → UInt64
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for USize, returns 0. -/
|
||||
/-- If the given float is non-negative, truncates the value to the nearest non-negative integer.
|
||||
If negative or NaN, returns `0`.
|
||||
If larger than the maximum value for `USize` (including Inf), returns the maximum value of `USize`
|
||||
(i.e. `USize.size - 1`). This value is platform dependent).
|
||||
-/
|
||||
@[extern "lean_float_to_usize"] opaque Float.toUSize : Float → USize
|
||||
|
||||
@[extern "lean_float_isnan"] opaque Float.isNaN : Float → Bool
|
||||
|
||||
@@ -194,7 +194,7 @@ theorem fdiv_eq_tdiv {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = tdiv
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
|
||||
/-! ### mod definitiions -/
|
||||
/-! ### mod definitions -/
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
|
||||
@@ -253,7 +253,7 @@ theorem tmod_def (a b : Int) : tmod a b = a - b * a.tdiv b := by
|
||||
|
||||
theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
|
||||
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
|
||||
| succ m, ofNat n => congrArg ofNat <| Nat.mod_add_div ..
|
||||
| succ _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
|
||||
| succ m, -[n+1] => by
|
||||
show subNatNat (m % succ n) n + (↑(succ n * (m / succ n)) + n + 1) = (m + 1)
|
||||
rw [Int.add_comm _ n, ← Int.add_assoc, ← Int.add_assoc,
|
||||
@@ -289,8 +289,8 @@ theorem fmod_eq_tmod {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fmod a b = tmod
|
||||
|
||||
@[simp] protected 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 m, -[n+1] => (Int.neg_neg _).symm
|
||||
| ofNat m, succ n | -[m+1], 0 | -[m+1], succ n | -[m+1], -[n+1] => rfl
|
||||
| ofNat _, -[_+1] => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], succ _ | -[_+1], -[_+1] => rfl
|
||||
|
||||
theorem ediv_neg' {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
|
||||
match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
|
||||
@@ -339,7 +339,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
|
||||
| _, ⟨k, rfl⟩, -[n+1] => show (a - n.succ * k.succ).ediv k.succ = a.ediv k.succ - n.succ by
|
||||
rw [← Int.add_sub_cancel (ediv ..), ← this, Int.sub_add_cancel]
|
||||
fun {k n} => @fun
|
||||
| ofNat m => congrArg ofNat <| Nat.add_mul_div_right _ _ k.succ_pos
|
||||
| ofNat _ => congrArg ofNat <| Nat.add_mul_div_right _ _ k.succ_pos
|
||||
| -[m+1] => by
|
||||
show ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
|
||||
by_cases h : m < n * k.succ
|
||||
@@ -396,7 +396,7 @@ theorem add_mul_ediv_left (a : Int) {b : Int}
|
||||
rw [Int.mul_neg, Int.ediv_neg, Int.ediv_neg]; apply congrArg Neg.neg; apply this
|
||||
fun m k b =>
|
||||
match b, k with
|
||||
| ofNat n, k => congrArg ofNat (Nat.mul_div_mul_left _ _ m.succ_pos)
|
||||
| ofNat _, _ => congrArg ofNat (Nat.mul_div_mul_left _ _ m.succ_pos)
|
||||
| -[n+1], 0 => by
|
||||
rw [Int.ofNat_zero, Int.mul_zero, Int.ediv_zero, Int.ediv_zero]
|
||||
| -[n+1], succ k => congrArg negSucc <|
|
||||
@@ -822,14 +822,14 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem tdiv_neg : ∀ a b : Int, a.tdiv (-b) = -(a.tdiv b)
|
||||
| ofNat m, 0 => show ofNat (m / 0) = -↑(m / 0) by rw [Nat.div_zero]; rfl
|
||||
| ofNat m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
|
||||
| ofNat m, succ n | -[m+1], 0 | -[m+1], -[n+1] => rfl
|
||||
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
|
||||
unseal Nat.div in
|
||||
@[simp] protected theorem neg_tdiv : ∀ a b : Int, (-a).tdiv b = -(a.tdiv b)
|
||||
| 0, n => by simp [Int.neg_zero]
|
||||
| succ m, (n:Nat) | -[m+1], 0 | -[m+1], -[n+1] => rfl
|
||||
| succ m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
|
||||
| succ _, (n:Nat) | -[_+1], 0 | -[_+1], -[_+1] => rfl
|
||||
| succ _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
|
||||
|
||||
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
|
||||
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
|
||||
|
||||
@@ -181,12 +181,12 @@ theorem subNatNat_add_negSucc (m n k : Nat) :
|
||||
Nat.add_comm]
|
||||
|
||||
protected theorem add_assoc : ∀ a b c : Int, a + b + c = a + (b + c)
|
||||
| (m:Nat), (n:Nat), c => aux1 ..
|
||||
| (m:Nat), (n:Nat), _ => aux1 ..
|
||||
| Nat.cast m, b, Nat.cast k => by
|
||||
rw [Int.add_comm, ← aux1, Int.add_comm k, aux1, Int.add_comm b]
|
||||
| a, (n:Nat), (k:Nat) => by
|
||||
rw [Int.add_comm, Int.add_comm a, ← aux1, Int.add_comm a, Int.add_comm k]
|
||||
| -[m+1], -[n+1], (k:Nat) => aux2 ..
|
||||
| -[_+1], -[_+1], (k:Nat) => aux2 ..
|
||||
| -[m+1], (n:Nat), -[k+1] => by
|
||||
rw [Int.add_comm, ← aux2, Int.add_comm n, ← aux2, Int.add_comm -[m+1]]
|
||||
| (m:Nat), -[n+1], -[k+1] => by
|
||||
|
||||
@@ -512,8 +512,8 @@ theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toN
|
||||
|
||||
@[simp] theorem pred_toNat : ∀ i : Int, (i - 1).toNat = i.toNat - 1
|
||||
| 0 => rfl
|
||||
| (n+1:Nat) => by simp [ofNat_add]
|
||||
| -[n+1] => rfl
|
||||
| (_+1:Nat) => by simp [ofNat_add]
|
||||
| -[_+1] => rfl
|
||||
|
||||
theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
| 0 => rfl
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Jeremy Avigad, Deniz Aydin, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
@@ -35,10 +36,24 @@ theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤
|
||||
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
|
||||
pow_le_pow_of_le_right h (Nat.zero_le _)
|
||||
|
||||
@[norm_cast]
|
||||
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]
|
||||
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :
|
||||
((2 ^ (w - 1) : Nat) - (2 ^ w : Nat) : Int) = - ((2 ^ (w - 1) : Nat) : Int) := by
|
||||
rw [← Nat.two_pow_pred_add_two_pow_pred h]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
protected theorem two_pow_pred_sub_two_pow' {w : Nat} (h : 0 < w) :
|
||||
(2 : Int) ^ (w - 1) - (2 : Int) ^ w = - (2 : Int) ^ (w - 1) := by
|
||||
norm_cast
|
||||
rw [← Nat.two_pow_pred_add_two_pow_pred h]
|
||||
simp [h]
|
||||
|
||||
end Int
|
||||
|
||||
@@ -73,7 +73,7 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (l H)
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H) :
|
||||
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun a h => H _ (mem_map_of_mem _ h) := by
|
||||
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun _ h => H _ (mem_map_of_mem _ h) := by
|
||||
induction l
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
@@ -84,7 +84,7 @@ theorem attach_congr {l₁ l₂ : List α} (h : l₁ = l₂) :
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {l₁ l₂ : List α} (w : l₁ = l₂) {P : α → Prop} {H : ∀ x ∈ l₁, P x} :
|
||||
l₁.attachWith P H = l₂.attachWith P fun x h => H _ (w ▸ h) := by
|
||||
l₁.attachWith P H = l₂.attachWith P fun _ h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
@@ -353,7 +353,7 @@ theorem attach_map {l : List α} (f : α → β) :
|
||||
induction l <;> simp [*]
|
||||
|
||||
theorem attachWith_map {l : List α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ l.map f → P b} :
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun a h => H _ (mem_map_of_mem f h))).map
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem f h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@@ -548,4 +548,131 @@ theorem count_attachWith [DecidableEq α] {p : α → Prop} (l : List α) (H :
|
||||
(l.attachWith p H).count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _ _ _
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`List.unattach` is the (one-sided) inverse of `List.attach`. It is a synonym for `List.map Subtype.val`.
|
||||
|
||||
We use it by providing a simp lemma `l.attach.unattach = l`, and simp lemmas which recognize higher order
|
||||
functions applied to `l : List { x // p x }` which only depend on the value, not the predicate, and rewrite these
|
||||
in terms of a simpler function applied to `l.unattach`.
|
||||
|
||||
Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as an intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) := l.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {α : Type _} {p : α → Prop} : ([] : List { x // p x }).unattach = [] := rfl
|
||||
@[simp] theorem unattach_cons {α : Type _} {p : α → Prop} {a : { x // p x }} {l : List { x // p x }} :
|
||||
(a :: l).unattach = a.val :: l.unattach := rfl
|
||||
|
||||
@[simp] theorem length_unattach {α : Type _} {p : α → Prop} {l : List { x // p x }} :
|
||||
l.unattach.length = l.length := by
|
||||
unfold unattach
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_attach {α : Type _} (l : List α) : l.attach.unattach = l := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, Function.comp_def]
|
||||
|
||||
@[simp] theorem unattach_attachWith {α : Type _} {p : α → Prop} {l : List α}
|
||||
{H : ∀ a ∈ l, p a} :
|
||||
(l.attachWith p H).unattach = l := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, Function.comp_def]
|
||||
|
||||
/-! ### Recognizing higher order functions on subtypes using a function that only depends on the value. -/
|
||||
|
||||
/--
|
||||
This lemma identifies folds over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldl_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} :
|
||||
l.foldl f x = l.unattach.foldl g x := by
|
||||
unfold unattach
|
||||
induction l generalizing x with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf]
|
||||
|
||||
/--
|
||||
This lemma identifies folds over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldr_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} :
|
||||
l.foldr f x = l.unattach.foldr g x := by
|
||||
unfold unattach
|
||||
induction l generalizing x with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf]
|
||||
|
||||
/--
|
||||
This lemma identifies maps over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem map_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → β} {g : α → β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.map f = l.unattach.map g := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf]
|
||||
|
||||
@[simp] theorem filterMap_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → Option β} {g : α → Option β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.filterMap f = l.unattach.filterMap g := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf, filterMap_cons]
|
||||
|
||||
@[simp] theorem bind_subtype {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → List β} {g : α → List β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
(l.bind f) = l.unattach.bind g := by
|
||||
unfold unattach
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih => simp [ih, hf]
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : List { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
(l.filter f).unattach = l.unattach.filter g := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp only [filter_cons, hf, unattach_cons]
|
||||
split <;> simp [ih]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {l : List { x // p x }} :
|
||||
l.reverse.unattach = l.unattach.reverse := by
|
||||
simp [unattach, -map_subtype]
|
||||
|
||||
@[simp] theorem unattach_append {p : α → Prop} {l₁ l₂ : List { x // p x }} :
|
||||
(l₁ ++ l₂).unattach = l₁.unattach ++ l₂.unattach := by
|
||||
simp [unattach, -map_subtype]
|
||||
|
||||
@[simp] theorem unattach_join {p : α → Prop} {l : List (List { x // p x })} :
|
||||
l.join.unattach = (l.map unattach).join := by
|
||||
unfold unattach
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem unattach_replicate {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(List.replicate n x).unattach = List.replicate n x.1 := by
|
||||
simp [unattach, -map_subtype]
|
||||
|
||||
end List
|
||||
|
||||
@@ -43,7 +43,7 @@ The operations are organized as follow:
|
||||
* Logic: `any`, `all`, `or`, and `and`.
|
||||
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
|
||||
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
|
||||
* Minima and maxima: `minimum?` and `maximum?`.
|
||||
* Minima and maxima: `min?` and `max?`.
|
||||
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`,
|
||||
`removeAll`
|
||||
(currently these functions are mostly only used in meta code,
|
||||
@@ -218,8 +218,8 @@ def get? : (as : List α) → (i : Nat) → Option α
|
||||
|
||||
theorem ext_get? : ∀ {l₁ l₂ : List α}, (∀ n, l₁.get? n = l₂.get? n) → l₁ = l₂
|
||||
| [], [], _ => rfl
|
||||
| a :: l₁, [], h => nomatch h 0
|
||||
| [], a' :: l₂, h => nomatch h 0
|
||||
| _ :: _, [], h => nomatch h 0
|
||||
| [], _ :: _, h => nomatch h 0
|
||||
| a :: l₁, a' :: l₂, h => by
|
||||
have h0 : some a = some a' := h 0
|
||||
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
|
||||
@@ -1464,30 +1464,34 @@ def enum : List α → List (Nat × α) := enumFrom 0
|
||||
|
||||
/-! ## Minima and maxima -/
|
||||
|
||||
/-! ### minimum? -/
|
||||
/-! ### min? -/
|
||||
|
||||
/--
|
||||
Returns the smallest element of the list, if it is not empty.
|
||||
* `[].minimum? = none`
|
||||
* `[4].minimum? = some 4`
|
||||
* `[1, 4, 2, 10, 6].minimum? = some 1`
|
||||
* `[].min? = none`
|
||||
* `[4].min? = some 4`
|
||||
* `[1, 4, 2, 10, 6].min? = some 1`
|
||||
-/
|
||||
def minimum? [Min α] : List α → Option α
|
||||
def min? [Min α] : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some <| as.foldl min a
|
||||
|
||||
/-! ### maximum? -/
|
||||
@[inherit_doc min?, deprecated min? (since := "2024-09-29")] abbrev minimum? := @min?
|
||||
|
||||
/-! ### max? -/
|
||||
|
||||
/--
|
||||
Returns the largest element of the list, if it is not empty.
|
||||
* `[].maximum? = none`
|
||||
* `[4].maximum? = some 4`
|
||||
* `[1, 4, 2, 10, 6].maximum? = some 10`
|
||||
* `[].max? = none`
|
||||
* `[4].max? = some 4`
|
||||
* `[1, 4, 2, 10, 6].max? = some 10`
|
||||
-/
|
||||
def maximum? [Max α] : List α → Option α
|
||||
def max? [Max α] : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some <| as.foldl max a
|
||||
|
||||
@[inherit_doc max?, deprecated max? (since := "2024-09-29")] abbrev maximum? := @max?
|
||||
|
||||
/-! ## Other list operations
|
||||
|
||||
The functions are currently mostly used in meta code,
|
||||
|
||||
@@ -235,8 +235,8 @@ theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.g
|
||||
theorem le_antisymm [LT α] [s : Antisymm (¬ · < · : α → α → Prop)] {as bs : List α} (h₁ : as ≤ bs) (h₂ : bs ≤ as) : as = bs :=
|
||||
match as, bs with
|
||||
| [], [] => rfl
|
||||
| [], b::bs => False.elim <| h₂ (List.lt.nil ..)
|
||||
| a::as, [] => False.elim <| h₁ (List.lt.nil ..)
|
||||
| [], _::_ => False.elim <| h₂ (List.lt.nil ..)
|
||||
| _::_, [] => False.elim <| h₁ (List.lt.nil ..)
|
||||
| a::as, b::bs => by
|
||||
by_cases hab : a < b
|
||||
· exact False.elim <| h₂ (List.lt.head _ _ hab)
|
||||
|
||||
@@ -52,9 +52,9 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
|
||||
theorem eraseP_ne_nil {xs : List α} {p : α → Bool} : xs.eraseP p ≠ [] ↔ xs ≠ [] ∧ ∀ x, p x → xs ≠ [x] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_eraseP : ∀ {l : List α} {a} (al : a ∈ l) (pa : p a),
|
||||
theorem exists_of_eraseP : ∀ {l : List α} {a} (_ : a ∈ l) (_ : p a),
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l.eraseP p = l₁ ++ l₂
|
||||
| b :: l, a, al, pa =>
|
||||
| b :: l, _, al, pa =>
|
||||
if pb : p b then
|
||||
⟨b, [], l, forall_mem_nil _, pb, by simp [pb]⟩
|
||||
else
|
||||
@@ -109,6 +109,10 @@ protected theorem Sublist.eraseP : l₁ <+ l₂ → l₁.eraseP p <+ l₂.eraseP
|
||||
theorem length_eraseP_le (l : List α) : (l.eraseP p).length ≤ l.length :=
|
||||
l.eraseP_sublist.length_le
|
||||
|
||||
theorem le_length_eraseP (l : List α) : l.length - 1 ≤ (l.eraseP p).length := by
|
||||
rw [length_eraseP]
|
||||
split <;> simp
|
||||
|
||||
theorem mem_of_mem_eraseP {l : List α} : a ∈ l.eraseP p → a ∈ l := (eraseP_subset _ ·)
|
||||
|
||||
@[simp] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a ∈ l.eraseP p ↔ a ∈ l := by
|
||||
@@ -164,8 +168,8 @@ theorem eraseP_append_left {a : α} (pa : p a) :
|
||||
|
||||
theorem eraseP_append_right :
|
||||
∀ {l₁ : List α} l₂, (∀ b ∈ l₁, ¬p b) → eraseP p (l₁++l₂) = l₁ ++ l₂.eraseP p
|
||||
| [], l₂, _ => rfl
|
||||
| x :: xs, l₂, h => by
|
||||
| [], _, _ => rfl
|
||||
| _ :: _, _, h => by
|
||||
simp [(forall_mem_cons.1 h).1, eraseP_append_right _ (forall_mem_cons.1 h).2]
|
||||
|
||||
theorem eraseP_append (l₁ l₂ : List α) :
|
||||
@@ -332,6 +336,10 @@ theorem IsPrefix.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+: l₂) : l₁
|
||||
theorem length_erase_le (a : α) (l : List α) : (l.erase a).length ≤ l.length :=
|
||||
(erase_sublist a l).length_le
|
||||
|
||||
theorem le_length_erase [LawfulBEq α] (a : α) (l : List α) : l.length - 1 ≤ (l.erase a).length := by
|
||||
rw [length_erase]
|
||||
split <;> simp
|
||||
|
||||
theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈ l := erase_subset _ _ h
|
||||
|
||||
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a ≠ b) :
|
||||
@@ -452,13 +460,22 @@ end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem length_eraseIdx : ∀ {l i}, i < length l → length (@eraseIdx α l i) = length l - 1
|
||||
| [], _, _ => rfl
|
||||
| _::_, 0, _ => by simp [eraseIdx]
|
||||
| x::xs, i+1, h => by
|
||||
have : i < length xs := Nat.lt_of_succ_lt_succ h
|
||||
simp [eraseIdx, ← Nat.add_one]
|
||||
rw [length_eraseIdx this, Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) this)]
|
||||
theorem length_eraseIdx (l : List α) (i : Nat) :
|
||||
(l.eraseIdx i).length = if i < l.length then l.length - 1 else l.length := by
|
||||
induction l generalizing i with
|
||||
| nil => simp
|
||||
| cons x l ih =>
|
||||
cases i with
|
||||
| zero => simp
|
||||
| succ i =>
|
||||
simp only [eraseIdx, length_cons, ih, add_one_lt_add_one_iff, Nat.add_one_sub_one]
|
||||
split
|
||||
· cases l <;> simp_all
|
||||
· rfl
|
||||
|
||||
theorem length_eraseIdx_of_lt {l : List α} {i} (h : i < length l) :
|
||||
(l.eraseIdx i).length = length l - 1 := by
|
||||
simp [length_eraseIdx, h]
|
||||
|
||||
@[simp] theorem eraseIdx_zero (l : List α) : eraseIdx l 0 = tail l := by cases l <;> rfl
|
||||
|
||||
@@ -468,6 +485,8 @@ theorem eraseIdx_eq_take_drop_succ :
|
||||
| a::l, 0 => by simp
|
||||
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
|
||||
|
||||
-- See `Init.Data.List.Nat.Erase` for `getElem?_eraseIdx` and `getElem_eraseIdx`.
|
||||
|
||||
@[simp] theorem eraseIdx_eq_nil {l : List α} {i : Nat} : eraseIdx l i = [] ↔ l = [] ∨ (length l = 1 ∧ i = 0) := by
|
||||
match l, i with
|
||||
| [], _
|
||||
@@ -499,6 +518,13 @@ theorem eraseIdx_eq_self : ∀ {l : List α} {k : Nat}, eraseIdx l k = l ↔ len
|
||||
theorem eraseIdx_of_length_le {l : List α} {k : Nat} (h : length l ≤ k) : eraseIdx l k = l := by
|
||||
rw [eraseIdx_eq_self.2 h]
|
||||
|
||||
theorem length_eraseIdx_le (l : List α) (i : Nat) : length (l.eraseIdx i) ≤ length l :=
|
||||
(eraseIdx_sublist l i).length_le
|
||||
|
||||
theorem le_length_eraseIdx (l : List α) (i : Nat) : length l - 1 ≤ length (l.eraseIdx i) := by
|
||||
rw [length_eraseIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem eraseIdx_append_of_lt_length {l : List α} {k : Nat} (hk : k < length l) (l' : List α) :
|
||||
eraseIdx (l ++ l') k = eraseIdx l k ++ l' := by
|
||||
induction l generalizing k with
|
||||
@@ -520,7 +546,7 @@ theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l ≤
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} :
|
||||
(replicate n a).eraseIdx k = if k < n then replicate (n - 1) a else replicate n a := by
|
||||
split <;> rename_i h
|
||||
· rw [eq_replicate_iff, length_eraseIdx (by simpa using h)]
|
||||
· rw [eq_replicate_iff, length_eraseIdx_of_lt (by simpa using h)]
|
||||
simp only [length_replicate, true_and]
|
||||
intro b m
|
||||
replace m := mem_of_mem_eraseIdx m
|
||||
|
||||
@@ -620,6 +620,18 @@ theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α
|
||||
· rfl
|
||||
· simp_all
|
||||
|
||||
theorem findIdx_le_findIdx {l : List α} {p q : α → Bool} (h : ∀ x ∈ l, p x → q x) : l.findIdx q ≤ l.findIdx p := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findIdx_cons, cond_eq_if]
|
||||
split
|
||||
· simp
|
||||
· split
|
||||
· simp_all
|
||||
· simp only [Nat.add_le_add_iff_right]
|
||||
exact ih fun _ m w => h _ (mem_cons_of_mem x m) w
|
||||
|
||||
/-! ### findIdx? -/
|
||||
|
||||
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p i = none := rfl
|
||||
@@ -803,7 +815,7 @@ theorem findIdx?_join {l : List (List α)} {p : α → Bool} :
|
||||
simp only [replicate, findIdx?_cons, Nat.zero_add, findIdx?_succ, zero_lt_succ, true_and]
|
||||
split <;> simp_all
|
||||
|
||||
theorem findIdx?_eq_enum_findSome? {xs : List α} {p : α → Bool} :
|
||||
theorem findIdx?_eq_findSome?_enum {xs : List α} {p : α → Bool} :
|
||||
xs.findIdx? p = xs.enum.findSome? fun ⟨i, a⟩ => if p a then some i else none := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
@@ -814,6 +826,30 @@ theorem findIdx?_eq_enum_findSome? {xs : List α} {p : α → Bool} :
|
||||
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone, reduceCtorEq]
|
||||
simp [Function.comp_def, ← map_fst_add_enum_eq_enumFrom, findSome?_map]
|
||||
|
||||
theorem findIdx?_eq_fst_find?_enum {xs : List α} {p : α → Bool} :
|
||||
xs.findIdx? p = (xs.enum.find? fun ⟨_, x⟩ => p x).map (·.1) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findIdx?_cons, Nat.zero_add, findIdx?_start_succ, enum_cons]
|
||||
split
|
||||
· simp_all
|
||||
· simp only [Option.map_map, enumFrom_eq_map_enum, Bool.false_eq_true, not_false_eq_true,
|
||||
find?_cons_of_neg, find?_map, *]
|
||||
congr
|
||||
|
||||
-- See also `findIdx_le_findIdx`.
|
||||
theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : List α} {p q : α → Bool} (w : ∀ x ∈ xs, p x → q x) :
|
||||
xs.findIdx? q = none → xs.findIdx? p = none := by
|
||||
simp only [findIdx?_eq_none_iff]
|
||||
intro h x m
|
||||
cases z : p x
|
||||
· rfl
|
||||
· exfalso
|
||||
specialize w x m z
|
||||
specialize h x m
|
||||
simp_all
|
||||
|
||||
theorem Sublist.findIdx?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
|
||||
(l₁.findIdx? p).isSome → (l₂.findIdx? p).isSome := by
|
||||
simp only [List.findIdx?_isSome, any_eq_true]
|
||||
|
||||
@@ -31,7 +31,7 @@ The following operations are still missing `@[csimp]` replacements:
|
||||
The following operations are not recursive to begin with
|
||||
(or are defined in terms of recursive primitives):
|
||||
`isEmpty`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft`, `rotateRight`, `insert`, `zip`, `enum`,
|
||||
`minimum?`, `maximum?`, and `removeAll`.
|
||||
`min?`, `max?`, and `removeAll`.
|
||||
|
||||
The following operations were already given `@[csimp]` replacements in `Init/Data/List/Basic.lean`:
|
||||
`length`, `map`, `filter`, `replicate`, `leftPad`, `unzip`, `range'`, `iota`, `intersperse`.
|
||||
|
||||
@@ -55,7 +55,7 @@ See also
|
||||
* `Init.Data.List.Erase` for lemmas about `List.eraseP` and `List.erase`.
|
||||
* `Init.Data.List.Find` for lemmas about `List.find?`, `List.findSome?`, `List.findIdx`,
|
||||
`List.findIdx?`, and `List.indexOf`
|
||||
* `Init.Data.List.MinMax` for lemmas about `List.minimum?` and `List.maximum?`.
|
||||
* `Init.Data.List.MinMax` for lemmas about `List.min?` and `List.max?`.
|
||||
* `Init.Data.List.Pairwise` for lemmas about `List.Pairwise` and `List.Nodup`.
|
||||
* `Init.Data.List.Sublist` for lemmas about `List.Subset`, `List.Sublist`, `List.IsPrefix`,
|
||||
`List.IsSuffix`, and `List.IsInfix`.
|
||||
@@ -191,7 +191,7 @@ theorem get?_eq_some : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
|
||||
⟨fun e =>
|
||||
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_len_le hn ▸ e
|
||||
⟨this, by rwa [get?_eq_get this, Option.some.injEq] at e⟩,
|
||||
fun ⟨h, e⟩ => e ▸ get?_eq_get _⟩
|
||||
fun ⟨_, e⟩ => e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem get?_eq_none : l.get? n = none ↔ length l ≤ n :=
|
||||
⟨fun e => Nat.ge_of_not_lt (fun h' => by cases e ▸ get?_eq_some.2 ⟨h', rfl⟩), get?_len_le⟩
|
||||
@@ -203,6 +203,9 @@ theorem get?_eq_none : l.get? n = none ↔ length l ≤ n :=
|
||||
|
||||
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
|
||||
|
||||
theorem getElem?_eq_some {l : List α} : l[i]? = some a ↔ ∃ h : i < l.length, l[i]'h = a := by
|
||||
simpa using get?_eq_some
|
||||
|
||||
/--
|
||||
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
|
||||
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
|
||||
@@ -266,9 +269,15 @@ theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l
|
||||
theorem getElem?_eq_some_iff {l : List α} : l[n]? = some a ↔ ∃ h : n < l.length, l[n] = a := by
|
||||
simp only [← get?_eq_getElem?, get?_eq_some, get_eq_getElem]
|
||||
|
||||
theorem some_eq_getElem?_iff {l : List α} : some a = l[n]? ↔ ∃ h : n < l.length, l[n] = a := by
|
||||
rw [eq_comm, getElem?_eq_some_iff]
|
||||
|
||||
@[simp] theorem getElem?_eq_none_iff : l[n]? = none ↔ length l ≤ n := by
|
||||
simp only [← get?_eq_getElem?, get?_eq_none]
|
||||
|
||||
@[simp] theorem none_eq_getElem?_iff {l : List α} {n : Nat} : none = l[n]? ↔ length l ≤ n := by
|
||||
simp [eq_comm (a := none)]
|
||||
|
||||
theorem getElem?_eq_none (h : length l ≤ n) : l[n]? = none := getElem?_eq_none_iff.mpr h
|
||||
|
||||
theorem getElem?_eq (l : List α) (i : Nat) :
|
||||
@@ -483,7 +492,7 @@ theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n : Nat, l[n]? = s
|
||||
theorem get?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, l.get? n = some a :=
|
||||
let ⟨⟨n, _⟩, e⟩ := get_of_mem h; ⟨n, e ▸ get?_eq_get _⟩
|
||||
|
||||
theorem getElem_mem : ∀ {l : List α} {n} (h : n < l.length), l[n]'h ∈ l
|
||||
@[simp] theorem getElem_mem : ∀ {l : List α} {n} (h : n < l.length), l[n]'h ∈ l
|
||||
| _ :: _, 0, _ => .head ..
|
||||
| _ :: l, _+1, _ => .tail _ (getElem_mem (l := l) ..)
|
||||
|
||||
@@ -709,9 +718,9 @@ theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α
|
||||
theorem set_comm (a b : α) : ∀ {n m : Nat} (l : List α), n ≠ m →
|
||||
(l.set n a).set m b = (l.set m b).set n a
|
||||
| _, _, [], _ => by simp
|
||||
| n+1, 0, _ :: _, _ => by simp [set]
|
||||
| 0, m+1, _ :: _, _ => by simp [set]
|
||||
| n+1, m+1, x :: t, h =>
|
||||
| _+1, 0, _ :: _, _ => by simp [set]
|
||||
| 0, _+1, _ :: _, _ => by simp [set]
|
||||
| _+1, _+1, _ :: t, h =>
|
||||
congrArg _ <| set_comm a b t fun h' => h <| Nat.succ_inj'.mpr h'
|
||||
|
||||
@[simp]
|
||||
@@ -872,6 +881,20 @@ theorem foldr_map' {α β : Type u} (g : α → β) (f : α → α → α) (f' :
|
||||
· simp
|
||||
· simp [*, h]
|
||||
|
||||
theorem foldl_assoc {op : α → α → α} [ha : Std.Associative op] :
|
||||
∀ {l : List α} {a₁ a₂}, l.foldl op (op a₁ a₂) = op a₁ (l.foldl op a₂)
|
||||
| [], a₁, a₂ => rfl
|
||||
| a :: l, a₁, a₂ => by
|
||||
simp only [foldl_cons, ha.assoc]
|
||||
rw [foldl_assoc]
|
||||
|
||||
theorem foldr_assoc {op : α → α → α} [ha : Std.Associative op] :
|
||||
∀ {l : List α} {a₁ a₂}, l.foldr op (op a₁ a₂) = op (l.foldr op a₁) a₂
|
||||
| [], a₁, a₂ => rfl
|
||||
| a :: l, a₁, a₂ => by
|
||||
simp only [foldr_cons, ha.assoc]
|
||||
rw [foldr_assoc]
|
||||
|
||||
theorem foldl_hom (f : α₁ → α₂) (g₁ : α₁ → β → α₁) (g₂ : α₂ → β → α₂) (l : List β) (init : α₁)
|
||||
(H : ∀ x y, g₂ (f x) y = f (g₁ x y)) : l.foldl g₂ (f init) = f (l.foldl g₁ init) := by
|
||||
induction l generalizing init <;> simp [*, H]
|
||||
@@ -932,6 +955,38 @@ def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β →
|
||||
x (mem_cons_self x l) :=
|
||||
rfl
|
||||
|
||||
/--
|
||||
We can prove that two folds over the same list are related (by some arbitrary relation)
|
||||
if we know that the initial elements are related and the folding function, for each element of the list,
|
||||
preserves the relation.
|
||||
-/
|
||||
theorem foldl_rel {l : List α} {f g : β → α → β} {a b : β} (r : β → β → Prop)
|
||||
(h : r a b) (h' : ∀ (a : α), a ∈ l → ∀ (c c' : β), r c c' → r (f c a) (g c' a)) :
|
||||
r (l.foldl (fun acc a => f acc a) a) (l.foldl (fun acc a => g acc a) b) := by
|
||||
induction l generalizing a b with
|
||||
| nil => simp_all
|
||||
| cons a l ih =>
|
||||
simp only [foldl_cons]
|
||||
apply ih
|
||||
· simp_all
|
||||
· exact fun a m c c' h => h' _ (by simp_all) _ _ h
|
||||
|
||||
/--
|
||||
We can prove that two folds over the same list are related (by some arbitrary relation)
|
||||
if we know that the initial elements are related and the folding function, for each element of the list,
|
||||
preserves the relation.
|
||||
-/
|
||||
theorem foldr_rel {l : List α} {f g : α → β → β} {a b : β} (r : β → β → Prop)
|
||||
(h : r a b) (h' : ∀ (a : α), a ∈ l → ∀ (c c' : β), r c c' → r (f a c) (g a c')) :
|
||||
r (l.foldr (fun a acc => f a acc) a) (l.foldr (fun a acc => g a acc) b) := by
|
||||
induction l generalizing a b with
|
||||
| nil => simp_all
|
||||
| cons a l ih =>
|
||||
simp only [foldr_cons]
|
||||
apply h'
|
||||
· simp
|
||||
· exact ih h fun a m c c' h => h' _ (by simp_all) _ _ h
|
||||
|
||||
/-! ### getLast -/
|
||||
|
||||
theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
|
||||
@@ -939,8 +994,8 @@ theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
|
||||
match l with
|
||||
| [] => contradiction
|
||||
| a :: l => exact Nat.le_refl _)
|
||||
| [a], h => rfl
|
||||
| a :: b :: l, h => by
|
||||
| [_], _ => rfl
|
||||
| _ :: _ :: _, _ => by
|
||||
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_getElem]
|
||||
|
||||
@[deprecated getLast_eq_getElem (since := "2024-07-15")]
|
||||
@@ -966,14 +1021,14 @@ theorem getLast_eq_getLastD (a l h) : @getLast α (a::l) h = getLastD l a := by
|
||||
theorem getLast!_cons [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
|
||||
simp [getLast!, getLast_eq_getLastD]
|
||||
|
||||
theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||||
@[simp] theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||||
| [], h => absurd rfl h
|
||||
| [_], _ => .head ..
|
||||
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
|
||||
|
||||
theorem getLast_mem_getLast? : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ getLast? l
|
||||
| [], h => by contradiction
|
||||
| a :: l, _ => rfl
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem getLastD_mem_cons : ∀ (l : List α) (a : α), getLastD l a ∈ a::l
|
||||
| [], _ => .head ..
|
||||
@@ -1064,7 +1119,7 @@ theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ↔ ∃ ys
|
||||
@[simp] theorem head?_isSome : l.head?.isSome ↔ l ≠ [] := by
|
||||
cases l <;> simp
|
||||
|
||||
theorem head_mem : ∀ {l : List α} (h : l ≠ []), head l h ∈ l
|
||||
@[simp] theorem head_mem : ∀ {l : List α} (h : l ≠ []), head l h ∈ l
|
||||
| [], h => absurd rfl h
|
||||
| _::_, _ => .head ..
|
||||
|
||||
@@ -1079,7 +1134,7 @@ theorem mem_of_mem_head? : ∀ {l : List α} {a : α}, a ∈ l.head? → a ∈ l
|
||||
|
||||
theorem head_mem_head? : ∀ {l : List α} (h : l ≠ []), head l h ∈ head? l
|
||||
| [], h => by contradiction
|
||||
| a :: l, _ => rfl
|
||||
| _ :: _, _ => rfl
|
||||
|
||||
theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
|
||||
cases l <;> simp
|
||||
@@ -1276,11 +1331,16 @@ theorem map_eq_iff : map f l = l' ↔ ∀ i : Nat, l'[i]? = l[i]?.map f := by
|
||||
theorem map_eq_foldr (f : α → β) (l : List α) : map f l = foldr (fun a bs => f a :: bs) [] l := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[simp] theorem set_map {f : α → β} {l : List α} {n : Nat} {a : α} :
|
||||
(map f l).set n (f a) = map f (l.set n a) := by
|
||||
induction l generalizing n with
|
||||
@[simp] theorem map_set {f : α → β} {l : List α} {i : Nat} {a : α} :
|
||||
(l.set i a).map f = (l.map f).set i (f a) := by
|
||||
induction l generalizing i with
|
||||
| nil => simp
|
||||
| cons b l ih => cases n <;> simp_all
|
||||
| cons b l ih => cases i <;> simp_all
|
||||
|
||||
@[deprecated "Use the reverse direction of `map_set`." (since := "2024-09-20")]
|
||||
theorem set_map {f : α → β} {l : List α} {n : Nat} {a : α} :
|
||||
(map f l).set n (f a) = map f (l.set n a) := by
|
||||
simp
|
||||
|
||||
@[simp] theorem head_map (f : α → β) (l : List α) (w) :
|
||||
head (map f l) w = f (head l (by simpa using w)) := by
|
||||
@@ -1406,7 +1466,7 @@ theorem map_filter_eq_foldr (f : α → β) (p : α → Bool) (as : List α) :
|
||||
|
||||
@[simp] theorem filter_append {p : α → Bool} :
|
||||
∀ (l₁ l₂ : List α), filter p (l₁ ++ l₂) = filter p l₁ ++ filter p l₂
|
||||
| [], l₂ => rfl
|
||||
| [], _ => rfl
|
||||
| a :: l₁, l₂ => by simp [filter]; split <;> simp [filter_append l₁]
|
||||
|
||||
theorem filter_eq_cons_iff {l} {a} {as} :
|
||||
@@ -1611,6 +1671,11 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
@[simp] theorem nil_append_fun : (([] : List α) ++ ·) = id := rfl
|
||||
|
||||
@[simp] theorem cons_append_fun (a : α) (as : List α) :
|
||||
(fun bs => ((a :: as) ++ bs)) = fun bs => a :: (as ++ bs) := rfl
|
||||
|
||||
theorem getElem_append {l₁ l₂ : List α} (n : Nat) (h) :
|
||||
(l₁ ++ l₂)[n] = if h' : n < l₁.length then l₁[n] else l₂[n - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
|
||||
split <;> rename_i h'
|
||||
@@ -1625,7 +1690,7 @@ theorem getElem?_append_left {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.leng
|
||||
|
||||
theorem getElem?_append_right : ∀ {l₁ l₂ : List α} {n : Nat}, l₁.length ≤ n →
|
||||
(l₁ ++ l₂)[n]? = l₂[n - l₁.length]?
|
||||
| [], _, n, _ => rfl
|
||||
| [], _, _, _ => rfl
|
||||
| a :: l, _, n+1, h₁ => by
|
||||
rw [cons_append]
|
||||
simp [Nat.succ_sub_succ_eq_sub, getElem?_append_right (Nat.lt_succ.1 h₁)]
|
||||
@@ -1690,8 +1755,8 @@ theorem append_of_mem {a : α} {l : List α} : a ∈ l → ∃ s t : List α, l
|
||||
|
||||
theorem append_inj :
|
||||
∀ {s₁ s₂ t₁ t₂ : List α}, s₁ ++ t₁ = s₂ ++ t₂ → length s₁ = length s₂ → s₁ = s₂ ∧ t₁ = t₂
|
||||
| [], [], t₁, t₂, h, _ => ⟨rfl, h⟩
|
||||
| a :: s₁, b :: s₂, t₁, t₂, h, hl => by
|
||||
| [], [], _, _, h, _ => ⟨rfl, h⟩
|
||||
| _ :: _, _ :: _, _, _, h, hl => by
|
||||
simp [append_inj (cons.inj h).2 (Nat.succ.inj hl)] at h ⊢; exact h
|
||||
|
||||
theorem append_inj_right (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : t₁ = t₂ :=
|
||||
@@ -2344,11 +2409,21 @@ theorem map_eq_replicate_iff {l : List α} {f : α → β} {b : β} :
|
||||
@[simp] theorem map_const (l : List α) (b : β) : map (Function.const α b) l = replicate l.length b :=
|
||||
map_eq_replicate_iff.mpr fun _ _ => rfl
|
||||
|
||||
@[simp] theorem map_const_fun (x : β) : map (Function.const α x) = (replicate ·.length x) := by
|
||||
funext l
|
||||
simp
|
||||
|
||||
/-- Variant of `map_const` using a lambda rather than `Function.const`. -/
|
||||
-- This can not be a `@[simp]` lemma because it would fire on every `List.map`.
|
||||
theorem map_const' (l : List α) (b : β) : map (fun _ => b) l = replicate l.length b :=
|
||||
map_const l b
|
||||
|
||||
@[simp] theorem set_replicate_self : (replicate n a).set i a = replicate n a := by
|
||||
apply ext_getElem
|
||||
· simp
|
||||
· intro i h₁ h₂
|
||||
simp [getElem_set]
|
||||
|
||||
@[simp] theorem append_replicate_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
|
||||
rw [eq_replicate_iff]
|
||||
constructor
|
||||
@@ -2632,7 +2707,7 @@ theorem bind_reverse {β} (l : List α) (f : α → List β) : (l.reverse.bind f
|
||||
@[simp] theorem reverse_replicate (n) (a : α) : reverse (replicate n a) = replicate n a :=
|
||||
eq_replicate_iff.2
|
||||
⟨by rw [length_reverse, length_replicate],
|
||||
fun b h => eq_of_mem_replicate (mem_reverse.1 h)⟩
|
||||
fun _ h => eq_of_mem_replicate (mem_reverse.1 h)⟩
|
||||
|
||||
/-! #### Further results about `getLast` and `getLast?` -/
|
||||
|
||||
@@ -2837,7 +2912,7 @@ theorem head?_dropLast (xs : List α) : xs.dropLast.head? = if 1 < xs.length the
|
||||
|
||||
theorem getLast_dropLast {xs : List α} (h) :
|
||||
xs.dropLast.getLast h =
|
||||
xs[xs.length - 2]'(match xs, h with | (a :: b :: xs), _ => Nat.lt_trans (Nat.lt_add_one _) (Nat.lt_add_one _)) := by
|
||||
xs[xs.length - 2]'(match xs, h with | (_ :: _ :: _), _ => Nat.lt_trans (Nat.lt_add_one _) (Nat.lt_add_one _)) := by
|
||||
rw [getLast_eq_getElem, getElem_dropLast]
|
||||
congr 1
|
||||
simp; rfl
|
||||
@@ -2861,8 +2936,8 @@ theorem dropLast_cons_of_ne_nil {α : Type u} {x : α}
|
||||
|
||||
theorem dropLast_concat_getLast : ∀ {l : List α} (h : l ≠ []), dropLast l ++ [getLast l h] = l
|
||||
| [], h => absurd rfl h
|
||||
| [a], h => rfl
|
||||
| a :: b :: l, h => by
|
||||
| [_], _ => rfl
|
||||
| _ :: b :: l, _ => by
|
||||
rw [dropLast_cons₂, cons_append, getLast_cons (cons_ne_nil _ _)]
|
||||
congr
|
||||
exact dropLast_concat_getLast (cons_ne_nil b l)
|
||||
|
||||
@@ -7,7 +7,7 @@ prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.minimum?` and `List.maximum?.
|
||||
# Lemmas about `List.min?` and `List.max?.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
@@ -16,24 +16,24 @@ open Nat
|
||||
|
||||
/-! ## Minima and maxima -/
|
||||
|
||||
/-! ### minimum? -/
|
||||
/-! ### min? -/
|
||||
|
||||
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
|
||||
@[simp] theorem min?_nil [Min α] : ([] : List α).min? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `minimum?_cons`,
|
||||
-- We don't put `@[simp]` on `min?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
|
||||
theorem min?_cons [Min α] {xs : List α} : (x :: xs).min? = foldl min x xs := rfl
|
||||
|
||||
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [minimum?]
|
||||
@[simp] theorem min?_eq_none_iff {xs : List α} [Min α] : xs.min? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [min?]
|
||||
|
||||
theorem minimum?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.minimum? = some a → a ∈ xs := by
|
||||
theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.min? = some a → a ∈ xs := by
|
||||
intro xs
|
||||
match xs with
|
||||
| nil => simp
|
||||
| x :: xs =>
|
||||
simp only [minimum?_cons, Option.some.injEq, List.mem_cons]
|
||||
simp only [min?_cons, Option.some.injEq, List.mem_cons]
|
||||
intro eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
@@ -49,12 +49,12 @@ theorem minimum?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem le_minimum?_iff [Min α] [LE α]
|
||||
theorem le_min?_iff [Min α] [LE α]
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) :
|
||||
{xs : List α} → xs.minimum? = some a → ∀ {x}, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
|
||||
{xs : List α} → xs.min? = some a → ∀ {x}, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [minimum?]
|
||||
rw [min?]
|
||||
intro eq y
|
||||
simp only [Option.some.injEq] at eq
|
||||
induction xs generalizing x with
|
||||
@@ -67,46 +67,46 @@ theorem le_minimum?_iff [Min α] [LE α]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
theorem min?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b)
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) {xs : List α} :
|
||||
xs.minimum? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨minimum?_mem min_eq_or h, (le_minimum?_iff le_min_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
((le_minimum?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
|
||||
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))
|
||||
|
||||
theorem minimum?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
|
||||
(replicate n a).minimum? = if n = 0 then none else some a := by
|
||||
theorem min?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
|
||||
(replicate n a).min? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, minimum?_cons]
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons]
|
||||
|
||||
@[simp] theorem minimum?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
|
||||
(replicate n a).minimum? = some a := by
|
||||
simp [minimum?_replicate, Nat.ne_of_gt h, w]
|
||||
@[simp] theorem min?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
|
||||
(replicate n a).min? = some a := by
|
||||
simp [min?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
/-! ### maximum? -/
|
||||
/-! ### max? -/
|
||||
|
||||
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
|
||||
@[simp] theorem max?_nil [Max α] : ([] : List α).max? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `maximum?_cons`,
|
||||
-- We don't put `@[simp]` on `max?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
|
||||
theorem max?_cons [Max α] {xs : List α} : (x :: xs).max? = foldl max x xs := rfl
|
||||
|
||||
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [maximum?]
|
||||
@[simp] theorem max?_eq_none_iff {xs : List α} [Max α] : xs.max? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [max?]
|
||||
|
||||
theorem maximum?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
|
||||
{xs : List α} → xs.maximum? = some a → a ∈ xs
|
||||
theorem max?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
|
||||
{xs : List α} → xs.max? = some a → a ∈ xs
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [maximum?]; rintro ⟨⟩
|
||||
rw [max?]; rintro ⟨⟩
|
||||
induction xs generalizing x with simp at *
|
||||
| cons y xs ih =>
|
||||
rcases ih (max x y) with h | h <;> simp [h]
|
||||
@@ -114,40 +114,57 @@ theorem maximum?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem maximum?_le_iff [Max α] [LE α]
|
||||
theorem max?_le_iff [Max α] [LE α]
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) :
|
||||
{xs : List α} → xs.maximum? = some a → ∀ {x}, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
|
||||
{xs : List α} → xs.max? = some a → ∀ {x}, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [maximum?]; rintro ⟨⟩ y
|
||||
rw [max?]; rintro ⟨⟩ y
|
||||
induction xs generalizing x with
|
||||
| nil => simp
|
||||
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem maximum?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
theorem max?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b)
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) {xs : List α} :
|
||||
xs.maximum? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
|
||||
refine ⟨fun h => ⟨maximum?_mem max_eq_or h, (maximum?_le_iff max_le_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
xs.max? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
|
||||
refine ⟨fun h => ⟨max?_mem max_eq_or h, (max?_le_iff max_le_iff h).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
(h₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
|
||||
((maximum?_le_iff max_le_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (max?_mem max_eq_or (xs := x::xs) rfl))
|
||||
((max?_le_iff max_le_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
|
||||
|
||||
theorem maximum?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
|
||||
(replicate n a).maximum? = if n = 0 then none else some a := by
|
||||
theorem max?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
|
||||
(replicate n a).max? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, maximum?_cons]
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons]
|
||||
|
||||
@[simp] theorem maximum?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
|
||||
(replicate n a).maximum? = some a := by
|
||||
simp [maximum?_replicate, Nat.ne_of_gt h, w]
|
||||
@[simp] theorem max?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
|
||||
(replicate n a).max? = some a := by
|
||||
simp [max?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
@[deprecated min?_nil (since := "2024-09-29")] abbrev minimum?_nil := @min?_nil
|
||||
@[deprecated min?_cons (since := "2024-09-29")] abbrev minimum?_cons := @min?_cons
|
||||
@[deprecated min?_eq_none_iff (since := "2024-09-29")] abbrev mininmum?_eq_none_iff := @min?_eq_none_iff
|
||||
@[deprecated min?_mem (since := "2024-09-29")] abbrev minimum?_mem := @min?_mem
|
||||
@[deprecated le_min?_iff (since := "2024-09-29")] abbrev le_minimum?_iff := @le_min?_iff
|
||||
@[deprecated min?_eq_some_iff (since := "2024-09-29")] abbrev minimum?_eq_some_iff := @min?_eq_some_iff
|
||||
@[deprecated min?_replicate (since := "2024-09-29")] abbrev minimum?_replicate := @min?_replicate
|
||||
@[deprecated min?_replicate_of_pos (since := "2024-09-29")] abbrev minimum?_replicate_of_pos := @min?_replicate_of_pos
|
||||
@[deprecated max?_nil (since := "2024-09-29")] abbrev maximum?_nil := @max?_nil
|
||||
@[deprecated max?_cons (since := "2024-09-29")] abbrev maximum?_cons := @max?_cons
|
||||
@[deprecated max?_eq_none_iff (since := "2024-09-29")] abbrev maximum?_eq_none_iff := @max?_eq_none_iff
|
||||
@[deprecated max?_mem (since := "2024-09-29")] abbrev maximum?_mem := @max?_mem
|
||||
@[deprecated max?_le_iff (since := "2024-09-29")] abbrev maximum?_le_iff := @max?_le_iff
|
||||
@[deprecated max?_eq_some_iff (since := "2024-09-29")] abbrev maximum?_eq_some_iff := @max?_eq_some_iff
|
||||
@[deprecated max?_replicate (since := "2024-09-29")] abbrev maximum?_replicate := @max?_replicate
|
||||
@[deprecated max?_replicate_of_pos (since := "2024-09-29")] abbrev maximum?_replicate_of_pos := @max?_replicate_of_pos
|
||||
|
||||
end List
|
||||
|
||||
@@ -51,6 +51,27 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
|
||||
@[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 [*]
|
||||
|
||||
/-- Auxiliary lemma for `mapM_eq_reverse_foldlM_cons`. -/
|
||||
theorem foldlM_cons_eq_append [Monad m] [LawfulMonad m] (f : α → m β) (as : List α) (b : β) (bs : List β) :
|
||||
(as.foldlM (init := b :: bs) fun acc a => return ((← f a) :: acc)) =
|
||||
(· ++ b :: bs) <$> as.foldlM (init := []) fun acc a => return ((← f a) :: acc) := by
|
||||
induction as generalizing b bs with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [bind_pure_comp] at ih
|
||||
simp [ih, _root_.map_bind, Functor.map_map, Function.comp_def]
|
||||
|
||||
theorem mapM_eq_reverse_foldlM_cons [Monad m] [LawfulMonad m] (f : α → m β) (l : List α) :
|
||||
mapM f l = reverse <$> (l.foldlM (fun acc a => return ((← f a) :: acc)) []) := by
|
||||
rw [← mapM'_eq_mapM]
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [mapM'_cons, ih, bind_map_left, foldlM_cons, LawfulMonad.bind_assoc, pure_bind,
|
||||
foldlM_cons_eq_append, _root_.map_bind, Functor.map_map, Function.comp_def, reverse_append,
|
||||
reverse_cons, reverse_nil, nil_append, singleton_append]
|
||||
simp [bind_pure_comp]
|
||||
|
||||
/-! ### forM -/
|
||||
|
||||
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
|
||||
@@ -66,4 +87,16 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
|
||||
(l₁ ++ l₂).forM f = (do l₁.forM f; l₂.forM f) := by
|
||||
induction l₁ <;> simp [*]
|
||||
|
||||
/-! ### allM -/
|
||||
|
||||
theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as : List α) :
|
||||
allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by
|
||||
induction as with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp only [allM, anyM, bind_map_left, _root_.map_bind]
|
||||
congr
|
||||
funext b
|
||||
split <;> simp_all
|
||||
|
||||
end List
|
||||
|
||||
@@ -10,3 +10,5 @@ import Init.Data.List.Nat.Range
|
||||
import Init.Data.List.Nat.Sublist
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Nat.Count
|
||||
import Init.Data.List.Nat.Erase
|
||||
import Init.Data.List.Nat.Find
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Count
|
||||
import Init.Data.List.Find
|
||||
import Init.Data.List.MinMax
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
@@ -85,26 +86,26 @@ theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x ∈ eraseIdx l k ↔ ∃
|
||||
obtain ⟨h', -⟩ := getElem?_eq_some_iff.1 h
|
||||
exact ⟨h', h⟩
|
||||
|
||||
/-! ### minimum? -/
|
||||
/-! ### min? -/
|
||||
|
||||
-- A specialization of `minimum?_eq_some_iff` to Nat.
|
||||
theorem minimum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.minimum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, a ≤ b) :=
|
||||
minimum?_eq_some_iff
|
||||
-- A specialization of `min?_eq_some_iff` to Nat.
|
||||
theorem min?_eq_some_iff' {xs : List Nat} :
|
||||
xs.min? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, a ≤ b) :=
|
||||
min?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(min_eq_or := fun _ _ => by omega)
|
||||
(le_min_iff := fun _ _ _ => by omega)
|
||||
(min_eq_or := fun _ _ => Nat.min_def .. ▸ by split <;> simp)
|
||||
(le_min_iff := fun _ _ _ => Nat.le_min)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem minimum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).minimum? = some (match l.minimum? with
|
||||
theorem min?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).min? = some (match l.min? with
|
||||
| none => a
|
||||
| some m => min a m) := by
|
||||
rw [minimum?_eq_some_iff']
|
||||
rw [min?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [minimum?_eq_some_iff'] at m
|
||||
· rw [min?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.min_def]
|
||||
constructor
|
||||
@@ -118,26 +119,73 @@ theorem minimum?_cons' {a : Nat} {l : List Nat} :
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
|
||||
/-! ### maximum? -/
|
||||
theorem foldl_min
|
||||
{α : Type _} [Min α] [Std.IdempotentOp (min : α → α → α)] [Std.Associative (min : α → α → α)]
|
||||
{l : List α} {a : α} :
|
||||
l.foldl (init := a) min = min a (l.min?.getD a) := by
|
||||
cases l with
|
||||
| nil => simp [Std.IdempotentOp.idempotent]
|
||||
| cons b l =>
|
||||
simp only [min?]
|
||||
induction l generalizing a b with
|
||||
| nil => simp
|
||||
| cons c l ih => simp [ih, Std.Associative.assoc]
|
||||
|
||||
-- A specialization of `maximum?_eq_some_iff` to Nat.
|
||||
theorem maximum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.maximum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, b ≤ a) :=
|
||||
maximum?_eq_some_iff
|
||||
theorem foldl_min_right {α β : Type _}
|
||||
[Min β] [Std.IdempotentOp (min : β → β → β)] [Std.Associative (min : β → β → β)]
|
||||
{l : List α} {b : β} {f : α → β} :
|
||||
(l.foldl (init := b) fun acc a => min acc (f a)) = min b ((l.map f).min?.getD b) := by
|
||||
rw [← foldl_map, foldl_min]
|
||||
|
||||
theorem foldl_min_le {l : List Nat} {a : Nat} : l.foldl (init := a) min ≤ a := by
|
||||
induction l generalizing a with
|
||||
| nil => simp
|
||||
| cons c l ih =>
|
||||
simp only [foldl_cons]
|
||||
exact Nat.le_trans ih (Nat.min_le_left _ _)
|
||||
|
||||
theorem foldl_min_min_of_le {l : List Nat} {a b : Nat} (h : a ≤ b) :
|
||||
l.foldl (init := a) min ≤ b :=
|
||||
Nat.le_trans (foldl_min_le) h
|
||||
|
||||
theorem min?_getD_le_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) :
|
||||
l.min?.getD k ≤ a := by
|
||||
cases l with
|
||||
| nil => simp at h
|
||||
| cons b l =>
|
||||
simp [min?_cons]
|
||||
simp at h
|
||||
rcases h with (rfl | h)
|
||||
· exact foldl_min_le
|
||||
· induction l generalizing b with
|
||||
| nil => simp_all
|
||||
| cons c l ih =>
|
||||
simp only [foldl_cons]
|
||||
simp at h
|
||||
rcases h with (rfl | h)
|
||||
· exact foldl_min_min_of_le (Nat.min_le_right _ _)
|
||||
· exact ih _ h
|
||||
|
||||
/-! ### max? -/
|
||||
|
||||
-- A specialization of `max?_eq_some_iff` to Nat.
|
||||
theorem max?_eq_some_iff' {xs : List Nat} :
|
||||
xs.max? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, b ≤ a) :=
|
||||
max?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(max_eq_or := fun _ _ => by omega)
|
||||
(max_le_iff := fun _ _ _ => by omega)
|
||||
(max_eq_or := fun _ _ => Nat.max_def .. ▸ by split <;> simp)
|
||||
(max_le_iff := fun _ _ _ => Nat.max_le)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem maximum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).maximum? = some (match l.maximum? with
|
||||
theorem max?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).max? = some (match l.max? with
|
||||
| none => a
|
||||
| some m => max a m) := by
|
||||
rw [maximum?_eq_some_iff']
|
||||
rw [max?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [maximum?_eq_some_iff'] at m
|
||||
· rw [max?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.max_def]
|
||||
constructor
|
||||
@@ -151,4 +199,58 @@ theorem maximum?_cons' {a : Nat} {l : List Nat} :
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
|
||||
theorem foldl_max
|
||||
{α : Type _} [Max α] [Std.IdempotentOp (max : α → α → α)] [Std.Associative (max : α → α → α)]
|
||||
{l : List α} {a : α} :
|
||||
l.foldl (init := a) max = max a (l.max?.getD a) := by
|
||||
cases l with
|
||||
| nil => simp [Std.IdempotentOp.idempotent]
|
||||
| cons b l =>
|
||||
simp only [max?]
|
||||
induction l generalizing a b with
|
||||
| nil => simp
|
||||
| cons c l ih => simp [ih, Std.Associative.assoc]
|
||||
|
||||
theorem foldl_max_right {α β : Type _}
|
||||
[Max β] [Std.IdempotentOp (max : β → β → β)] [Std.Associative (max : β → β → β)]
|
||||
{l : List α} {b : β} {f : α → β} :
|
||||
(l.foldl (init := b) fun acc a => max acc (f a)) = max b ((l.map f).max?.getD b) := by
|
||||
rw [← foldl_map, foldl_max]
|
||||
|
||||
theorem le_foldl_max {l : List Nat} {a : Nat} : a ≤ l.foldl (init := a) max := by
|
||||
induction l generalizing a with
|
||||
| nil => simp
|
||||
| cons c l ih =>
|
||||
simp only [foldl_cons]
|
||||
exact Nat.le_trans (Nat.le_max_left _ _) ih
|
||||
|
||||
theorem le_foldl_max_of_le {l : List Nat} {a b : Nat} (h : a ≤ b) :
|
||||
a ≤ l.foldl (init := b) max :=
|
||||
Nat.le_trans h (le_foldl_max)
|
||||
|
||||
theorem le_max?_getD_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) :
|
||||
a ≤ l.max?.getD k := by
|
||||
cases l with
|
||||
| nil => simp at h
|
||||
| cons b l =>
|
||||
simp [max?_cons]
|
||||
simp at h
|
||||
rcases h with (rfl | h)
|
||||
· exact le_foldl_max
|
||||
· induction l generalizing b with
|
||||
| nil => simp_all
|
||||
| cons c l ih =>
|
||||
simp only [foldl_cons]
|
||||
simp at h
|
||||
rcases h with (rfl | h)
|
||||
· exact le_foldl_max_of_le (Nat.le_max_right b a)
|
||||
· exact ih _ h
|
||||
|
||||
@[deprecated min?_eq_some_iff' (since := "2024-09-29")] abbrev minimum?_eq_some_iff' := @min?_eq_some_iff'
|
||||
@[deprecated min?_cons' (since := "2024-09-29")] abbrev minimum?_cons' := @min?_cons'
|
||||
@[deprecated min?_getD_le_of_mem (since := "2024-09-29")] abbrev minimum?_getD_le_of_mem := @min?_getD_le_of_mem
|
||||
@[deprecated max?_eq_some_iff' (since := "2024-09-29")] abbrev maximum?_eq_some_iff' := @max?_eq_some_iff'
|
||||
@[deprecated max?_cons' (since := "2024-09-29")] abbrev maximum?_cons' := @max?_cons'
|
||||
@[deprecated le_max?_getD_of_mem (since := "2024-09-29")] abbrev le_maximum?_getD_of_mem := @le_max?_getD_of_mem
|
||||
|
||||
end List
|
||||
|
||||
66
src/Init/Data/List/Nat/Erase.lean
Normal file
66
src/Init/Data/List/Nat/Erase.lean
Normal file
@@ -0,0 +1,66 @@
|
||||
/-
|
||||
Copyright (c) 2024 Kim Morrison. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Erase
|
||||
|
||||
namespace List
|
||||
|
||||
theorem getElem?_eraseIdx (l : List α) (i : Nat) (j : Nat) :
|
||||
(l.eraseIdx i)[j]? = if j < i then l[j]? else l[j + 1]? := by
|
||||
rw [eraseIdx_eq_take_drop_succ, getElem?_append]
|
||||
split <;> rename_i h
|
||||
· rw [getElem?_take]
|
||||
split
|
||||
· rfl
|
||||
· simp_all
|
||||
omega
|
||||
· rw [getElem?_drop]
|
||||
split <;> rename_i h'
|
||||
· simp only [length_take, Nat.min_def, Nat.not_lt] at h
|
||||
split at h
|
||||
· omega
|
||||
· simp_all [getElem?_eq_none]
|
||||
omega
|
||||
· simp only [length_take]
|
||||
simp only [length_take, Nat.min_def, Nat.not_lt] at h
|
||||
split at h
|
||||
· congr 1
|
||||
omega
|
||||
· rw [getElem?_eq_none, getElem?_eq_none] <;> omega
|
||||
|
||||
theorem getElem?_eraseIdx_of_lt (l : List α) (i : Nat) (j : Nat) (h : j < i) :
|
||||
(l.eraseIdx i)[j]? = l[j]? := by
|
||||
rw [getElem?_eraseIdx]
|
||||
simp [h]
|
||||
|
||||
theorem getElem?_eraseIdx_of_ge (l : List α) (i : Nat) (j : Nat) (h : i ≤ j) :
|
||||
(l.eraseIdx i)[j]? = l[j + 1]? := by
|
||||
rw [getElem?_eraseIdx]
|
||||
simp only [dite_eq_ite, ite_eq_right_iff]
|
||||
intro h'
|
||||
omega
|
||||
|
||||
theorem getElem_eraseIdx (l : List α) (i : Nat) (j : Nat) (h : j < (l.eraseIdx i).length) :
|
||||
(l.eraseIdx i)[j] = if h' : j < i then
|
||||
l[j]'(by have := length_eraseIdx_le l i; omega)
|
||||
else
|
||||
l[j + 1]'(by rw [length_eraseIdx] at h; split at h <;> omega) := by
|
||||
apply Option.some.inj
|
||||
rw [← getElem?_eq_getElem, getElem?_eraseIdx]
|
||||
split <;> simp
|
||||
|
||||
theorem getElem_eraseIdx_of_lt (l : List α) (i : Nat) (j : Nat) (h : j < (l.eraseIdx i).length) (h' : j < i) :
|
||||
(l.eraseIdx i)[j] = l[j]'(by have := length_eraseIdx_le l i; omega) := by
|
||||
rw [getElem_eraseIdx]
|
||||
simp only [dite_eq_left_iff, Nat.not_lt]
|
||||
intro h'
|
||||
omega
|
||||
|
||||
theorem getElem_eraseIdx_of_ge (l : List α) (i : Nat) (j : Nat) (h : j < (l.eraseIdx i).length) (h' : i ≤ j) :
|
||||
(l.eraseIdx i)[j] = l[j + 1]'(by rw [length_eraseIdx] at h; split at h <;> omega) := by
|
||||
rw [getElem_eraseIdx, dif_neg]
|
||||
omega
|
||||
32
src/Init/Data/List/Nat/Find.lean
Normal file
32
src/Init/Data/List/Nat/Find.lean
Normal file
@@ -0,0 +1,32 @@
|
||||
/-
|
||||
Copyright (c) 2024 Kim Morrison. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.Range
|
||||
import Init.Data.List.Find
|
||||
|
||||
namespace List
|
||||
|
||||
theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : List α} {p q : α → Bool} (w : ∀ x ∈ xs, p x → q x) {i : Nat}
|
||||
(h : xs.findIdx? p = some i) : ∃ j, j ≤ i ∧ xs.findIdx? q = some j := by
|
||||
simp only [findIdx?_eq_findSome?_enum] at h
|
||||
rw [findSome?_eq_some_iff] at h
|
||||
simp only [Option.ite_none_right_eq_some, Option.some.injEq, ite_eq_right_iff, reduceCtorEq,
|
||||
imp_false, Bool.not_eq_true, Prod.forall, exists_and_right, Prod.exists] at h
|
||||
obtain ⟨h, h₁, b, ⟨es, h₂⟩, ⟨hb, rfl⟩, h₃⟩ := h
|
||||
rw [enum_eq_enumFrom, enumFrom_eq_append_iff] at h₂
|
||||
obtain ⟨l₁', l₂', rfl, rfl, h₂⟩ := h₂
|
||||
rw [eq_comm, enumFrom_eq_cons_iff] at h₂
|
||||
obtain ⟨a, as, rfl, h₂, rfl⟩ := h₂
|
||||
simp only [Nat.zero_add, Prod.mk.injEq] at h₂
|
||||
obtain ⟨rfl, rfl⟩ := h₂
|
||||
simp only [findIdx?_append]
|
||||
match h : findIdx? q l₁' with
|
||||
| some j =>
|
||||
refine ⟨j, ?_, by simp⟩
|
||||
rw [findIdx?_eq_some_iff_findIdx_eq] at h
|
||||
omega
|
||||
| none =>
|
||||
refine ⟨l₁'.length, by simp, by simp_all⟩
|
||||
@@ -154,7 +154,7 @@ theorem erase_range' :
|
||||
/-! ### range -/
|
||||
|
||||
theorem reverse_range' : ∀ s n : Nat, reverse (range' s n) = map (s + n - 1 - ·) (range n)
|
||||
| s, 0 => rfl
|
||||
| _, 0 => rfl
|
||||
| s, n + 1 => by
|
||||
rw [range'_1_concat, reverse_append, range_succ_eq_map,
|
||||
show s + (n + 1) - 1 = s + n from rfl, map, map_map]
|
||||
@@ -358,17 +358,6 @@ theorem map_enumFrom (f : α → β) (n : Nat) (l : List α) :
|
||||
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
|
||||
induction l generalizing n <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_fst (n) :
|
||||
∀ (l : List α), map Prod.fst (enumFrom n l) = range' n l.length
|
||||
| [] => rfl
|
||||
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) = l
|
||||
| _, [] => rfl
|
||||
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
|
||||
|
||||
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) : x.2 ∈ l :=
|
||||
enumFrom_map_snd n l ▸ mem_map_of_mem _ h
|
||||
|
||||
@@ -391,10 +380,6 @@ theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) ∈ xs.enum
|
||||
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
|
||||
⟨le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h⟩
|
||||
|
||||
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
|
||||
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
|
||||
rw [enumFrom_cons, Nat.add_comm, ← map_fst_add_enumFrom_eq_enumFrom]
|
||||
|
||||
theorem enumFrom_map (n : Nat) (l : List α) (f : α → β) :
|
||||
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
|
||||
induction l with
|
||||
@@ -411,22 +396,39 @@ theorem enumFrom_append (xs ys : List α) (n : Nat) :
|
||||
rw [cons_append, enumFrom_cons, IH, ← cons_append, ← enumFrom_cons, length, Nat.add_right_comm,
|
||||
Nat.add_assoc]
|
||||
|
||||
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
|
||||
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
|
||||
theorem enumFrom_eq_cons_iff {l : List α} {n : Nat} :
|
||||
l.enumFrom n = x :: l' ↔ ∃ a as, l = a :: as ∧ x = (n, a) ∧ l' = enumFrom (n + 1) as := by
|
||||
rw [enumFrom_eq_zip_range', zip_eq_cons_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, h, rfl, rfl⟩
|
||||
rw [range'_eq_cons_iff] at h
|
||||
obtain ⟨rfl, -, rfl⟩ := h
|
||||
exact ⟨x.2, l₂, by simp [enumFrom_eq_zip_range']⟩
|
||||
· rintro ⟨a, as, rfl, rfl, rfl⟩
|
||||
refine ⟨range' (n+1) as.length, as, ?_⟩
|
||||
simp [enumFrom_eq_zip_range', range'_succ]
|
||||
|
||||
@[simp]
|
||||
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
|
||||
(l.enumFrom n).unzip = (range' n l.length, l) := by
|
||||
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
|
||||
theorem enumFrom_eq_append_iff {l : List α} {n : Nat} :
|
||||
l.enumFrom n = l₁ ++ l₂ ↔
|
||||
∃ l₁' l₂', l = l₁' ++ l₂' ∧ l₁ = l₁'.enumFrom n ∧ l₂ = l₂'.enumFrom (n + l₁'.length) := by
|
||||
rw [enumFrom_eq_zip_range', zip_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨w, x, y, z, h, h', rfl, rfl, rfl⟩
|
||||
rw [range'_eq_append_iff] at h'
|
||||
obtain ⟨k, -, rfl, rfl⟩ := h'
|
||||
simp only [length_range'] at h
|
||||
obtain rfl := h
|
||||
refine ⟨y, z, rfl, ?_⟩
|
||||
simp only [enumFrom_eq_zip_range', length_append, true_and]
|
||||
congr
|
||||
omega
|
||||
· rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
|
||||
simp only [enumFrom_eq_zip_range']
|
||||
refine ⟨range' n l₁'.length, range' (n + l₁'.length) l₂'.length, l₁', l₂', ?_⟩
|
||||
simp [Nat.add_comm]
|
||||
|
||||
/-! ### enum -/
|
||||
|
||||
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
|
||||
|
||||
theorem enum_cons' (x : α) (xs : List α) :
|
||||
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
|
||||
enumFrom_cons' _ _ _
|
||||
|
||||
@[simp]
|
||||
theorem enum_eq_nil {l : List α} : List.enum l = [] ↔ l = [] := enumFrom_eq_nil
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ theorem getElem_take' (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j)
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem getElem_take (L : List α) {j i : Nat} {h : i < (L.take j).length} :
|
||||
@[simp] theorem getElem_take (L : List α) {j i : Nat} {h : i < (L.take j).length} :
|
||||
(L.take j)[i] =
|
||||
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
|
||||
rw [length_take, Nat.lt_min] at h; rw [getElem_take' L _ h.1]
|
||||
@@ -52,7 +52,7 @@ length `> i`. Version designed to rewrite from the big list to the small list. -
|
||||
@[deprecated getElem_take' (since := "2024-06-12")]
|
||||
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
get L ⟨i, hi⟩ = get (L.take j) ⟨i, length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩⟩ := by
|
||||
simp [getElem_take' _ hi hj]
|
||||
simp
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
|
||||
@@ -98,8 +98,8 @@ theorem Perm.append_cons (a : α) {h₁ h₂ t₁ t₂ : List α} (p₁ : h₁ ~
|
||||
perm_middle.trans <| by rw [append_nil]
|
||||
|
||||
theorem perm_append_comm : ∀ {l₁ l₂ : List α}, l₁ ++ l₂ ~ l₂ ++ l₁
|
||||
| [], l₂ => by simp
|
||||
| a :: t, l₂ => (perm_append_comm.cons _).trans perm_middle.symm
|
||||
| [], _ => by simp
|
||||
| _ :: _, _ => (perm_append_comm.cons _).trans perm_middle.symm
|
||||
|
||||
theorem perm_append_comm_assoc (l₁ l₂ l₃ : List α) :
|
||||
Perm (l₁ ++ (l₂ ++ l₃)) (l₂ ++ (l₁ ++ l₃)) := by
|
||||
@@ -248,6 +248,10 @@ theorem countP_eq_countP_filter_add (l : List α) (p q : α → Bool) :
|
||||
theorem Perm.count_eq [DecidableEq α] {l₁ l₂ : List α} (p : l₁ ~ l₂) (a) :
|
||||
count a l₁ = count a l₂ := p.countP_eq _
|
||||
|
||||
/-
|
||||
This theorem is a variant of `Perm.foldl_eq` defined in Mathlib which uses typeclasses rather
|
||||
than the explicit `comm` argument.
|
||||
-/
|
||||
theorem Perm.foldl_eq' {f : β → α → β} {l₁ l₂ : List α} (p : l₁ ~ l₂)
|
||||
(comm : ∀ x ∈ l₁, ∀ y ∈ l₁, ∀ (z), f (f z x) y = f (f z y) x)
|
||||
(init) : foldl f init l₁ = foldl f init l₂ := by
|
||||
@@ -264,6 +268,28 @@ theorem Perm.foldl_eq' {f : β → α → β} {l₁ l₂ : List α} (p : l₁ ~
|
||||
refine (IH₁ comm init).trans (IH₂ ?_ _)
|
||||
intros; apply comm <;> apply p₁.symm.subset <;> assumption
|
||||
|
||||
/-
|
||||
This theorem is a variant of `Perm.foldr_eq` defined in Mathlib which uses typeclasses rather
|
||||
than the explicit `comm` argument.
|
||||
-/
|
||||
theorem Perm.foldr_eq' {f : α → β → β} {l₁ l₂ : List α} (p : l₁ ~ l₂)
|
||||
(comm : ∀ x ∈ l₁, ∀ y ∈ l₁, ∀ (z), f y (f x z) = f x (f y z))
|
||||
(init) : foldr f init l₁ = foldr f init l₂ := by
|
||||
induction p using recOnSwap' generalizing init with
|
||||
| nil => simp
|
||||
| cons x _p IH =>
|
||||
simp only [foldr]
|
||||
congr 1
|
||||
apply IH; intros; apply comm <;> exact .tail _ ‹_›
|
||||
| swap' x y _p IH =>
|
||||
simp only [foldr]
|
||||
rw [comm x (.tail _ <| .head _) y (.head _)]
|
||||
congr 2
|
||||
apply IH; intros; apply comm <;> exact .tail _ (.tail _ ‹_›)
|
||||
| trans p₁ _p₂ IH₁ IH₂ =>
|
||||
refine (IH₁ comm init).trans (IH₂ ?_ _)
|
||||
intros; apply comm <;> apply p₁.symm.subset <;> assumption
|
||||
|
||||
theorem Perm.rec_heq {β : List α → Sort _} {f : ∀ a l, β l → β (a :: l)} {b : β []} {l l' : List α}
|
||||
(hl : l ~ l') (f_congr : ∀ {a l l' b b'}, l ~ l' → HEq b b' → HEq (f a l b) (f a l' b'))
|
||||
(f_swap : ∀ {a a' l b}, HEq (f a (a' :: l) (f a' l b)) (f a' (a :: l) (f a l b))) :
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Zip
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.range` and `List.enum`
|
||||
@@ -91,7 +92,7 @@ theorem map_add_range' (a) : ∀ s n step, map (a + ·) (range' s n step) = rang
|
||||
|
||||
theorem range'_append : ∀ s m n step : Nat,
|
||||
range' s m step ++ range' (s + step * m) n step = range' s (n + m) step
|
||||
| s, 0, n, step => rfl
|
||||
| _, 0, _, _ => rfl
|
||||
| s, m + 1, n, step => by
|
||||
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
|
||||
using range'_append (s + step) m n step
|
||||
@@ -130,7 +131,7 @@ theorem range'_eq_cons_iff : range' s n = a :: xs ↔ s = a ∧ 0 < n ∧ xs = r
|
||||
/-! ### range -/
|
||||
|
||||
theorem range_loop_range' : ∀ s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
|
||||
| 0, n => rfl
|
||||
| 0, _ => rfl
|
||||
| s + 1, n => by rw [← Nat.add_assoc, Nat.add_right_comm n s 1]; exact range_loop_range' s (n + 1)
|
||||
|
||||
theorem range_eq_range' (n : Nat) : range n = range' 0 n :=
|
||||
@@ -213,9 +214,9 @@ theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] ↔ l =
|
||||
@[simp]
|
||||
theorem getElem?_enumFrom :
|
||||
∀ n (l : List α) m, (enumFrom n l)[m]? = l[m]?.map fun a => (n + m, a)
|
||||
| n, [], m => rfl
|
||||
| n, a :: l, 0 => by simp
|
||||
| n, a :: l, m + 1 => by
|
||||
| _, [], _ => rfl
|
||||
| _, _ :: _, 0 => by simp
|
||||
| n, _ :: l, m + 1 => by
|
||||
simp only [enumFrom_cons, getElem?_cons_succ]
|
||||
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
|
||||
|
||||
@@ -241,4 +242,47 @@ theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
|
||||
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
|
||||
map_fst_add_enumFrom_eq_enumFrom l _ _
|
||||
|
||||
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
|
||||
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
|
||||
rw [enumFrom_cons, Nat.add_comm, ← map_fst_add_enumFrom_eq_enumFrom]
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_fst (n) :
|
||||
∀ (l : List α), map Prod.fst (enumFrom n l) = range' n l.length
|
||||
| [] => rfl
|
||||
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) = l
|
||||
| _, [] => rfl
|
||||
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
|
||||
|
||||
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
|
||||
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
|
||||
|
||||
@[simp]
|
||||
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
|
||||
(l.enumFrom n).unzip = (range' n l.length, l) := by
|
||||
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
|
||||
|
||||
/-! ### enum -/
|
||||
|
||||
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
|
||||
|
||||
theorem enum_cons' (x : α) (xs : List α) :
|
||||
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
|
||||
enumFrom_cons' _ _ _
|
||||
|
||||
theorem enum_eq_enumFrom {l : List α} : l.enum = l.enumFrom 0 := rfl
|
||||
|
||||
theorem enumFrom_eq_map_enum (l : List α) (n : Nat) :
|
||||
enumFrom n l = (enum l).map (Prod.map (· + n) id) := by
|
||||
induction l generalizing n with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [enumFrom_cons, ih, enum_cons, map_cons, Prod.map_apply, Nat.zero_add, id_eq, map_map,
|
||||
cons.injEq, map_inj_left, Function.comp_apply, Prod.forall, Prod.mk.injEq, and_true, true_and]
|
||||
intro a b _
|
||||
exact (succ_add a n).symm
|
||||
|
||||
end List
|
||||
|
||||
@@ -102,7 +102,7 @@ def mergeSortTR (l : List α) (le : α → α → Bool := by exact fun a b => a
|
||||
where run : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
| _+2, xs =>
|
||||
let (l, r) := splitInTwo xs
|
||||
mergeTR (run l) (run r) le
|
||||
|
||||
@@ -136,13 +136,13 @@ where
|
||||
run : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
| _+2, xs =>
|
||||
let (l, r) := splitRevInTwo xs
|
||||
mergeTR (run' l) (run r) le
|
||||
run' : {n : Nat} → { l : List α // l.length = n } → List α
|
||||
| 0, ⟨[], _⟩ => []
|
||||
| 1, ⟨[a], _⟩ => [a]
|
||||
| n+2, xs =>
|
||||
| _+2, xs =>
|
||||
let (l, r) := splitRevInTwo' xs
|
||||
mergeTR (run' r) (run l) le
|
||||
|
||||
|
||||
@@ -725,16 +725,25 @@ theorem infix_iff_suffix_prefix {l₁ l₂ : List α} : l₁ <:+: l₂ ↔ ∃ t
|
||||
theorem IsInfix.eq_of_length (h : l₁ <:+: l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem IsInfix.eq_of_length_le (h : l₁ <:+: l₂) : l₂.length ≤ l₁.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length_le
|
||||
|
||||
theorem IsPrefix.eq_of_length (h : l₁ <+: l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem IsPrefix.eq_of_length_le (h : l₁ <+: l₂) : l₂.length ≤ l₁.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length_le
|
||||
|
||||
theorem IsSuffix.eq_of_length (h : l₁ <:+ l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem IsSuffix.eq_of_length_le (h : l₁ <:+ l₂) : l₂.length ≤ l₁.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length_le
|
||||
|
||||
theorem prefix_of_prefix_length_le :
|
||||
∀ {l₁ l₂ l₃ : List α}, l₁ <+: l₃ → l₂ <+: l₃ → length l₁ ≤ length l₂ → l₁ <+: l₂
|
||||
| [], l₂, _, _, _, _ => nil_prefix
|
||||
| a :: l₁, b :: l₂, _, ⟨r₁, rfl⟩, ⟨r₂, e⟩, ll => by
|
||||
| [], _, _, _, _, _ => nil_prefix
|
||||
| _ :: _, b :: _, _, ⟨_, rfl⟩, ⟨_, e⟩, ll => by
|
||||
injection e with _ e'; subst b
|
||||
rcases prefix_of_prefix_length_le ⟨_, rfl⟩ ⟨_, e'⟩ (le_of_succ_le_succ ll) with ⟨r₃, rfl⟩
|
||||
exact ⟨r₃, rfl⟩
|
||||
@@ -829,6 +838,24 @@ theorem isPrefix_iff : l₁ <+: l₂ ↔ ∀ i (h : i < l₁.length), l₂[i]? =
|
||||
rw (config := {occs := .pos [2]}) [← Nat.and_forall_add_one]
|
||||
simp [Nat.succ_lt_succ_iff, eq_comm]
|
||||
|
||||
theorem isPrefix_iff_getElem {l₁ l₂ : List α} :
|
||||
l₁ <+: l₂ ↔ ∃ (h : l₁.length ≤ l₂.length), ∀ x (hx : x < l₁.length),
|
||||
l₁[x] = l₂[x]'(Nat.lt_of_lt_of_le hx h) where
|
||||
mp h := ⟨h.length_le, fun _ _ ↦ h.getElem _⟩
|
||||
mpr h := by
|
||||
obtain ⟨hl, h⟩ := h
|
||||
induction l₂ generalizing l₁ with
|
||||
| nil =>
|
||||
simpa using hl
|
||||
| cons _ _ tail_ih =>
|
||||
cases l₁ with
|
||||
| nil =>
|
||||
exact nil_prefix
|
||||
| cons _ _ =>
|
||||
simp only [length_cons, Nat.add_le_add_iff_right, Fin.getElem_fin] at hl h
|
||||
simp only [cons_prefix_cons]
|
||||
exact ⟨h 0 (zero_lt_succ _), tail_ih hl fun a ha ↦ h a.succ (succ_lt_succ ha)⟩
|
||||
|
||||
-- See `Init.Data.List.Nat.Sublist` for `isSuffix_iff` and `ifInfix_iff`.
|
||||
|
||||
theorem isPrefix_filterMap_iff {β} {f : α → Option β} {l₁ : List α} {l₂ : List β} :
|
||||
|
||||
@@ -16,87 +16,6 @@ open Nat
|
||||
|
||||
/-! ## Zippers -/
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
theorem zip_map (f : α → γ) (g : β → δ) :
|
||||
∀ (l₁ : List α) (l₂ : List β), zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
|
||||
| [], l₂ => rfl
|
||||
| l₁, [] => by simp only [map, zip_nil_right]
|
||||
| a :: l₁, b :: l₂ => by
|
||||
simp only [map, zip_cons_cons, zip_map, Prod.map]; constructor
|
||||
|
||||
theorem zip_map_left (f : α → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_map_right (f : β → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [← zip_map, map_id]
|
||||
|
||||
@[simp] theorem tail_zip (l₁ : List α) (l₂ : List β) :
|
||||
(zip l₁ l₂).tail = zip l₁.tail l₂.tail := by
|
||||
cases l₁ <;> cases l₂ <;> simp
|
||||
|
||||
theorem zip_append :
|
||||
∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
|
||||
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
|
||||
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
|
||||
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
|
||||
| a :: l₁, r₁, b :: l₂, r₂, h => by
|
||||
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
|
||||
|
||||
theorem zip_map' (f : α → β) (g : α → γ) :
|
||||
∀ l : List α, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
|
||||
| [] => rfl
|
||||
| a :: l => by simp only [map, zip_cons_cons, zip_map']
|
||||
|
||||
theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip l₁ l₂ → a ∈ l₁ ∧ b ∈ l₂
|
||||
| _ :: l₁, _ :: l₂, h => by
|
||||
cases h
|
||||
case head => simp
|
||||
case tail h =>
|
||||
· have := of_mem_zip h
|
||||
exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
|
||||
|
||||
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
|
||||
|
||||
theorem map_fst_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
|
||||
| [], bs, _ => rfl
|
||||
| _ :: as, _ :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.fst (zip as bs) = _ :: as
|
||||
rw [map_fst_zip as bs h]
|
||||
| a :: as, [], h => by simp at h
|
||||
|
||||
theorem map_snd_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
|
||||
| _, [], _ => by
|
||||
rw [zip_nil_right]
|
||||
rfl
|
||||
| [], b :: bs, h => by simp at h
|
||||
| a :: as, b :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.snd (zip as bs) = _ :: bs
|
||||
rw [map_snd_zip as bs h]
|
||||
|
||||
theorem map_prod_left_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (x, f x)) = l.zip (l.map f) := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
theorem map_prod_right_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (f x, x)) = (l.map f).zip l := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
|
||||
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
theorem zipWith_comm (f : α → β → γ) :
|
||||
@@ -253,6 +172,65 @@ theorem zipWith_append (f : α → β → γ) (l la : List α) (l' lb : List β)
|
||||
simp only [length_cons, Nat.succ.injEq] at h
|
||||
simp [ih _ h]
|
||||
|
||||
theorem zipWith_eq_cons_iff {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
|
||||
zipWith f l₁ l₂ = g :: l ↔
|
||||
∃ a l₁' b l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ g = f a b ∧ l = zipWith f l₁' l₂' := by
|
||||
match l₁, l₂ with
|
||||
| [], [] => simp
|
||||
| [], b :: l₂ => simp
|
||||
| a :: l₁, [] => simp
|
||||
| a' :: l₁, b' :: l₂ =>
|
||||
simp only [zip_cons_cons, cons.injEq, Prod.mk.injEq]
|
||||
constructor
|
||||
· rintro ⟨⟨rfl, rfl⟩, rfl⟩
|
||||
refine ⟨a', l₁, b', l₂, by simp⟩
|
||||
· rintro ⟨a, l₁, b, l₂, ⟨rfl, rfl⟩, ⟨rfl, rfl⟩, rfl, rfl⟩
|
||||
simp
|
||||
|
||||
theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : List β} :
|
||||
zipWith f l₁ l₂ = l₁' ++ l₂' ↔
|
||||
∃ w x y z, w.length = y.length ∧ l₁ = w ++ x ∧ l₂ = y ++ z ∧ l₁' = zipWith f w y ∧ l₂' = zipWith f x z := by
|
||||
induction l₁ generalizing l₂ l₁' with
|
||||
| nil =>
|
||||
simp
|
||||
constructor
|
||||
· rintro ⟨rfl, rfl⟩
|
||||
exact ⟨[], [], [], by simp⟩
|
||||
· rintro ⟨_, _, _, -, ⟨rfl, rfl⟩, _, rfl, rfl, rfl⟩
|
||||
simp
|
||||
| cons x₁ l₁ ih₁ =>
|
||||
cases l₂ with
|
||||
| nil =>
|
||||
constructor
|
||||
· simp only [zipWith_nil_right, nil_eq, append_eq_nil, exists_and_left, and_imp]
|
||||
rintro rfl rfl
|
||||
exact ⟨[], x₁ :: l₁, [], by simp⟩
|
||||
· rintro ⟨w, x, y, z, h₁, _, h₃, rfl, rfl⟩
|
||||
simp only [nil_eq, append_eq_nil] at h₃
|
||||
obtain ⟨rfl, rfl⟩ := h₃
|
||||
simp
|
||||
| cons x₂ l₂ =>
|
||||
simp only [zipWith_cons_cons]
|
||||
rw [cons_eq_append_iff]
|
||||
constructor
|
||||
· rintro (⟨rfl, rfl⟩ | ⟨l₁'', rfl, h⟩)
|
||||
· exact ⟨[], x₁ :: l₁, [], x₂ :: l₂, by simp⟩
|
||||
· rw [ih₁] at h
|
||||
obtain ⟨w, x, y, z, h, rfl, rfl, h', rfl⟩ := h
|
||||
refine ⟨x₁ :: w, x, x₂ :: y, z, by simp [h, h']⟩
|
||||
· rintro ⟨w, x, y, z, h₁, h₂, h₃, rfl, rfl⟩
|
||||
rw [cons_eq_append_iff] at h₂
|
||||
rw [cons_eq_append_iff] at h₃
|
||||
obtain (⟨rfl, rfl⟩ | ⟨w', rfl, rfl⟩) := h₂
|
||||
· simp only [zipWith_nil_left, true_and, nil_eq, reduceCtorEq, false_and, exists_const,
|
||||
or_false]
|
||||
obtain (⟨rfl, rfl⟩ | ⟨y', rfl, rfl⟩) := h₃
|
||||
· simp
|
||||
· simp_all
|
||||
· obtain (⟨rfl, rfl⟩ | ⟨y', rfl, rfl⟩) := h₃
|
||||
· simp_all
|
||||
· simp_all [zipWith_append, Nat.succ_inj']
|
||||
|
||||
/-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :
|
||||
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
|
||||
@@ -260,6 +238,113 @@ theorem zipWith_append (f : α → β → γ) (l la : List α) (l' lb : List β)
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
theorem zip_eq_zipWith : ∀ (l₁ : List α) (l₂ : List β), zip l₁ l₂ = zipWith Prod.mk l₁ l₂
|
||||
| [], _ => rfl
|
||||
| _, [] => rfl
|
||||
| a :: l₁, b :: l₂ => by simp [zip_cons_cons, zip_eq_zipWith l₁ l₂]
|
||||
|
||||
theorem zip_map (f : α → γ) (g : β → δ) :
|
||||
∀ (l₁ : List α) (l₂ : List β), zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
|
||||
| [], _ => rfl
|
||||
| _, [] => by simp only [map, zip_nil_right]
|
||||
| _ :: _, _ :: _ => by
|
||||
simp only [map, zip_cons_cons, zip_map, Prod.map]; constructor
|
||||
|
||||
theorem zip_map_left (f : α → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_map_right (f : β → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [← zip_map, map_id]
|
||||
|
||||
@[simp] theorem tail_zip (l₁ : List α) (l₂ : List β) :
|
||||
(zip l₁ l₂).tail = zip l₁.tail l₂.tail := by
|
||||
cases l₁ <;> cases l₂ <;> simp
|
||||
|
||||
theorem zip_append :
|
||||
∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
|
||||
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
|
||||
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
|
||||
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
|
||||
| a :: l₁, r₁, b :: l₂, r₂, h => by
|
||||
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
|
||||
|
||||
theorem zip_map' (f : α → β) (g : α → γ) :
|
||||
∀ l : List α, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
|
||||
| [] => rfl
|
||||
| a :: l => by simp only [map, zip_cons_cons, zip_map']
|
||||
|
||||
theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip l₁ l₂ → a ∈ l₁ ∧ b ∈ l₂
|
||||
| _ :: l₁, _ :: l₂, h => by
|
||||
cases h
|
||||
case head => simp
|
||||
case tail h =>
|
||||
· have := of_mem_zip h
|
||||
exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
|
||||
|
||||
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
|
||||
|
||||
theorem map_fst_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
|
||||
| [], _, _ => rfl
|
||||
| _ :: as, _ :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.fst (zip as bs) = _ :: as
|
||||
rw [map_fst_zip as bs h]
|
||||
| _ :: _, [], h => by simp at h
|
||||
|
||||
theorem map_snd_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
|
||||
| _, [], _ => by
|
||||
rw [zip_nil_right]
|
||||
rfl
|
||||
| [], b :: bs, h => by simp at h
|
||||
| a :: as, b :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.snd (zip as bs) = _ :: bs
|
||||
rw [map_snd_zip as bs h]
|
||||
|
||||
theorem map_prod_left_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (x, f x)) = l.zip (l.map f) := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
theorem map_prod_right_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (f x, x)) = (l.map f).zip l := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
@[simp] theorem zip_eq_nil_iff {l₁ : List α} {l₂ : List β} :
|
||||
zip l₁ l₂ = [] ↔ l₁ = [] ∨ l₂ = [] := by
|
||||
simp [zip_eq_zipWith]
|
||||
|
||||
theorem zip_eq_cons_iff {l₁ : List α} {l₂ : List β} :
|
||||
zip l₁ l₂ = (a, b) :: l ↔
|
||||
∃ l₁' l₂', l₁ = a :: l₁' ∧ l₂ = b :: l₂' ∧ l = zip l₁' l₂' := by
|
||||
simp only [zip_eq_zipWith, zipWith_eq_cons_iff]
|
||||
constructor
|
||||
· rintro ⟨a, l₁, b, l₂, rfl, rfl, h, rfl, rfl⟩
|
||||
simp only [Prod.mk.injEq] at h
|
||||
obtain ⟨rfl, rfl⟩ := h
|
||||
simp
|
||||
· rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
|
||||
refine ⟨a, l₁', b, l₂', by simp⟩
|
||||
|
||||
theorem zip_eq_append_iff {l₁ : List α} {l₂ : List β} :
|
||||
zip l₁ l₂ = l₁' ++ l₂' ↔
|
||||
∃ w x y z, w.length = y.length ∧ l₁ = w ++ x ∧ l₂ = y ++ z ∧ l₁' = zip w y ∧ l₂' = zip x z := by
|
||||
simp [zip_eq_zipWith, zipWith_eq_append_iff]
|
||||
|
||||
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
|
||||
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### zipWithAll -/
|
||||
|
||||
theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
|
||||
@@ -345,9 +430,9 @@ theorem zip_unzip : ∀ l : List (α × β), zip (unzip l).1 (unzip l).2 = l
|
||||
|
||||
theorem unzip_zip_left :
|
||||
∀ {l₁ : List α} {l₂ : List β}, length l₁ ≤ length l₂ → (unzip (zip l₁ l₂)).1 = l₁
|
||||
| [], l₂, _ => rfl
|
||||
| l₁, [], h => by rw [eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero h)]; rfl
|
||||
| a :: l₁, b :: l₂, h => by
|
||||
| [], _, _ => rfl
|
||||
| _, [], h => by rw [eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero h)]; rfl
|
||||
| _ :: _, _ :: _, h => by
|
||||
simp only [zip_cons_cons, unzip_cons, unzip_zip_left (le_of_succ_le_succ h)]
|
||||
|
||||
theorem unzip_zip_right :
|
||||
|
||||
@@ -248,7 +248,7 @@ protected theorem add_mul (n m k : Nat) : (n + m) * k = n * k + m * k :=
|
||||
Nat.right_distrib n m k
|
||||
|
||||
protected theorem mul_assoc : ∀ (n m k : Nat), (n * m) * k = n * (m * k)
|
||||
| n, m, 0 => rfl
|
||||
| _, _, 0 => rfl
|
||||
| n, m, succ k => by simp [mul_succ, Nat.mul_assoc n m k, Nat.left_distrib]
|
||||
instance : Std.Associative (α := Nat) (· * ·) := ⟨Nat.mul_assoc⟩
|
||||
|
||||
@@ -634,6 +634,8 @@ theorem lt_succ_of_lt (h : a < b) : a < succ b := le_succ_of_le h
|
||||
|
||||
theorem lt_add_one_of_lt (h : a < b) : a < b + 1 := le_succ_of_le h
|
||||
|
||||
@[simp] theorem lt_one_iff : n < 1 ↔ n = 0 := Nat.lt_succ_iff.trans <| by rw [le_zero_eq]
|
||||
|
||||
theorem succ_pred_eq_of_ne_zero : ∀ {n}, n ≠ 0 → succ (pred n) = n
|
||||
| _+1, _ => rfl
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
@@ -36,7 +36,7 @@ private theorem two_mul_sub_one {n : Nat} (n_pos : n > 0) : (2*n - 1) % 2 = 1 :=
|
||||
/-! ### Preliminaries -/
|
||||
|
||||
/--
|
||||
An induction principal that works on divison by two.
|
||||
An induction principal that works on division by two.
|
||||
-/
|
||||
noncomputable def div2Induction {motive : Nat → Sort u}
|
||||
(n : Nat) (ind : ∀(n : Nat), (n > 0 → motive (n/2)) → motive n) : motive n := by
|
||||
|
||||
@@ -84,7 +84,7 @@ decreasing_by apply div_rec_lemma; assumption
|
||||
protected def mod : @& Nat → @& Nat → Nat
|
||||
/-
|
||||
Nat.modCore is defined by well-founded recursion and thus irreducible. Nevertheless it is
|
||||
desireable if trivial `Nat.mod` calculations, namely
|
||||
desirable if trivial `Nat.mod` calculations, namely
|
||||
* `Nat.mod 0 m` for all `m`
|
||||
* `Nat.mod n (m+n)` for concrete literals `n`
|
||||
reduce definitionally.
|
||||
@@ -269,7 +269,7 @@ protected theorem div_div_eq_div_mul (m n k : Nat) : m / n / k = m / (n * k) :=
|
||||
|
||||
theorem div_mul_le_self : ∀ (m n : Nat), m / n * n ≤ m
|
||||
| m, 0 => by simp
|
||||
| m, n+1 => (le_div_iff_mul_le (Nat.succ_pos _)).1 (Nat.le_refl _)
|
||||
| _, _+1 => (le_div_iff_mul_le (Nat.succ_pos _)).1 (Nat.le_refl _)
|
||||
|
||||
theorem div_lt_iff_lt_mul (Hk : 0 < k) : x / k < y ↔ x < y * k := by
|
||||
rw [← Nat.not_le, ← Nat.not_le]; exact not_congr (le_div_iff_mul_le Hk)
|
||||
|
||||
@@ -230,6 +230,17 @@ 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
|
||||
rw [Nat.min_def]
|
||||
simp
|
||||
@[simp] theorem min_add_right {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]
|
||||
|
||||
protected theorem sub_sub_eq_min : ∀ (a b : Nat), a - (a - b) = min a b
|
||||
| 0, _ => by rw [Nat.zero_sub, Nat.zero_min]
|
||||
| _, 0 => by rw [Nat.sub_zero, Nat.sub_self, Nat.min_zero]
|
||||
@@ -284,6 +295,17 @@ protected theorem max_assoc : ∀ (a b c : Nat), max (max a b) c = max a (max b
|
||||
| _+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
|
||||
rw [Nat.max_def]
|
||||
simp
|
||||
@[simp] theorem max_add_right {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]
|
||||
|
||||
protected theorem sub_add_eq_max (a b : Nat) : a - b + b = max a b := by
|
||||
match Nat.le_total a b with
|
||||
| .inl hl => rw [Nat.max_eq_right hl, Nat.sub_eq_zero_iff_le.mpr hl, Nat.zero_add]
|
||||
@@ -583,6 +605,9 @@ theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
|
||||
| zero => simp_all
|
||||
| succ k => omega
|
||||
|
||||
@[simp] theorem mod_mul_mod {a b c : Nat} : (a % c * b) % c = a * b % c := by
|
||||
rw [mul_mod, mod_mod, ← mul_mod]
|
||||
|
||||
/-! ### pow -/
|
||||
|
||||
theorem pow_succ' {m n : Nat} : m ^ n.succ = m * m ^ n := by
|
||||
@@ -745,6 +770,16 @@ protected theorem two_pow_pred_mod_two_pow (h : 0 < w) :
|
||||
rw [mod_eq_of_lt]
|
||||
apply Nat.pow_pred_lt_pow (by omega) h
|
||||
|
||||
protected theorem pow_lt_pow_iff_pow_mul_le_pow {a n m : Nat} (h : 1 < a) :
|
||||
a ^ n < a ^ m ↔ a ^ n * a ≤ a ^ m := by
|
||||
rw [←Nat.pow_add_one, Nat.pow_le_pow_iff_right (by omega), Nat.pow_lt_pow_iff_right (by omega)]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem two_pow_pred_mul_two (h : 0 < w) :
|
||||
2 ^ (w - 1) * 2 = 2 ^ w := by
|
||||
simp [← Nat.pow_succ, Nat.sub_add_cancel h]
|
||||
|
||||
/-! ### log2 -/
|
||||
|
||||
@[simp]
|
||||
@@ -839,15 +874,15 @@ theorem shiftLeft_succ_inside (m n : Nat) : m <<< (n+1) = (2*m) <<< n := rfl
|
||||
|
||||
/-- Shiftleft on successor with multiple moved to outside. -/
|
||||
theorem shiftLeft_succ : ∀(m n), m <<< (n + 1) = 2 * (m <<< n)
|
||||
| m, 0 => rfl
|
||||
| m, k + 1 => by
|
||||
| _, 0 => rfl
|
||||
| _, k + 1 => by
|
||||
rw [shiftLeft_succ_inside _ (k+1)]
|
||||
rw [shiftLeft_succ _ k, shiftLeft_succ_inside]
|
||||
|
||||
/-- Shiftright on successor with division moved inside. -/
|
||||
theorem shiftRight_succ_inside : ∀m n, m >>> (n+1) = (m/2) >>> n
|
||||
| m, 0 => rfl
|
||||
| m, k + 1 => by
|
||||
| _, 0 => rfl
|
||||
| _, k + 1 => by
|
||||
rw [shiftRight_succ _ (k+1)]
|
||||
rw [shiftRight_succ_inside _ k, shiftRight_succ]
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega
|
||||
@@ -15,7 +15,7 @@ in particular
|
||||
and its corollary
|
||||
`Nat.mod_pow_succ : x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b)`.
|
||||
|
||||
It contains the necesssary preliminary results relating order and `*` and `/`,
|
||||
It contains the necessary preliminary results relating order and `*` and `/`,
|
||||
which should probably be moved to their own file.
|
||||
-/
|
||||
|
||||
|
||||
@@ -35,4 +35,4 @@ theorem neZero_iff {n : R} : NeZero n ↔ n ≠ 0 :=
|
||||
⟨fun h ↦ h.out, NeZero.mk⟩
|
||||
|
||||
@[simp] theorem neZero_zero_iff_false {α : Type _} [Zero α] : NeZero (0 : α) ↔ False :=
|
||||
⟨fun h ↦ h.ne rfl, fun h ↦ h.elim⟩
|
||||
⟨fun _ ↦ NeZero.ne (0 : α) rfl, fun h ↦ h.elim⟩
|
||||
|
||||
@@ -8,3 +8,5 @@ import Init.Data.Option.Basic
|
||||
import Init.Data.Option.BasicAux
|
||||
import Init.Data.Option.Instances
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.Option.Attach
|
||||
import Init.Data.Option.List
|
||||
|
||||
178
src/Init/Data/Option/Attach.lean
Normal file
178
src/Init/Data/Option/Attach.lean
Normal file
@@ -0,0 +1,178 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Option.Basic
|
||||
import Init.Data.Option.List
|
||||
import Init.Data.List.Attach
|
||||
import Init.BinderPredicates
|
||||
|
||||
namespace Option
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
`Option {x // P x}` is the same as the input `Option α`.
|
||||
-/
|
||||
@[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}`. -/
|
||||
@[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}`. -/
|
||||
@[inline] def attach (xs : Option α) : Option {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
|
||||
@[simp] theorem attachWith_none : (none : Option α).attachWith P H = none := rfl
|
||||
|
||||
@[simp] theorem attach_some {x : α} :
|
||||
(some x).attach = some ⟨x, rfl⟩ := rfl
|
||||
@[simp] theorem attachWith_some {x : α} {P : α → Prop} (h : ∀ (b : α), b ∈ some x → P b) :
|
||||
(some x).attachWith P h = some ⟨x, by simpa using h⟩ := rfl
|
||||
|
||||
theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
|
||||
o₁.attach = o₂.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α → Prop} {H : ∀ x ∈ o₁, P x} :
|
||||
o₁.attachWith P H = o₂.attachWith P fun x h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
theorem attach_map_coe (o : Option α) (f : α → β) :
|
||||
(o.attach.map fun (i : {i // i ∈ o}) => f i) = o.map f := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_map_val (o : Option α) (f : α → β) :
|
||||
(o.attach.map fun i => f i.val) = o.map f :=
|
||||
attach_map_coe _ _
|
||||
|
||||
@[simp]
|
||||
theorem attach_map_subtype_val (o : Option α) :
|
||||
o.attach.map Subtype.val = o :=
|
||||
(attach_map_coe _ _).trans (congrFun Option.map_id _)
|
||||
|
||||
theorem attachWith_map_coe {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
((o.attachWith p H).map fun (i : { i // p i}) => f i.val) = o.map f := by
|
||||
cases o <;> simp [H]
|
||||
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
((o.attachWith p H).map fun i => f i.val) = o.map f :=
|
||||
attachWith_map_coe _ _ _
|
||||
|
||||
@[simp]
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
(o.attachWith p H).map Subtype.val = o :=
|
||||
(attachWith_map_coe _ _ _).trans (congrFun Option.map_id _)
|
||||
|
||||
@[simp] theorem mem_attach : ∀ (o : Option α) (x : {x // x ∈ o}), x ∈ o.attach
|
||||
| none, ⟨x, h⟩ => by simp at h
|
||||
| some a, ⟨x, h⟩ => by simpa using h
|
||||
|
||||
@[simp] theorem isNone_attach (o : Option α) : o.attach.isNone = o.isNone := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isNone_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
(o.attachWith p H).isNone = o.isNone := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isSome_attach (o : Option α) : o.attach.isSome = o.isSome := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem isSome_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
(o.attachWith p H).isSome = o.isSome := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attach_eq_none_iff (o : Option α) : o.attach = none ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // x ∈ o}} :
|
||||
o.attach = some x ↔ o = some x.val := by
|
||||
cases o <;> cases x <;> simp
|
||||
|
||||
@[simp] theorem attachWith_eq_none_iff {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||||
o.attachWith p H = none ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
@[simp] theorem attachWith_eq_some_iff {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) {x : {x // p x}} :
|
||||
o.attachWith p H = some x ↔ o = some x.val := by
|
||||
cases o <;> cases x <;> simp
|
||||
|
||||
@[simp] theorem get_attach {o : Option α} (h : o.attach.isSome = true) :
|
||||
o.attach.get h = ⟨o.get (by simpa using h), by simp⟩ := by
|
||||
cases o
|
||||
· simp at h
|
||||
· simp [get_some]
|
||||
|
||||
@[simp] theorem get_attachWith {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) (h : (o.attachWith p H).isSome) :
|
||||
(o.attachWith p H).get h = ⟨o.get (by simpa using h), H _ (by simp)⟩ := by
|
||||
cases o
|
||||
· simp at h
|
||||
· simp [get_some]
|
||||
|
||||
@[simp] theorem toList_attach (o : Option α) :
|
||||
o.attach.toList = o.toList.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_map {o : Option α} (f : α → β) :
|
||||
(o.map f).attach = o.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attachWith_map {o : Option α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ o.map f → P b} :
|
||||
(o.map f).attachWith P H = (o.attachWith (P ∘ f) (fun a h => H _ (mem_map_of_mem f h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem map_attach {o : Option α} (f : { x // x ∈ o } → β) :
|
||||
o.attach.map f = o.pmap (fun a (h : a ∈ o) => f ⟨a, h⟩) (fun a h => h) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem map_attachWith {o : Option α} {P : α → Prop} {H : ∀ (a : α), a ∈ o → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(o.attachWith P H).map f =
|
||||
o.pmap (fun a (h : a ∈ o ∧ P a) => f ⟨a, h.2⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_bind {o : Option α} {f : α → Option β} :
|
||||
(o.bind f).attach =
|
||||
o.attach.bind fun ⟨x, h⟩ => (f x).attach.map fun ⟨y, h'⟩ => ⟨y, mem_bind_iff.mpr ⟨x, h, h'⟩⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem bind_attach {o : Option α} {f : {x // x ∈ o} → Option β} :
|
||||
o.attach.bind f = o.pbind fun a h => f ⟨a, h⟩ := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||||
o.pbind f = o.attach.bind fun ⟨x, h⟩ => f x h := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem attach_filter {o : Option α} {p : α → Bool} :
|
||||
(o.filter p).attach =
|
||||
o.attach.bind fun ⟨x, h⟩ => if h' : p x then some ⟨x, by simp_all⟩ else none := by
|
||||
cases o with
|
||||
| none => simp
|
||||
| some a =>
|
||||
simp only [filter_some, attach_some]
|
||||
ext
|
||||
simp only [mem_def, attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
|
||||
dite_none_right_eq_some]
|
||||
constructor
|
||||
· rintro ⟨h, w⟩
|
||||
refine ⟨h, by ext; simpa using w⟩
|
||||
· rintro ⟨h, rfl⟩
|
||||
simp [h]
|
||||
|
||||
theorem filter_attach {o : Option α} {p : {x // x ∈ o} → Bool} :
|
||||
o.attach.filter p = o.pbind fun a h => if p ⟨a, h⟩ then some ⟨a, h⟩ else none := by
|
||||
cases o <;> simp [filter_some]
|
||||
|
||||
end Option
|
||||
@@ -202,7 +202,7 @@ result.
|
||||
instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
rfl {x} :=
|
||||
match x with
|
||||
| some x => LawfulBEq.rfl (α := α)
|
||||
| some _ => LawfulBEq.rfl (α := α)
|
||||
| none => rfl
|
||||
eq_of_beq {x y h} := by
|
||||
match x, y with
|
||||
|
||||
@@ -138,6 +138,10 @@ theorem bind_eq_none' {o : Option α} {f : α → Option β} :
|
||||
o.bind f = none ↔ ∀ b a, a ∈ o → b ∉ f a := by
|
||||
simp only [eq_none_iff_forall_not_mem, not_exists, not_and, mem_def, bind_eq_some]
|
||||
|
||||
theorem mem_bind_iff {o : Option α} {f : α → Option β} :
|
||||
b ∈ o.bind f ↔ ∃ a, a ∈ o ∧ b ∈ f a := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem bind_comm {f : α → β → Option γ} (a : Option α) (b : Option β) :
|
||||
(a.bind fun x => b.bind (f x)) = b.bind fun y => a.bind fun x => f x y := by
|
||||
cases a <;> cases b <;> rfl
|
||||
@@ -232,9 +236,27 @@ theorem isSome_filter_of_isSome (p : α → Bool) (o : Option α) (h : (o.filter
|
||||
cases o <;> simp at h ⊢
|
||||
|
||||
@[simp] theorem filter_eq_none {p : α → Bool} :
|
||||
Option.filter p o = none ↔ o = none ∨ ∀ a, a ∈ o → ¬ p a := by
|
||||
o.filter p = none ↔ o = none ∨ ∀ a, a ∈ o → ¬ p a := by
|
||||
cases o <;> simp [filter_some]
|
||||
|
||||
@[simp] theorem filter_eq_some {o : Option α} {p : α → Bool} :
|
||||
o.filter p = some a ↔ a ∈ o ∧ p a := by
|
||||
cases o with
|
||||
| none => simp
|
||||
| some a =>
|
||||
simp [filter_some]
|
||||
split <;> rename_i h
|
||||
· simp only [some.injEq, iff_self_and]
|
||||
rintro rfl
|
||||
exact h
|
||||
· simp only [reduceCtorEq, false_iff, not_and, Bool.not_eq_true]
|
||||
rintro rfl
|
||||
simpa using h
|
||||
|
||||
theorem mem_filter_iff {p : α → Bool} {a : α} {o : Option α} :
|
||||
a ∈ o.filter p ↔ a ∈ o ∧ p a := by
|
||||
simp
|
||||
|
||||
@[simp] theorem all_guard (p : α → Prop) [DecidablePred p] (a : α) :
|
||||
Option.all q (guard p a) = (!p a || q a) := by
|
||||
simp only [guard]
|
||||
@@ -308,8 +330,8 @@ theorem guard_comp {p : α → Prop} [DecidablePred p] {f : β → α} :
|
||||
theorem liftOrGet_eq_or_eq {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
|
||||
∀ o₁ o₂, liftOrGet f o₁ o₂ = o₁ ∨ liftOrGet f o₁ o₂ = o₂
|
||||
| none, none => .inl rfl
|
||||
| some a, none => .inl rfl
|
||||
| none, some b => .inr rfl
|
||||
| some _, none => .inl rfl
|
||||
| none, some _ => .inr rfl
|
||||
| some a, some b => by have := h a b; simp [liftOrGet] at this ⊢; exact this
|
||||
|
||||
@[simp] theorem liftOrGet_none_left {f} {b : Option α} : liftOrGet f none b = b := by
|
||||
@@ -350,6 +372,8 @@ end choice
|
||||
|
||||
@[simp] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
|
||||
|
||||
-- See `Init.Data.Option.List` for lemmas about `toList`.
|
||||
|
||||
@[simp] theorem or_some : (some a).or o = some a := rfl
|
||||
@[simp] theorem none_or : none.or o = o := rfl
|
||||
|
||||
|
||||
14
src/Init/Data/Option/List.lean
Normal file
14
src/Init/Data/Option/List.lean
Normal file
@@ -0,0 +1,14 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
namespace Option
|
||||
|
||||
@[simp] theorem mem_toList {a : α} {o : Option α} : a ∈ o.toList ↔ a ∈ o := by
|
||||
cases o <;> simp [eq_comm]
|
||||
|
||||
end Option
|
||||
@@ -51,6 +51,12 @@ instance [Repr α] : Repr (id α) :=
|
||||
instance [Repr α] : Repr (Id α) :=
|
||||
inferInstanceAs (Repr α)
|
||||
|
||||
/-
|
||||
This instance allows us to use `Empty` as a type parameter without causing instance synthesis to fail.
|
||||
-/
|
||||
instance : Repr Empty where
|
||||
reprPrec := nofun
|
||||
|
||||
instance : Repr Bool where
|
||||
reprPrec
|
||||
| true, _ => "true"
|
||||
|
||||
@@ -322,7 +322,7 @@ theorem lt_next (s : String) (i : Pos) : i.1 < (s.next i).1 :=
|
||||
|
||||
theorem utf8PrevAux_lt_of_pos : ∀ (cs : List Char) (i p : Pos), p ≠ 0 →
|
||||
(utf8PrevAux cs i p).1 < p.1
|
||||
| [], i, p, h =>
|
||||
| [], _, _, h =>
|
||||
Nat.lt_of_le_of_lt (Nat.zero_le _)
|
||||
(Nat.zero_lt_of_ne_zero (mt (congrArg Pos.mk) h))
|
||||
| c::cs, i, p, h => by
|
||||
|
||||
@@ -156,11 +156,11 @@ theorem getElem?_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem d
|
||||
theorem getElem!_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
[Inhabited elem] (c : cont) (i : idx) (h : dom c i) [Decidable (dom c i)] :
|
||||
c[i]! = c[i]'h := by
|
||||
simp only [getElem!_def, getElem?_def, h]
|
||||
simp [getElem!_def, getElem?_def, h]
|
||||
|
||||
theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
|
||||
[Inhabited elem] (c : cont) (i : idx) (h : ¬dom c i) [Decidable (dom c i)] : c[i]! = default := by
|
||||
simp only [getElem!_def, getElem?_def, h]
|
||||
simp [getElem!_def, getElem?_def, h]
|
||||
|
||||
namespace Fin
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Additional goodies for writing macros
|
||||
-/
|
||||
prelude
|
||||
import Init.MetaTypes
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Array.GetLit
|
||||
import Init.Data.Option.BasicAux
|
||||
|
||||
namespace Lean
|
||||
@@ -862,7 +862,7 @@ partial def decodeRawStrLitAux (s : String) (i : String.Pos) (num : Nat) : Strin
|
||||
/--
|
||||
Takes the string literal lexical syntax parsed by the parser and interprets it as a string.
|
||||
This is where escape sequences are processed for example.
|
||||
The string `s` is is either a plain string literal or a raw string literal.
|
||||
The string `s` is either a plain string literal or a raw string literal.
|
||||
|
||||
If it returns `none` then the string literal is ill-formed, which indicates a bug in the parser.
|
||||
The function is not required to return `none` if the string literal is ill-formed.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.Int
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.IntList
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.LinearCombo
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.DivMod
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Zip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.Coeffs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.PropLemmas
|
||||
|
||||
@@ -754,10 +754,11 @@ infer the proof of `Nonempty α`.
|
||||
noncomputable def Classical.ofNonempty {α : Sort u} [Nonempty α] : α :=
|
||||
Classical.choice inferInstance
|
||||
|
||||
instance (α : Sort u) {β : Sort v} [Nonempty β] : Nonempty (α → β) :=
|
||||
instance {α : Sort u} {β : Sort v} [Nonempty β] : Nonempty (α → β) :=
|
||||
Nonempty.intro fun _ => Classical.ofNonempty
|
||||
|
||||
instance (α : Sort u) {β : α → Sort v} [(a : α) → Nonempty (β a)] : Nonempty ((a : α) → β a) :=
|
||||
instance Pi.instNonempty {α : Sort u} {β : α → Sort v} [(a : α) → Nonempty (β a)] :
|
||||
Nonempty ((a : α) → β a) :=
|
||||
Nonempty.intro fun _ => Classical.ofNonempty
|
||||
|
||||
instance : Inhabited (Sort u) where
|
||||
@@ -766,7 +767,8 @@ instance : Inhabited (Sort u) where
|
||||
instance (α : Sort u) {β : Sort v} [Inhabited β] : Inhabited (α → β) where
|
||||
default := fun _ => default
|
||||
|
||||
instance (α : Sort u) {β : α → Sort v} [(a : α) → Inhabited (β a)] : Inhabited ((a : α) → β a) where
|
||||
instance Pi.instInhabited {α : Sort u} {β : α → Sort v} [(a : α) → Inhabited (β a)] :
|
||||
Inhabited ((a : α) → β a) where
|
||||
default := fun _ => default
|
||||
|
||||
deriving instance Inhabited for Bool
|
||||
@@ -1210,7 +1212,7 @@ class HDiv (α : Type u) (β : Type v) (γ : outParam (Type w)) where
|
||||
* For most types like `Nat`, `Int`, `Rat`, `Real`, `a / 0` is defined to be `0`.
|
||||
* For `Nat`, `a / b` rounds downwards.
|
||||
* For `Int`, `a / b` rounds downwards if `b` is positive or upwards if `b` is negative.
|
||||
It is implemented as `Int.ediv`, the unique function satisfiying
|
||||
It is implemented as `Int.ediv`, the unique function satisfying
|
||||
`a % b + b * (a / b) = a` and `0 ≤ a % b < natAbs b` for `b ≠ 0`.
|
||||
Other rounding conventions are available using the functions
|
||||
`Int.fdiv` (floor rounding) and `Int.div` (truncation rounding).
|
||||
@@ -1364,7 +1366,7 @@ class Pow (α : Type u) (β : Type v) where
|
||||
/-- `a ^ b` computes `a` to the power of `b`. See `HPow`. -/
|
||||
pow : α → β → α
|
||||
|
||||
/-- The homogenous version of `Pow` where the exponent is a `Nat`.
|
||||
/-- The homogeneous version of `Pow` where the exponent is a `Nat`.
|
||||
The purpose of this class is that it provides a default `Pow` instance,
|
||||
which can be used to specialize the exponent to `Nat` during elaboration.
|
||||
|
||||
@@ -2065,7 +2067,7 @@ The size of type `USize`, that is, `2^System.Platform.numBits`, which may
|
||||
be either `2^32` or `2^64` depending on the platform's architecture.
|
||||
|
||||
Remark: we define `USize.size` using `(2^numBits - 1) + 1` to ensure the
|
||||
Lean unifier can solve contraints such as `?m + 1 = USize.size`. Recall that
|
||||
Lean unifier can solve constraints such as `?m + 1 = USize.size`. Recall that
|
||||
`numBits` does not reduce to a numeral in the Lean kernel since it is platform
|
||||
specific. Without this trick, the following definition would be rejected by the
|
||||
Lean type checker.
|
||||
@@ -2568,7 +2570,9 @@ structure Array (α : Type u) where
|
||||
/--
|
||||
Converts a `List α` into an `Array α`.
|
||||
|
||||
At runtime, this constructor is implemented by `List.toArray` and is O(n) in the length of the
|
||||
You can also use the synonym `List.toArray` when dot notation is convenient.
|
||||
|
||||
At runtime, this constructor is implemented by `List.toArrayImpl` and is O(n) in the length of the
|
||||
list.
|
||||
-/
|
||||
mk ::
|
||||
@@ -2582,6 +2586,9 @@ structure Array (α : Type u) where
|
||||
attribute [extern "lean_array_to_list"] Array.toList
|
||||
attribute [extern "lean_array_mk"] Array.mk
|
||||
|
||||
@[inherit_doc Array.mk, match_pattern]
|
||||
abbrev List.toArray (xs : List α) : Array α := .mk xs
|
||||
|
||||
/-- Construct a new empty array with initial capacity `c`. -/
|
||||
@[extern "lean_mk_empty_array_with_capacity"]
|
||||
def Array.mkEmpty {α : Type u} (c : @& Nat) : Array α where
|
||||
@@ -2709,7 +2716,10 @@ def Array.extract (as : Array α) (start stop : Nat) : Array α :=
|
||||
let sz' := Nat.sub (min stop as.size) start
|
||||
loop sz' start (mkEmpty sz')
|
||||
|
||||
/-- Auxiliary definition for `List.toArray`. -/
|
||||
/--
|
||||
Auxiliary definition for `List.toArray`.
|
||||
`List.toArrayAux as r = r ++ as.toArray`
|
||||
-/
|
||||
@[inline_if_reduce]
|
||||
def List.toArrayAux : List α → Array α → Array α
|
||||
| nil, r => r
|
||||
@@ -2725,7 +2735,7 @@ def List.redLength : List α → Nat
|
||||
-- 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]
|
||||
def List.toArray (as : List α) : Array α :=
|
||||
def List.toArrayImpl (as : List α) : Array α :=
|
||||
as.toArrayAux (Array.mkEmpty as.redLength)
|
||||
|
||||
/-- The typeclass which supplies the `>>=` "bind" function. See `Monad`. -/
|
||||
|
||||
@@ -352,10 +352,10 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
@[simp] theorem exists_or_eq_left' (y : α) (p : α → Prop) : ∃ x : α, y = x ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right' (y : α) (p : α → Prop) : ∃ x : α, p x ∨ y = x := ⟨y, .inr rfl⟩
|
||||
|
||||
@[simp] theorem exists_prop' {p : Prop} : (∃ _ : α, p) ↔ Nonempty α ∧ p :=
|
||||
theorem exists_prop' {p : Prop} : (∃ _ : α, p) ↔ Nonempty α ∧ p :=
|
||||
⟨fun ⟨a, h⟩ => ⟨⟨a⟩, h⟩, fun ⟨⟨a⟩, h⟩ => ⟨a, h⟩⟩
|
||||
|
||||
theorem exists_prop : (∃ _h : a, b) ↔ a ∧ b :=
|
||||
@[simp] theorem exists_prop : (∃ _h : a, b) ↔ a ∧ b :=
|
||||
⟨fun ⟨hp, hq⟩ => ⟨hp, hq⟩, fun ⟨hp, hq⟩ => ⟨hp, hq⟩⟩
|
||||
|
||||
@[simp] theorem exists_apply_eq_apply (f : α → β) (a' : α) : ∃ a, f a = f a' := ⟨a', rfl⟩
|
||||
@@ -458,7 +458,7 @@ theorem Decidable.imp_iff_not_or [Decidable a] : (a → b) ↔ (¬a ∨ b) :=
|
||||
theorem Decidable.imp_iff_or_not [Decidable b] : b → a ↔ a ∨ ¬b :=
|
||||
Decidable.imp_iff_not_or.trans or_comm
|
||||
|
||||
theorem Decidable.imp_or [h : Decidable a] : (a → b ∨ c) ↔ (a → b) ∨ (a → c) :=
|
||||
theorem Decidable.imp_or [Decidable a] : (a → b ∨ c) ↔ (a → b) ∨ (a → c) :=
|
||||
if h : a then by
|
||||
rw [imp_iff_right h, imp_iff_right h, imp_iff_right h]
|
||||
else by
|
||||
|
||||
@@ -149,26 +149,27 @@ syntax (name := assumption) "assumption" : tactic
|
||||
|
||||
/--
|
||||
`contradiction` closes the main goal if its hypotheses are "trivially contradictory".
|
||||
|
||||
- Inductive type/family with no applicable constructors
|
||||
```lean
|
||||
example (h : False) : p := by contradiction
|
||||
```
|
||||
```lean
|
||||
example (h : False) : p := by contradiction
|
||||
```
|
||||
- Injectivity of constructors
|
||||
```lean
|
||||
example (h : none = some true) : p := by contradiction --
|
||||
```
|
||||
```lean
|
||||
example (h : none = some true) : p := by contradiction --
|
||||
```
|
||||
- Decidable false proposition
|
||||
```lean
|
||||
example (h : 2 + 2 = 3) : p := by contradiction
|
||||
```
|
||||
```lean
|
||||
example (h : 2 + 2 = 3) : p := by contradiction
|
||||
```
|
||||
- Contradictory hypotheses
|
||||
```lean
|
||||
example (h : p) (h' : ¬ p) : q := by contradiction
|
||||
```
|
||||
```lean
|
||||
example (h : p) (h' : ¬ p) : q := by contradiction
|
||||
```
|
||||
- Other simple contradictions such as
|
||||
```lean
|
||||
example (x : Nat) (h : x ≠ x) : p := by contradiction
|
||||
```
|
||||
```lean
|
||||
example (x : Nat) (h : x ≠ x) : p := by contradiction
|
||||
```
|
||||
-/
|
||||
syntax (name := contradiction) "contradiction" : tactic
|
||||
|
||||
@@ -363,31 +364,24 @@ syntax (name := fail) "fail" (ppSpace str)? : tactic
|
||||
syntax (name := eqRefl) "eq_refl" : tactic
|
||||
|
||||
/--
|
||||
`rfl` tries to close the current goal using reflexivity.
|
||||
This is supposed to be an extensible tactic and users can add their own support
|
||||
for new reflexive relations.
|
||||
|
||||
Remark: `rfl` is an extensible tactic. We later add `macro_rules` to try different
|
||||
reflexivity theorems (e.g., `Iff.rfl`).
|
||||
This tactic applies to a goal whose target has the form `x ~ x`,
|
||||
where `~` is equality, heterogeneous equality or any relation that
|
||||
has a reflexivity lemma tagged with the attribute @[refl].
|
||||
-/
|
||||
macro "rfl" : tactic => `(tactic| case' _ => fail "The rfl tactic failed. Possible reasons:
|
||||
- The goal is not a reflexive relation (neither `=` nor a relation with a @[refl] lemma).
|
||||
- The arguments of the relation are not equal.
|
||||
Try using the reflexivity lemma for your relation explicitly, e.g. `exact Eq.refl _` or
|
||||
`exact HEq.rfl` etc.")
|
||||
|
||||
macro_rules | `(tactic| rfl) => `(tactic| eq_refl)
|
||||
macro_rules | `(tactic| rfl) => `(tactic| exact HEq.rfl)
|
||||
syntax "rfl" : tactic
|
||||
|
||||
/--
|
||||
This tactic applies to a goal whose target has the form `x ~ x`,
|
||||
where `~` is a reflexive relation other than `=`,
|
||||
that is, a relation which has a reflexive lemma tagged with the attribute @[refl].
|
||||
The same as `rfl`, but without trying `eq_refl` at the end.
|
||||
-/
|
||||
syntax (name := applyRfl) "apply_rfl" : tactic
|
||||
|
||||
-- We try `apply_rfl` first, beause it produces a nice error message
|
||||
macro_rules | `(tactic| rfl) => `(tactic| apply_rfl)
|
||||
|
||||
-- But, mostly for backward compatibility, we try `eq_refl` too (reduces more aggressively)
|
||||
macro_rules | `(tactic| rfl) => `(tactic| eq_refl)
|
||||
-- Als for backward compatibility, because `exact` can trigger the implicit lambda feature (see #5366)
|
||||
macro_rules | `(tactic| rfl) => `(tactic| exact HEq.rfl)
|
||||
/--
|
||||
`rfl'` is similar to `rfl`, but disables smart unfolding and unfolds all kinds of definitions,
|
||||
theorems included (relevant for declarations defined by well-founded recursion).
|
||||
@@ -405,6 +399,19 @@ example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by ac_rfl
|
||||
-/
|
||||
syntax (name := acRfl) "ac_rfl" : tactic
|
||||
|
||||
/--
|
||||
`ac_nf` normalizes equalities up to application of an associative and commutative operator.
|
||||
```
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by
|
||||
ac_nf
|
||||
-- goal: a + (b + (c + d)) = a + (b + (c + d))
|
||||
```
|
||||
-/
|
||||
syntax (name := acNf) "ac_nf" : tactic
|
||||
|
||||
/--
|
||||
The `sorry` tactic closes the goal using `sorryAx`. This is intended for stubbing out incomplete
|
||||
parts of a proof while still having a syntactically correct proof skeleton. Lean will give
|
||||
@@ -456,7 +463,7 @@ syntax (name := change) "change " term (location)? : tactic
|
||||
|
||||
/--
|
||||
* `change a with b` will change occurrences of `a` to `b` in the goal,
|
||||
assuming `a` and `b` are are definitionally equal.
|
||||
assuming `a` and `b` are definitionally equal.
|
||||
* `change a with b at h` similarly changes `a` to `b` in the type of hypothesis `h`.
|
||||
-/
|
||||
syntax (name := changeWith) "change " term " with " term (location)? : tactic
|
||||
@@ -773,8 +780,9 @@ macro_rules
|
||||
macro "refine_lift' " e:term : tactic => `(tactic| focus (refine' no_implicit_lambda% $e; rotate_right))
|
||||
/-- Similar to `have`, but using `refine'` -/
|
||||
macro "have' " d:haveDecl : tactic => `(tactic| refine_lift' have $d:haveDecl; ?_)
|
||||
/-- Similar to `have`, but using `refine'` -/
|
||||
set_option linter.missingDocs false in -- OK, because `tactic_alt` causes inheritance of docs
|
||||
macro (priority := high) "have'" x:ident " := " p:term : tactic => `(tactic| have' $x:ident : _ := $p)
|
||||
attribute [tactic_alt tacticHave'_] «tacticHave'_:=_»
|
||||
/-- Similar to `let`, but using `refine'` -/
|
||||
macro "let' " d:letDecl : tactic => `(tactic| refine_lift' let $d:letDecl; ?_)
|
||||
|
||||
@@ -793,7 +801,7 @@ syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ " => " (hole <|> synth
|
||||
After `with`, there is an optional tactic that runs on all branches, and
|
||||
then a list of alternatives.
|
||||
-/
|
||||
syntax inductionAlts := " with" (ppSpace tactic)? withPosition((colGe inductionAlt)+)
|
||||
syntax inductionAlts := " with" (ppSpace colGt tactic)? withPosition((colGe inductionAlt)+)
|
||||
|
||||
/--
|
||||
Assuming `x` is a variable in the local context with an inductive type,
|
||||
@@ -1588,7 +1596,7 @@ macro "get_elem_tactic" : tactic =>
|
||||
There is a proof embedded in the right-hand-side, and we want it to be just `hi`.
|
||||
If `omega` is used to "fill" this proof, we will have a more complex proof term that
|
||||
cannot be inferred by unification.
|
||||
We hardcoded `assumption` here to ensure users cannot accidentaly break this IF
|
||||
We hardcoded `assumption` here to ensure users cannot accidentally break this IF
|
||||
they add new `macro_rules` for `get_elem_tactic_trivial`.
|
||||
|
||||
TODO: Implement priorities for `macro_rules`.
|
||||
@@ -1598,7 +1606,7 @@ macro "get_elem_tactic" : tactic =>
|
||||
| get_elem_tactic_trivial
|
||||
| fail "failed to prove index is valid, possible solutions:
|
||||
- Use `have`-expressions to prove the index is valid
|
||||
- Use `a[i]!` notation instead, runtime check is perfomed, and 'Panic' error message is produced if index is not valid
|
||||
- Use `a[i]!` notation instead, runtime check is performed, and 'Panic' error message is produced if index is not valid
|
||||
- Use `a[i]?` notation instead, result is an `Option` type
|
||||
- Use `a[i]'h` notation instead, where `h` is a proof that index is valid"
|
||||
)
|
||||
|
||||
@@ -249,8 +249,8 @@ theorem lex_def {r : α → α → Prop} {s : β → β → Prop} {p q : α ×
|
||||
Prod.Lex r s p q ↔ r p.1 q.1 ∨ p.1 = q.1 ∧ s p.2 q.2 :=
|
||||
⟨fun h => by cases h <;> simp [*], fun h =>
|
||||
match p, q, h with
|
||||
| (a, b), (c, d), Or.inl h => Lex.left _ _ h
|
||||
| (a, b), (c, d), Or.inr ⟨e, h⟩ => by subst e; exact Lex.right _ h⟩
|
||||
| _, _, Or.inl h => Lex.left _ _ h
|
||||
| (_, _), (_, _), Or.inr ⟨e, h⟩ => by subst e; exact Lex.right _ h⟩
|
||||
|
||||
namespace Lex
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ macro "simp_wf" : tactic =>
|
||||
|
||||
/--
|
||||
This tactic is used internally by lean before presenting the proof obligations from a well-founded
|
||||
definition to the user via `decreasing_by`. It is not necessary to use this tactic manuall.
|
||||
definition to the user via `decreasing_by`. It is not necessary to use this tactic manually.
|
||||
-/
|
||||
macro "clean_wf" : tactic =>
|
||||
`(tactic| simp
|
||||
|
||||
@@ -305,15 +305,16 @@ def registerAttributeImplBuilder (builderId : Name) (builder : AttributeImplBuil
|
||||
if table.contains builderId then throw (IO.userError ("attribute implementation builder '" ++ toString builderId ++ "' has already been declared"))
|
||||
attributeImplBuilderTableRef.modify fun table => table.insert builderId builder
|
||||
|
||||
def mkAttributeImplOfBuilder (builderId ref : Name) (args : List DataValue) : IO AttributeImpl := do
|
||||
let table ← attributeImplBuilderTableRef.get
|
||||
match table[builderId]? with
|
||||
| none => throw (IO.userError ("unknown attribute implementation builder '" ++ toString builderId ++ "'"))
|
||||
| some builder => IO.ofExcept <| builder ref args
|
||||
structure AttributeExtensionOLeanEntry where
|
||||
builderId : Name
|
||||
ref : Name
|
||||
args : List DataValue
|
||||
|
||||
inductive AttributeExtensionOLeanEntry where
|
||||
| decl (declName : Name) -- `declName` has type `AttributeImpl`
|
||||
| builder (builderId ref : Name) (args : List DataValue)
|
||||
def mkAttributeImplOfEntry (e : AttributeExtensionOLeanEntry) : IO AttributeImpl := do
|
||||
let table ← attributeImplBuilderTableRef.get
|
||||
match table[e.builderId]? with
|
||||
| none => throw (IO.userError ("unknown attribute implementation builder '" ++ toString e.builderId ++ "'"))
|
||||
| some builder => IO.ofExcept <| builder e.ref e.args
|
||||
|
||||
structure AttributeExtensionState where
|
||||
newEntries : List AttributeExtensionOLeanEntry := []
|
||||
@@ -337,19 +338,13 @@ unsafe def mkAttributeImplOfConstantUnsafe (env : Environment) (opts : Options)
|
||||
@[implemented_by mkAttributeImplOfConstantUnsafe]
|
||||
opaque mkAttributeImplOfConstant (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl
|
||||
|
||||
def mkAttributeImplOfEntry (env : Environment) (opts : Options) (e : AttributeExtensionOLeanEntry) : IO AttributeImpl :=
|
||||
match e with
|
||||
| .decl declName => IO.ofExcept <| mkAttributeImplOfConstant env opts declName
|
||||
| .builder builderId ref args => mkAttributeImplOfBuilder builderId ref args
|
||||
|
||||
private def AttributeExtension.addImported (es : Array (Array AttributeExtensionOLeanEntry)) : ImportM AttributeExtensionState := do
|
||||
let ctx ← read
|
||||
let map ← attributeMapRef.get
|
||||
let map ← es.foldlM
|
||||
(fun map entries =>
|
||||
entries.foldlM
|
||||
(fun (map : Std.HashMap Name AttributeImpl) entry => do
|
||||
let attrImpl ← mkAttributeImplOfEntry ctx.env ctx.opts entry
|
||||
let attrImpl ← mkAttributeImplOfEntry entry
|
||||
return map.insert attrImpl.name attrImpl)
|
||||
map)
|
||||
map
|
||||
@@ -400,19 +395,13 @@ def getAttributeImpl (env : Environment) (attrName : Name) : Except String Attri
|
||||
| some attr => pure attr
|
||||
| none => throw ("unknown attribute '" ++ toString attrName ++ "'")
|
||||
|
||||
def registerAttributeOfDecl (env : Environment) (opts : Options) (attrDeclName : Name) : Except String Environment := do
|
||||
let attrImpl ← mkAttributeImplOfConstant env opts attrDeclName
|
||||
if isAttribute env attrImpl.name then
|
||||
throw ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used")
|
||||
else
|
||||
return attributeExtension.addEntry env (.decl attrDeclName, attrImpl)
|
||||
|
||||
def registerAttributeOfBuilder (env : Environment) (builderId ref : Name) (args : List DataValue) : IO Environment := do
|
||||
let attrImpl ← mkAttributeImplOfBuilder builderId ref args
|
||||
let entry := {builderId, ref, args}
|
||||
let attrImpl ← mkAttributeImplOfEntry entry
|
||||
if isAttribute env attrImpl.name then
|
||||
throw (IO.userError ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used"))
|
||||
else
|
||||
return attributeExtension.addEntry env (.builder builderId ref args, attrImpl)
|
||||
return attributeExtension.addEntry env (entry, attrImpl)
|
||||
|
||||
def Attribute.add (declName : Name) (attrName : Name) (stx : Syntax) (kind := AttributeKind.global) : AttrM Unit := do
|
||||
let attr ← ofExcept <| getAttributeImpl (← getEnv) attrName
|
||||
|
||||
@@ -103,7 +103,7 @@ private def mkFresh : M VarId := do
|
||||
|
||||
/--
|
||||
Helper function for applying `S`. We only introduce a `reset` if we managed
|
||||
to replace a `ctor` withe `reuse` in `b`.
|
||||
to replace a `ctor` with `reuse` in `b`.
|
||||
-/
|
||||
private def tryS (x : VarId) (c : CtorInfo) (b : FnBody) : M FnBody := do
|
||||
let w ← mkFresh
|
||||
|
||||
@@ -20,18 +20,18 @@ def ensureHasDefault (alts : Array Alt) : Array Alt :=
|
||||
private def getOccsOf (alts : Array Alt) (i : Nat) : Nat := Id.run do
|
||||
let aBody := (alts.get! i).body
|
||||
let mut n := 1
|
||||
for j in [i+1:alts.size] do
|
||||
if alts[j]!.body == aBody then
|
||||
for h : j in [i+1:alts.size] do
|
||||
if alts[j].body == aBody then
|
||||
n := n+1
|
||||
return n
|
||||
|
||||
private def maxOccs (alts : Array Alt) : Alt × Nat := Id.run do
|
||||
let mut maxAlt := alts[0]!
|
||||
let mut max := getOccsOf alts 0
|
||||
for i in [1:alts.size] do
|
||||
for h : i in [1:alts.size] do
|
||||
let curr := getOccsOf alts i
|
||||
if curr > max then
|
||||
maxAlt := alts[i]!
|
||||
maxAlt := alts[i]
|
||||
max := curr
|
||||
return (maxAlt, max)
|
||||
|
||||
|
||||
@@ -110,8 +110,8 @@ def isCtorParam (f : Expr) (i : Nat) : CoreM Bool := do
|
||||
def checkAppArgs (f : Expr) (args : Array Arg) : CheckM Unit := do
|
||||
let mut fType ← inferType f
|
||||
let mut j := 0
|
||||
for i in [:args.size] do
|
||||
let arg := args[i]!
|
||||
for h : i in [:args.size] do
|
||||
let arg := args[i]
|
||||
if fType.isErased then
|
||||
return ()
|
||||
fType := fType.headBeta
|
||||
|
||||
@@ -505,8 +505,8 @@ ones. Return whether any `Value` got updated in the process.
|
||||
-/
|
||||
def inferStep : InterpM Bool := do
|
||||
let ctx ← read
|
||||
for idx in [0:ctx.decls.size] do
|
||||
let decl := ctx.decls[idx]!
|
||||
for h : idx in [0:ctx.decls.size] do
|
||||
let decl := ctx.decls[idx]
|
||||
if !decl.safe then
|
||||
continue
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ structure ExtendState where
|
||||
/--
|
||||
A map from join point `FVarId`s to a respective map from free variables
|
||||
to `Param`s. The free variables in this map are the once that the context
|
||||
of said join point will be extended by by passing in the respective parameter.
|
||||
of said join point will be extended by passing in the respective parameter.
|
||||
-/
|
||||
fvarMap : Std.HashMap FVarId (Std.HashMap FVarId Param) := {}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user