mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
Compare commits
319 Commits
std_comman
...
bitblast
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0faf31008f | ||
|
|
1f9f87d2f5 | ||
|
|
ae4123d9c0 | ||
|
|
fbd9c076c0 | ||
|
|
ae492265fe | ||
|
|
c4a784d6a3 | ||
|
|
def564183c | ||
|
|
46bf4b69b6 | ||
|
|
89ec60befe | ||
|
|
f48079eb90 | ||
|
|
01104cc81e | ||
|
|
37450d47e2 | ||
|
|
e814fc859e | ||
|
|
093e1cf22a | ||
|
|
e6d6855a85 | ||
|
|
bba4ef3728 | ||
|
|
3ad078fec9 | ||
|
|
8689a56a5d | ||
|
|
870c6d0dc4 | ||
|
|
ad901498fa | ||
|
|
acb1b09fbf | ||
|
|
791142a7ff | ||
|
|
015af6d108 | ||
|
|
04385b7fb9 | ||
|
|
2510808ebf | ||
|
|
9f305fb31f | ||
|
|
380dd9e6e7 | ||
|
|
908b98dad8 | ||
|
|
a4d41beab1 | ||
|
|
95f28be088 | ||
|
|
c66c5bb45b | ||
|
|
870de4322c | ||
|
|
4fdc243179 | ||
|
|
8a3c9cafb9 | ||
|
|
826f0580a6 | ||
|
|
0359ff753b | ||
|
|
8b2710c8b3 | ||
|
|
0199228784 | ||
|
|
17e498c11f | ||
|
|
54ff38aa5f | ||
|
|
ecfaf8f3e7 | ||
|
|
3c0e575fe0 | ||
|
|
49f41a6224 | ||
|
|
7a27b04d50 | ||
|
|
f777e0cc85 | ||
|
|
64adb0627a | ||
|
|
ea9a417371 | ||
|
|
70d9106644 | ||
|
|
9cf3fc50c7 | ||
|
|
78726c936f | ||
|
|
7e944c1a30 | ||
|
|
18306db396 | ||
|
|
570b50dddd | ||
|
|
43d6eb144e | ||
|
|
ed02262941 | ||
|
|
c0dfe2e439 | ||
|
|
61fba365f2 | ||
|
|
0362fcea69 | ||
|
|
60d056ffdf | ||
|
|
dc0f026e64 | ||
|
|
67c9498892 | ||
|
|
dc0f771561 | ||
|
|
970b6e59b1 | ||
|
|
b9f9ce874d | ||
|
|
5a33091732 | ||
|
|
b762567174 | ||
|
|
819a32a9eb | ||
|
|
755de48ff3 | ||
|
|
37cd4cc996 | ||
|
|
e53ae5d89e | ||
|
|
69e33efa2f | ||
|
|
973cbb186b | ||
|
|
9afca1c3a9 | ||
|
|
e1acdcd339 | ||
|
|
dc4c2b14d3 | ||
|
|
2312c15ac6 | ||
|
|
fa058ed228 | ||
|
|
17b8880983 | ||
|
|
b9c4a7e51d | ||
|
|
08e149de15 | ||
|
|
37fd128f9f | ||
|
|
a3226d4fe4 | ||
|
|
a23292f049 | ||
|
|
d683643755 | ||
|
|
7cce64ee70 | ||
|
|
86ca8e32c6 | ||
|
|
a179469061 | ||
|
|
aed29525ab | ||
|
|
6e24a08907 | ||
|
|
321ef5b956 | ||
|
|
9c00a59339 | ||
|
|
d7ee5ba1cb | ||
|
|
850bfe521c | ||
|
|
855fbed024 | ||
|
|
2e4557dbd0 | ||
|
|
7d5b6cf097 | ||
|
|
6e408ee402 | ||
|
|
c5fd88f5e1 | ||
|
|
d6df1ec32f | ||
|
|
5e101cf983 | ||
|
|
bb0695b017 | ||
|
|
4a14ea3a5c | ||
|
|
f0b4902f7a | ||
|
|
e73495e5a6 | ||
|
|
17fb8664f8 | ||
|
|
992000a672 | ||
|
|
eb48e6908b | ||
|
|
a929c0176d | ||
|
|
88fbe2e531 | ||
|
|
b9b7f97d42 | ||
|
|
c96f815137 | ||
|
|
bc8511ccbf | ||
|
|
bfb981d465 | ||
|
|
48a9a99a97 | ||
|
|
365243e9a3 | ||
|
|
ade3256625 | ||
|
|
02e4fe0b1c | ||
|
|
5514b8f1fd | ||
|
|
2edde7b376 | ||
|
|
3dd10654e1 | ||
|
|
72d233d181 | ||
|
|
9e5e0e23b2 | ||
|
|
33bc46d1a7 | ||
|
|
056cb75ee0 | ||
|
|
66be8b9d4c | ||
|
|
6d569aa7b5 | ||
|
|
335fef4396 | ||
|
|
a3596d953d | ||
|
|
5b15e1a9f3 | ||
|
|
d179d6c8d7 | ||
|
|
3ead33bd13 | ||
|
|
51fe66b9eb | ||
|
|
c48d020255 | ||
|
|
f7e74320df | ||
|
|
72f90bff9d | ||
|
|
2defc58159 | ||
|
|
338aa5aa7c | ||
|
|
4d4b79757d | ||
|
|
710c3ae9e8 | ||
|
|
87e7c666e2 | ||
|
|
60f30a46cf | ||
|
|
6c828ee9eb | ||
|
|
4d94147643 | ||
|
|
9dfb93bbe9 | ||
|
|
8bf9d398af | ||
|
|
5a32473f66 | ||
|
|
b9b4d8f41d | ||
|
|
4e87d7f173 | ||
|
|
e17e0d36a7 | ||
|
|
8bf6475e10 | ||
|
|
7f7d9bdaaf | ||
|
|
0824442a6f | ||
|
|
815200eaad | ||
|
|
8193af33e3 | ||
|
|
53146db620 | ||
|
|
23d3ac4760 | ||
|
|
5bbc54429f | ||
|
|
b27ab5e25d | ||
|
|
47595540bb | ||
|
|
aa0f43e9a1 | ||
|
|
997ae402da | ||
|
|
61c22c88d7 | ||
|
|
629b7d0fdd | ||
|
|
46df6142a6 | ||
|
|
2b1a0371c6 | ||
|
|
b6ed97bb3d | ||
|
|
9a970611ca | ||
|
|
6e821de11a | ||
|
|
db3c1d4e7e | ||
|
|
4d0c0e2328 | ||
|
|
e5d2cbceaa | ||
|
|
ddd6342737 | ||
|
|
75272cb157 | ||
|
|
d55bab41bb | ||
|
|
71cfbb26de | ||
|
|
74e7886ce7 | ||
|
|
0fb936158b | ||
|
|
cc8adfb2a5 | ||
|
|
a0089d4667 | ||
|
|
29b589a867 | ||
|
|
f76bb2495b | ||
|
|
89490f648a | ||
|
|
6719af350f | ||
|
|
3d8f73380e | ||
|
|
959ad98861 | ||
|
|
29244f32f6 | ||
|
|
09cfcefb25 | ||
|
|
c9aea32d3e | ||
|
|
07f490513c | ||
|
|
928f3e434e | ||
|
|
855a762bcb | ||
|
|
e1c176543a | ||
|
|
15be8fc2a6 | ||
|
|
28a02a8688 | ||
|
|
a898aa18f3 | ||
|
|
263629d140 | ||
|
|
b598c0fea9 | ||
|
|
d0fb48b4e4 | ||
|
|
79a9f6759a | ||
|
|
f1a3169424 | ||
|
|
4a7c1ea439 | ||
|
|
15cbcae7b2 | ||
|
|
ea665de453 | ||
|
|
8b8e001794 | ||
|
|
35e374350c | ||
|
|
9e27e92eea | ||
|
|
489f2da711 | ||
|
|
75d7bc0ef1 | ||
|
|
5d9552d66c | ||
|
|
067913bc36 | ||
|
|
c23a35c472 | ||
|
|
f64d14ea54 | ||
|
|
90b5a0011d | ||
|
|
ca941249b9 | ||
|
|
94a9ab45ff | ||
|
|
e2b3b34d14 | ||
|
|
204b408df7 | ||
|
|
7545b85512 | ||
|
|
1d66c32d5f | ||
|
|
7f08975176 | ||
|
|
0e0ed9ccaf | ||
|
|
59bf220934 | ||
|
|
032a2ecaa1 | ||
|
|
da24708ba5 | ||
|
|
16757bb256 | ||
|
|
3f548edcd7 | ||
|
|
8758c0adf5 | ||
|
|
b41499cec1 | ||
|
|
88deb34ddb | ||
|
|
5e5bdfba1a | ||
|
|
23e49eb519 | ||
|
|
5ce20ba160 | ||
|
|
aa42fc07d3 | ||
|
|
bc74e6eb38 | ||
|
|
52f1fcc498 | ||
|
|
a6cdc333d5 | ||
|
|
58ed6b9630 | ||
|
|
cd9648a61e | ||
|
|
55ce5d570c | ||
|
|
ead14987bc | ||
|
|
834b515592 | ||
|
|
9fe72c5f95 | ||
|
|
77de817960 | ||
|
|
457d33d660 | ||
|
|
b882ebcf4a | ||
|
|
602b1a0d15 | ||
|
|
17c7cb0e1c | ||
|
|
e206e53f4e | ||
|
|
d569ed4e5f | ||
|
|
433c4d22c2 | ||
|
|
6383af0595 | ||
|
|
c8236ccd47 | ||
|
|
559a18874c | ||
|
|
3dcc8cab3e | ||
|
|
fb18ef3688 | ||
|
|
3e5695e07e | ||
|
|
61a76a814f | ||
|
|
0c92d17792 | ||
|
|
d536534c4d | ||
|
|
97e7e668d6 | ||
|
|
dda88c9926 | ||
|
|
ef9a6bb839 | ||
|
|
baa9fe5932 | ||
|
|
368326fb48 | ||
|
|
678797b67b | ||
|
|
496a8d578e | ||
|
|
3fb7262fe0 | ||
|
|
8f010a6115 | ||
|
|
089cd50d00 | ||
|
|
18afefda96 | ||
|
|
06e21faecd | ||
|
|
c9f27c36a0 | ||
|
|
c9cba33f57 | ||
|
|
84bd563cff | ||
|
|
73524e37ae | ||
|
|
229f16f421 | ||
|
|
eaf44d74ae | ||
|
|
6fc3ea7790 | ||
|
|
a4e27d3090 | ||
|
|
1d9074c524 | ||
|
|
e29d75a961 | ||
|
|
8aab74e65d | ||
|
|
4e58b428e9 | ||
|
|
271ae5b8e5 | ||
|
|
a14bbbffb2 | ||
|
|
5a95f91fae | ||
|
|
11727a415b | ||
|
|
90a516de09 | ||
|
|
ae524d465f | ||
|
|
9a3f0f1909 | ||
|
|
fae5b2e87c | ||
|
|
2bd187044f | ||
|
|
144c1bbbaf | ||
|
|
98085661c7 | ||
|
|
9cea1a503e | ||
|
|
25147accc8 | ||
|
|
6048ba9832 | ||
|
|
33bb87cd1d | ||
|
|
4aa62a6a9c | ||
|
|
eebdfdf87a | ||
|
|
01c9f4c783 | ||
|
|
a706c3b89a | ||
|
|
329e00661a | ||
|
|
8b0dd2e835 | ||
|
|
88a5d27d65 | ||
|
|
232b2b6300 | ||
|
|
fdc64def1b | ||
|
|
644d4263f1 | ||
|
|
56d703db8e | ||
|
|
50d661610d | ||
|
|
0554ab39aa | ||
|
|
3a6ebd88bb | ||
|
|
06f73d621b | ||
|
|
c27474341e | ||
|
|
27b962f14d | ||
|
|
2032ffa3fc | ||
|
|
c424d99cc9 | ||
|
|
fbedb79b46 | ||
|
|
1965a022eb |
26
.github/workflows/check-prelude.yml
vendored
Normal file
26
.github/workflows/check-prelude.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Check for modules that should use `prelude`
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
check-prelude:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# the default is to use a virtual merge commit between the PR and master: just use the PR
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
sparse-checkout: src/Lean
|
||||
- name: Check Prelude
|
||||
run: |
|
||||
failed_files=""
|
||||
while IFS= read -r -d '' file; do
|
||||
if ! grep -q "^prelude$" "$file"; then
|
||||
failed_files="$failed_files$file\n"
|
||||
fi
|
||||
done < <(find src/Lean -name '*.lean' -print0)
|
||||
if [ -n "$failed_files" ]; then
|
||||
echo -e "The following files should use 'prelude':\n$failed_files"
|
||||
exit 1
|
||||
fi
|
||||
31
.github/workflows/ci.yml
vendored
31
.github/workflows/ci.yml
vendored
@@ -410,7 +410,8 @@ jobs:
|
||||
run: |
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
make update-stage0 && make -j4
|
||||
# clean rebuild in case of Makefile changes
|
||||
make update-stage0 && rm -rf ./stage* && make -j4
|
||||
if: matrix.name == 'Linux' && needs.configure.outputs.quick == 'false'
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
@@ -421,19 +422,21 @@ jobs:
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
echo bt | $GDB/bin/gdb -q $progbin $c || true
|
||||
done
|
||||
- name: Upload coredumps
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: coredumps-${{ matrix.name }}
|
||||
path: |
|
||||
./coredumps
|
||||
./build/stage0/bin/lean
|
||||
./build/stage0/lib/lean/libleanshared.so
|
||||
./build/stage1/bin/lean
|
||||
./build/stage1/lib/lean/libleanshared.so
|
||||
./build/stage2/bin/lean
|
||||
./build/stage2/lib/lean/libleanshared.so
|
||||
# has not been used in a long while, would need to be adapted to new
|
||||
# shared libs
|
||||
#- name: Upload coredumps
|
||||
# uses: actions/upload-artifact@v3
|
||||
# if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
# with:
|
||||
# name: coredumps-${{ matrix.name }}
|
||||
# path: |
|
||||
# ./coredumps
|
||||
# ./build/stage0/bin/lean
|
||||
# ./build/stage0/lib/lean/libleanshared.so
|
||||
# ./build/stage1/bin/lean
|
||||
# ./build/stage1/lib/lean/libleanshared.so
|
||||
# ./build/stage2/bin/lean
|
||||
# ./build/stage2/lib/lean/libleanshared.so
|
||||
|
||||
# This job collects results from all the matrix jobs
|
||||
# This can be made the “required” job, instead of listing each
|
||||
|
||||
20
.github/workflows/copyright-header.yml
vendored
Normal file
20
.github/workflows/copyright-header.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Check for copyright header
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
check-lean-files:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Verify .lean files start with a copyright header.
|
||||
run: |
|
||||
FILES=$(find . -type d \( -path "./tests" -o -path "./doc" -o -path "./src/lake/examples" -o -path "./src/lake/tests" -o -path "./build" -o -path "./nix" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
|
||||
if [ -n "$FILES" ]; then
|
||||
echo "Found .lean files which do not have a copyright header:"
|
||||
echo "$FILES"
|
||||
exit 1
|
||||
else
|
||||
echo "All copyright headers present."
|
||||
fi
|
||||
10
.github/workflows/nix-ci.yml
vendored
10
.github/workflows/nix-ci.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
@@ -71,12 +72,6 @@ jobs:
|
||||
run: |
|
||||
sudo chown -R root:nixbld /nix/var/cache
|
||||
sudo chmod -R 770 /nix/var/cache
|
||||
- name: Install Cachix
|
||||
uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: lean4
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
skipPush: true # we push specific outputs only
|
||||
- name: Build
|
||||
run: |
|
||||
nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build
|
||||
@@ -98,9 +93,6 @@ jobs:
|
||||
# gmplib.org consistently times out from GH actions
|
||||
# the GitHub token is to avoid rate limiting
|
||||
args: --base './dist' --no-progress --github-token ${{ secrets.GITHUB_TOKEN }} --exclude 'gmplib.org' './dist/**/*.html'
|
||||
- name: Push to Cachix
|
||||
run: |
|
||||
[ -z "${{ secrets.CACHIX_AUTH_TOKEN }}" ] || cachix push -j4 lean4 ./push-* || true
|
||||
- name: Rebuild Nix Store Cache
|
||||
run: |
|
||||
rm -rf nix-store-cache || true
|
||||
|
||||
2
.github/workflows/pr-release.yml
vendored
2
.github/workflows/pr-release.yml
vendored
@@ -151,7 +151,7 @@ jobs:
|
||||
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch."
|
||||
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_SHA\`."
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
|
||||
24
.github/workflows/update-stage0.yml
vendored
24
.github/workflows/update-stage0.yml
vendored
@@ -40,18 +40,32 @@ jobs:
|
||||
run: |
|
||||
git config --global user.name "Lean stage0 autoupdater"
|
||||
git config --global user.email "<>"
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
# Would be nice, but does not work yet:
|
||||
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
|
||||
# This action does not run that often and building runs in a few minutes, so ok for now
|
||||
#- if: env.should_update_stage0 == 'yes'
|
||||
# uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Install Cachix
|
||||
uses: cachix/cachix-action@v12
|
||||
name: Restore Build Cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
name: lean4
|
||||
path: nix-store-cache
|
||||
key: Nix Linux-nix-store-cache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
Nix Linux-nix-store-cache
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Further Set Up Nix Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
# Nix seems to mutate the cache, so make a copy
|
||||
cp -r nix-store-cache nix-store-cache-copy || true
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
extra-conf: |
|
||||
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: nix run .#update-stage0-commit
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
/.github/ @Kha @semorrison
|
||||
/RELEASES.md @semorrison
|
||||
/src/ @leodemoura @Kha
|
||||
/src/Init/IO.lean @joehendrix
|
||||
/src/kernel/ @leodemoura
|
||||
/src/lake/ @tydeu
|
||||
@@ -17,6 +16,7 @@
|
||||
/src/Lean/Meta/Tactic/ @leodemoura
|
||||
/src/Lean/Parser/ @Kha
|
||||
/src/Lean/PrettyPrinter/ @Kha
|
||||
/src/Lean/PrettyPrinter/Delaborator/ @kmill
|
||||
/src/Lean/Server/ @mhuisi
|
||||
/src/Lean/Widget/ @Vtec234
|
||||
/src/runtime/io.cpp @joehendrix
|
||||
|
||||
312
RELEASES.md
312
RELEASES.md
@@ -8,74 +8,266 @@ This file contains work-in-progress notes for the upcoming release, as well as p
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
|
||||
of each version.
|
||||
|
||||
v4.7.0 (development in progress)
|
||||
v4.8.0 (development in progress)
|
||||
---------
|
||||
|
||||
v4.7.0
|
||||
---------
|
||||
|
||||
* `simp` and `rw` now use instance arguments found by unification,
|
||||
rather than always resynthesizing. For backwards compatibility, the original behaviour is
|
||||
available via `set_option tactic.skipAssignedInstances false`.
|
||||
[#3507](https://github.com/leanprover/lean4/pull/3507) and
|
||||
[#3509](https://github.com/leanprover/lean4/pull/3509).
|
||||
|
||||
* When the `pp.proofs` is false, now omitted proofs use `⋯` rather than `_`,
|
||||
which gives a more helpful error message when copied from the Infoview.
|
||||
The `pp.proofs.threshold` option lets small proofs always be pretty printed.
|
||||
[#3241](https://github.com/leanprover/lean4/pull/3241).
|
||||
|
||||
* `pp.proofs.withType` is now set to false by default to reduce noise in the info view.
|
||||
|
||||
* The pretty printer for applications now handles the case of over-application itself when applying app unexpanders.
|
||||
In particular, the ``| `($_ $a $b $xs*) => `(($a + $b) $xs*)`` case of an `app_unexpander` is no longer necessary.
|
||||
[#3495](https://github.com/leanprover/lean4/pull/3495).
|
||||
|
||||
* New `simp` (and `dsimp`) configuration option: `zetaDelta`. It is `false` by default.
|
||||
The `zeta` option is still `true` by default, but their meaning has changed.
|
||||
- When `zeta := true`, `simp` and `dsimp` reduce terms of the form
|
||||
`let x := val; e[x]` into `e[val]`.
|
||||
- When `zetaDelta := true`, `simp` and `dsimp` will expand let-variables in
|
||||
the context. For example, suppose the context contains `x := val`. Then,
|
||||
any occurrence of `x` is replaced with `val`.
|
||||
|
||||
See [issue #2682](https://github.com/leanprover/lean4/pull/2682) for additional details. Here are some examples:
|
||||
```
|
||||
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
||||
intro x
|
||||
simp
|
||||
/-
|
||||
New goal:
|
||||
h : z = 9; x := 5 |- x + 4 = z
|
||||
-/
|
||||
rw [h]
|
||||
|
||||
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
||||
intro x
|
||||
-- Using both `zeta` and `zetaDelta`.
|
||||
simp (config := { zetaDelta := true })
|
||||
/-
|
||||
New goal:
|
||||
h : z = 9; x := 5 |- 9 = z
|
||||
-/
|
||||
rw [h]
|
||||
|
||||
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
||||
intro x
|
||||
simp [x] -- asks `simp` to unfold `x`
|
||||
/-
|
||||
New goal:
|
||||
h : z = 9; x := 5 |- 9 = z
|
||||
-/
|
||||
rw [h]
|
||||
|
||||
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
||||
intro x
|
||||
simp (config := { zetaDelta := true, zeta := false })
|
||||
/-
|
||||
New goal:
|
||||
h : z = 9; x := 5 |- let y := 4; 5 + y = z
|
||||
-/
|
||||
rw [h]
|
||||
```
|
||||
|
||||
* When adding new local theorems to `simp`, the system assumes that the function application arguments
|
||||
have been annotated with `no_index`. This modification, which addresses [issue #2670](https://github.com/leanprover/lean4/issues/2670),
|
||||
restores the Lean 3 behavior that users expect. With this modification, the following examples are now operational:
|
||||
```
|
||||
example {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
|
||||
(a : α) (b : β) : f (a, b) b = b := by
|
||||
simp [h]
|
||||
|
||||
example {α β : Type} {f : α × β → β → β}
|
||||
(a : α) (b : β) (h : f (a,b) (a,b).2 = (a,b).2) : f (a, b) b = b := by
|
||||
simp [h]
|
||||
```
|
||||
In both cases, `h` is applicable because `simp` does not index f-arguments anymore when adding `h` to the `simp`-set.
|
||||
It's important to note, however, that global theorems continue to be indexed in the usual manner.
|
||||
|
||||
* Improved the error messages produced by the `decide` tactic. [#3422](https://github.com/leanprover/lean4/pull/3422)
|
||||
|
||||
* Improved auto-completion performance. [#3460](https://github.com/leanprover/lean4/pull/3460)
|
||||
|
||||
* Improved initial language server startup performance. [#3552](https://github.com/leanprover/lean4/pull/3552)
|
||||
|
||||
* Changed call hierarchy to sort entries and strip private header from names displayed in the call hierarchy. [#3482](https://github.com/leanprover/lean4/pull/3482)
|
||||
|
||||
* There is now a low-level error recovery combinator in the parsing framework, primarily intended for DSLs. [#3413](https://github.com/leanprover/lean4/pull/3413)
|
||||
|
||||
* You can now write `termination_by?` after a declaration to see the automatically inferred
|
||||
termination argument, and turn it into a `termination_by …` clause using the “Try this” widget or a code action. [#3514](https://github.com/leanprover/lean4/pull/3514)
|
||||
|
||||
* A large fraction of `Std` has been moved into the Lean repository.
|
||||
This was motivated by:
|
||||
1. Making universally useful tactics such as `ext`, `by_cases`, `change at`,
|
||||
`norm_cast`, `rcases`, `simpa`, `simp?`, `omega`, and `exact?`
|
||||
available to all users of Lean, without imports.
|
||||
2. Minimizing the syntactic changes between plain Lean and Lean with `import Std`.
|
||||
3. Simplifying the development process for the basic data types
|
||||
`Nat`, `Int`, `Fin` (and variants such as `UInt64`), `List`, `Array`,
|
||||
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`)
|
||||
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.
|
||||
|
||||
* The `/` and `%` notations in `Int` now use `Int.ediv` and `Int.emod`
|
||||
(i.e. the rounding conventions have changed).
|
||||
Previously `Std` overrode these notations, so this is no change for users of `Std`.
|
||||
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.
|
||||
* 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).
|
||||
* `omega` now has support for `Fin` [#3427](https://github.com/leanprover/lean4/pull/3427),
|
||||
the `<<<` operator [#3433](https://github.com/leanprover/lean4/pull/3433).
|
||||
* During the port `omega` was modified to no longer identify atoms up to definitional equality
|
||||
(so in particular it can no longer prove `id x ≤ x`). [#3525](https://github.com/leanprover/lean4/pull/3525).
|
||||
This may cause some regressions.
|
||||
We plan to provide a general purpose preprocessing tactic later, or an `omega!` mode.
|
||||
* `omega` is now invoked in Lean's automation for termination proofs
|
||||
[#3503](https://github.com/leanprover/lean4/pull/3503) as well as in
|
||||
array indexing proofs [#3515](https://github.com/leanprover/lean4/pull/3515).
|
||||
This automation will be substantially revised in the medium term,
|
||||
and while `omega` does help automate some proofs, we plan to make this much more robust.
|
||||
|
||||
* The library search tactics `exact?` and `apply?` that were originally in
|
||||
Mathlib are now available in Lean itself. These use the implementation using
|
||||
lazy discrimination trees from `Std`, and thus do not require a disk cache but
|
||||
have a slightly longer startup time. The order used for selection lemmas has
|
||||
changed as well to favor goals purely based on how many terms in the head
|
||||
pattern match the current goal.
|
||||
|
||||
* The `solve_by_elim` tactic has been ported from `Std` to Lean so that library
|
||||
search can use it.
|
||||
|
||||
* New `#check_tactic` and `#check_simp` commands have been added. These are
|
||||
useful for checking tactics (particularly `simp`) behave as expected in test
|
||||
suites.
|
||||
|
||||
* Previously, app unexpanders would only be applied to entire applications. However, some notations produce
|
||||
functions, and these functions can be given additional arguments. The solution so far has been to write app unexpanders so that they can take an arbitrary number of additional arguments. However this leads to misleading hover information in the Infoview. For example, while `HAdd.hAdd f g 1` pretty prints as `(f + g) 1`, hovering over `f + g` shows `f`. There is no way to fix the situation from within an app unexpander; the expression position for `HAdd.hAdd f g` is absent, and app unexpanders cannot register TermInfo.
|
||||
|
||||
This commit changes the app delaborator to try running app unexpanders on every prefix of an application, from longest to shortest prefix. For efficiency, it is careful to only try this when app delaborators do in fact exist for the head constant, and it also ensures arguments are only delaborated once. Then, in `(f + g) 1`, the `f + g` gets TermInfo registered for that subexpression, making it properly hoverable.
|
||||
|
||||
[#3375](https://github.com/leanprover/lean4/pull/3375)
|
||||
|
||||
Breaking changes:
|
||||
* `Lean.withTraceNode` and variants got a stronger `MonadAlwaysExcept` assumption to
|
||||
fix trace trees not being built on elaboration runtime exceptions. Instances for most elaboration
|
||||
monads built on `EIO Exception` should be synthesized automatically.
|
||||
* The `match ... with.` and `fun.` notations previously in Std have been replaced by
|
||||
`nomatch ...` and `nofun`. [#3279](https://github.com/leanprover/lean4/pull/3279) and [#3286](https://github.com/leanprover/lean4/pull/3286)
|
||||
|
||||
|
||||
Other improvements:
|
||||
* several bug fixes for `simp`:
|
||||
* we should not crash when `simp` loops [#3269](https://github.com/leanprover/lean4/pull/3269)
|
||||
* `simp` gets stuck on `autoParam` [#3315](https://github.com/leanprover/lean4/pull/3315)
|
||||
* `simp` fails when custom discharger makes no progress [#3317](https://github.com/leanprover/lean4/pull/3317)
|
||||
* `simp` fails to discharge `autoParam` premises even when it can reduce them to `True` [#3314](https://github.com/leanprover/lean4/pull/3314)
|
||||
* `simp?` suggests generated equations lemma names, fixes [#3547](https://github.com/leanprover/lean4/pull/3547) [#3573](https://github.com/leanprover/lean4/pull/3573)
|
||||
* fixes for `match` expressions:
|
||||
* fix regression with builtin literals [#3521](https://github.com/leanprover/lean4/pull/3521)
|
||||
* accept `match` when patterns cover all cases of a `BitVec` finite type [#3538](https://github.com/leanprover/lean4/pull/3538)
|
||||
* fix matching `Int` literals [#3504](https://github.com/leanprover/lean4/pull/3504)
|
||||
* patterns containing int values and constructors [#3496](https://github.com/leanprover/lean4/pull/3496)
|
||||
* improve `termination_by` error messages [#3255](https://github.com/leanprover/lean4/pull/3255)
|
||||
* fix `rename_i` in macros, fixes [#3553](https://github.com/leanprover/lean4/pull/3553) [#3581](https://github.com/leanprover/lean4/pull/3581)
|
||||
* fix excessive resource usage in `generalize`, fixes [#3524](https://github.com/leanprover/lean4/pull/3524) [#3575](https://github.com/leanprover/lean4/pull/3575)
|
||||
* an equation lemma with autoParam arguments fails to rewrite, fixing [#2243](https://github.com/leanprover/lean4/pull/2243) [#3316](https://github.com/leanprover/lean4/pull/3316)
|
||||
* `add_decl_doc` should check that declarations are local [#3311](https://github.com/leanprover/lean4/pull/3311)
|
||||
* instantiate the types of inductives with the right parameters, closing [#3242](https://github.com/leanprover/lean4/pull/3242) [#3246](https://github.com/leanprover/lean4/pull/3246)
|
||||
* New simprocs for many basic types. [#3407](https://github.com/leanprover/lean4/pull/3407)
|
||||
|
||||
Lake fixes:
|
||||
* Warn on fetch cloud release failure [#3401](https://github.com/leanprover/lean4/pull/3401)
|
||||
* Cloud release trace & `lake build :release` errors [#3248](https://github.com/leanprover/lean4/pull/3248)
|
||||
|
||||
v4.6.1
|
||||
---------
|
||||
* Backport of [#3552](https://github.com/leanprover/lean4/pull/3552) fixing a performance regression
|
||||
in server startup.
|
||||
|
||||
v4.6.0
|
||||
---------
|
||||
|
||||
* Add custom simplification procedures (aka `simproc`s) to `simp`. Simprocs can be triggered by the simplifier on a specified term-pattern. Here is an small example:
|
||||
```lean
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
|
||||
```lean
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
|
||||
|
||||
def foo (x : Nat) : Nat :=
|
||||
x + 10
|
||||
def foo (x : Nat) : Nat :=
|
||||
x + 10
|
||||
|
||||
/--
|
||||
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
|
||||
-/
|
||||
simproc reduceFoo (foo _) :=
|
||||
/- A term of type `Expr → SimpM Step -/
|
||||
fun e => do
|
||||
/--
|
||||
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
|
||||
-/
|
||||
simproc reduceFoo (foo _) :=
|
||||
/- A term of type `Expr → SimpM Step -/
|
||||
fun e => do
|
||||
/-
|
||||
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
|
||||
* The constructor `.done` instructs `simp` that the result does
|
||||
not need to be simplied 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`.
|
||||
`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`.
|
||||
-/
|
||||
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
|
||||
unless e.isAppOfArity ``foo 1 do
|
||||
return .continue
|
||||
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
|
||||
let some n ← Nat.fromExpr? e.appArg!
|
||||
| return .continue
|
||||
return .done { expr := Lean.mkNatLit (n+10) }
|
||||
```
|
||||
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
|
||||
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
|
||||
```lean
|
||||
example : x + foo 2 = 12 + x := by
|
||||
set_option simprocs false in
|
||||
/- This `simp` command does not make progress since `simproc`s are disabled. -/
|
||||
fail_if_success simp
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- `simp only` must not use the default simproc set. -/
|
||||
fail_if_success simp only
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/-
|
||||
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
|
||||
* The constructor `.done` instructs `simp` that the result does
|
||||
not need to be simplied further.
|
||||
* The constructor `.visit` instructs `simp` to visit the resulting expression.
|
||||
* The constructor `.continue` instructs `simp` to try other simplification procedures.
|
||||
`simp only` does not use the default simproc set,
|
||||
but we can provide simprocs as arguments. -/
|
||||
simp only [reduceFoo]
|
||||
simp_arith
|
||||
|
||||
All three constructors take a `Result`. The `.continue` contructor 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`.
|
||||
-/
|
||||
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
|
||||
unless e.isAppOfArity ``foo 1 do
|
||||
return .continue
|
||||
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
|
||||
let some n ← Nat.fromExpr? e.appArg!
|
||||
| return .continue
|
||||
return .done { expr := Lean.mkNatLit (n+10) }
|
||||
```
|
||||
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
|
||||
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
|
||||
```lean
|
||||
example : x + foo 2 = 12 + x := by
|
||||
set_option simprocs false in
|
||||
/- This `simp` command does not make progress since `simproc`s are disabled. -/
|
||||
fail_if_success simp
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- `simp only` must not use the default simproc set. -/
|
||||
fail_if_success simp only
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/-
|
||||
`simp only` does not use the default simproc set,
|
||||
but we can provide simprocs as arguments. -/
|
||||
simp only [reduceFoo]
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- We can use `-` to disable `simproc`s. -/
|
||||
fail_if_success simp [-reduceFoo]
|
||||
simp_arith
|
||||
```
|
||||
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
|
||||
```lean
|
||||
simproc [my_simp] reduceFoo (foo _) := ...
|
||||
```
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- We can use `-` to disable `simproc`s. -/
|
||||
fail_if_success simp [-reduceFoo]
|
||||
simp_arith
|
||||
```
|
||||
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
|
||||
```lean
|
||||
simproc [my_simp] reduceFoo (foo _) := ...
|
||||
```
|
||||
|
||||
* The syntax of the `termination_by` and `decreasing_by` termination hints is overhauled:
|
||||
|
||||
@@ -214,7 +406,7 @@ simproc [my_simp] reduceFoo (foo _) := ...
|
||||
and hence greatly reduces the reliance on costly structure eta reduction. This has a large impact on mathlib,
|
||||
reducing total CPU instructions by 3% and enabling impactful refactors like leanprover-community/mathlib4#8386
|
||||
which reduces the build time by almost 20%.
|
||||
See PR [#2478](https://github.com/leanprover/lean4/pull/2478) and RFC [#2451](https://github.com/leanprover/lean4/issues/2451).
|
||||
See [PR #2478](https://github.com/leanprover/lean4/pull/2478) and [RFC #2451](https://github.com/leanprover/lean4/issues/2451).
|
||||
|
||||
* Add pretty printer settings to omit deeply nested terms (`pp.deepTerms false` and `pp.deepTerms.threshold`) ([PR #3201](https://github.com/leanprover/lean4/pull/3201))
|
||||
|
||||
@@ -233,7 +425,7 @@ Other improvements:
|
||||
* produce simpler proof terms in `rw` [#3121](https://github.com/leanprover/lean4/pull/3121)
|
||||
* fuse nested `mkCongrArg` calls in proofs generated by `simp` [#3203](https://github.com/leanprover/lean4/pull/3203)
|
||||
* `induction using` followed by a general term [#3188](https://github.com/leanprover/lean4/pull/3188)
|
||||
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060, fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
|
||||
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060), fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
|
||||
* reducing out-of-bounds `swap!` should return `a`, not `default`` [#3197](https://github.com/leanprover/lean4/pull/3197), fixing [#3196](https://github.com/leanprover/lean4/issues/3196)
|
||||
* derive `BEq` on structure with `Prop`-fields [#3191](https://github.com/leanprover/lean4/pull/3191), fixing [#3140](https://github.com/leanprover/lean4/issues/3140)
|
||||
* refine through more `casesOnApp`/`matcherApp` [#3176](https://github.com/leanprover/lean4/pull/3176), fixing [#3175](https://github.com/leanprover/lean4/pull/3175)
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
- [Testing](./dev/testing.md)
|
||||
- [Debugging](./dev/debugging.md)
|
||||
- [Commit Convention](./dev/commit_convention.md)
|
||||
- [Release checklist](./dev/release_checklist.md)
|
||||
- [Building This Manual](./dev/mdbook.md)
|
||||
- [Foreign Function Interface](./dev/ffi.md)
|
||||
|
||||
@@ -74,3 +74,9 @@ Lean's build process uses [`ccache`](https://ccache.dev/) if it is
|
||||
installed to speed up recompilation of the generated C code. Without
|
||||
`ccache`, you'll likely spend more time than necessary waiting on
|
||||
rebuilds - it's a good idea to make sure it's installed.
|
||||
|
||||
### `prelude`
|
||||
Unlike most Lean projects, all submodules of the `Lean` module begin with the
|
||||
`prelude` keyword. This disables the automated import of `Init`, meaning that
|
||||
developers need to figure out their own subset of `Init` to import. This is done
|
||||
such that changing files in `Init` doesn't force a full rebuild of `Lean`.
|
||||
|
||||
201
doc/dev/release_checklist.md
Normal file
201
doc/dev/release_checklist.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Releasing a stable version
|
||||
|
||||
This checklist walks you through releasing a stable version.
|
||||
See below for the checklist for release candidates.
|
||||
|
||||
We'll use `v4.6.0` as the intended release version as a running example.
|
||||
|
||||
- One week before the planned release, ensure that someone has written the first draft of the release blog post
|
||||
- `git checkout releases/v4.6.0`
|
||||
(This branch should already exist, from the release candidates.)
|
||||
- `git pull`
|
||||
- In `src/CMakeLists.txt`, verify you see
|
||||
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)`
|
||||
- (both of these should already be in place from the release candidates)
|
||||
- It is possible that the `v4.6.0` section of `RELEASES.md` is out of sync between
|
||||
`releases/v4.6.0` and `master`. This should be reconciled:
|
||||
- Run `git diff master RELEASES.md`.
|
||||
- You should expect to see additons on `master` in the `v4.7.0-rc1` section; ignore these.
|
||||
(i.e. the new release notes for the upcoming release candidate).
|
||||
- Reconcile discrepancies in the `v4.6.0` section,
|
||||
usually via copy and paste and a commit to `releases/v4.6.0`.
|
||||
- `git tag v4.6.0`
|
||||
- `git push origin v4.6.0`
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`,
|
||||
looking for the `v4.6.0` tag.
|
||||
- This step can take up to an hour.
|
||||
- If you are intending to cut the next release candidate on the same day,
|
||||
you may want to start on the release candidate checklist now.
|
||||
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
|
||||
- Edit the release notes on Github to select the "Set as the latest release".
|
||||
- Copy and paste the Github release notes from the previous releases candidate for this version
|
||||
(e.g. `v4.6.0-rc1`), and quickly sanity check.
|
||||
- Next, we will move a curated list of downstream repos to the latest stable release.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`.
|
||||
The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
Since the `v4.6.0` release should be functionally identical to the last release candidate,
|
||||
which the repository should already be on, this PR is a no-op besides changing the toolchain.
|
||||
- Once this is merged, create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the stable branch.
|
||||
- We do this for the repositories:
|
||||
- [lean4checker](https://github.com/leanprover/lean4checker)
|
||||
- `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
|
||||
- [Std](https://github.com/leanprover-community/repl)
|
||||
- [ProofWidgets4](https://github.com/leanprover-community/ProofWidgets4)
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- [Aesop](https://github.com/leanprover-community/aesop)
|
||||
- [Mathlib](https://github.com/leanprover-community/mathlib4)
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
|
||||
`git checkout toolchain/v4.6.0` to the appropriate tag,
|
||||
and then run `.github/workflows/mk_build_yml.sh`.
|
||||
- [REPL](https://github.com/leanprover-community/repl)
|
||||
- Note that there are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`.
|
||||
- Note that there are dependencies between these packages:
|
||||
you should update the lakefile so that you are using the `v4.6.0` tag of upstream repositories
|
||||
(or the sequential tag for `ProofWidgets4`), and run `lake update` before committing.
|
||||
- This means that this process is sequential; each repository must have its bump PR merged,
|
||||
and the new tag pushed, before you can make the PR for the downstream repositories.
|
||||
- `lean4checker` has no dependencies
|
||||
- `Std` has no dependencies
|
||||
- `Aesop` depends on `Std`
|
||||
- `ProofWidgets4` depends on `Std`
|
||||
- `Mathlib` depends on `Aesop`, `ProofWidgets4`, and `lean4checker` (and transitively on `Std`)
|
||||
- `REPL` depends on `Mathlib` (this dependency is only for testing).
|
||||
- Merge the release announcement PR for the Lean website - it will be deployed automatically
|
||||
- Finally, make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Link to the blog post from the Zulip announcement.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
|
||||
## Optimistic(?) time estimates:
|
||||
- Initial checks and push the tag: 30 minutes.
|
||||
- Note that if `RELEASES.md` has discrepancies this could take longer!
|
||||
- Waiting for the release: 60 minutes.
|
||||
- Fixing release notes: 10 minutes.
|
||||
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
|
||||
- Waiting for Mathlib CI and bors: 120 minutes.
|
||||
- Finalizing Mathlib tags and stable branch, and updating REPL: 15 minutes.
|
||||
- Posting announcement and/or blog post: 20 minutes.
|
||||
|
||||
# Creating a release candidate.
|
||||
|
||||
This checklist walks you through creating the first release candidate for a version of Lean.
|
||||
|
||||
We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
|
||||
- Decide which nightly release you want to turn into a release candidate.
|
||||
We will use `nightly-2024-02-29` in this example.
|
||||
- It is essential that Std and Mathlib already have reviewed branches compatible with this nightly.
|
||||
- Check that both Std and Mathlib's `bump/v4.7.0` branch contain `nightly-2024-02-29`
|
||||
in their `lean-toolchain`.
|
||||
- The steps required to reach that state are beyond the scope of this checklist, but see below!
|
||||
- Create the release branch from this nightly tag:
|
||||
```
|
||||
git remote add nightly https://github.com/leanprover/lean4-nightly.git
|
||||
git fetch nightly tag nightly-2024-02-29
|
||||
git checkout nightly-2024-02-29
|
||||
git checkout -b releases/v4.7.0
|
||||
```
|
||||
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
|
||||
- Our current goal is to have written release notes only about major language features or breaking changes,
|
||||
and to rely on automatically generated release notes for bugfixes and minor changes.
|
||||
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
|
||||
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
|
||||
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
|
||||
- Commit your changes to `RELEASES.md`, and push.
|
||||
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
|
||||
- In `src/CMakeLists.txt`,
|
||||
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
|
||||
- Commit your changes to `src/CMakeLists.txt`, and push.
|
||||
- `git tag v4.7.0-rc1`
|
||||
- `git push origin v4.7.0-rc1`
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
|
||||
- This step can take up to an hour.
|
||||
- Once the release appears at https://github.com/leanprover/lean4/releases/
|
||||
- Edit the release notes on Github to select the "Set as a pre-release box".
|
||||
- Copy the section of `RELEASES.md` for this version into the Github release notes.
|
||||
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
|
||||
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
- This will add a list of all the commits since the last stable version.
|
||||
- Delete anything already mentioned in the hand-written release notes above.
|
||||
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
|
||||
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
|
||||
but for minor items don't put any work in expanding on commit messages.
|
||||
- (How we want to release notes to look is evolving: please update this section if it looks wrong!)
|
||||
- Next, we will move a curated list of downstream repos to the release candidate.
|
||||
- This assumes that there is already a *reviewed* branch `bump/v4.7.0` on each repository
|
||||
containing the required adaptations (or no adaptations are required).
|
||||
The preparation of this branch is beyond the scope of this document.
|
||||
- For each of the target repositories:
|
||||
- Checkout the `bump/v4.7.0` branch.
|
||||
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
|
||||
- `git merge origin/master`
|
||||
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
|
||||
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
|
||||
back to `master` or `main`, and run `lake update` for those dependencies.
|
||||
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
|
||||
- `git commit`
|
||||
- `git push`
|
||||
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
|
||||
or notify the maintainers that it is ready to go.
|
||||
- Once this PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
|
||||
- We do this for the same list of repositories as for stable releases, see above.
|
||||
As above, there are dependencies between these, and so the process above is iterative.
|
||||
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
|
||||
- For Std/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
|
||||
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
|
||||
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
|
||||
- Make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.7.0-rc1`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
|
||||
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
|
||||
- Removes `(in development)` from the section heading in `RELEASES.md` for `v4.7.0`,
|
||||
and creates a new `v4.8.0 (in development)` section heading.
|
||||
|
||||
## Time estimates:
|
||||
Slightly longer than the corresponding steps for a stable release.
|
||||
Similar process, but more things go wrong.
|
||||
In particular, updating the downstream repositories is significantly more work
|
||||
(because we need to merge existing `bump/v4.7.0` branches, not just update a toolchain).
|
||||
|
||||
# Preparing `bump/v4.7.0` branches
|
||||
|
||||
While not part of the release process per se,
|
||||
this is a brief summary of the work that goes into updating Std/Aesop/Mathlib to new versions.
|
||||
|
||||
Please read https://leanprover-community.github.io/contribute/tags_and_branches.html
|
||||
|
||||
* Each repo has an unreviewed `nightly-testing` branch that
|
||||
receives commits automatically from `master`, and
|
||||
has its toolchain updated automatically for every nightly.
|
||||
(Note: the aesop branch is not automated, and is updated on an as needed basis.)
|
||||
As a consequence this branch is often broken.
|
||||
A bot posts in the (private!) "Mathlib reviewers" stream on Zulip about the status of these branches.
|
||||
* We fix the breakages by committing directly to `nightly-testing`: there is no PR process.
|
||||
* This can either be done by the person managing this process directly,
|
||||
or by soliciting assistance from authors of files, or generally helpful people on Zulip!
|
||||
* Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions.
|
||||
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we:
|
||||
* Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary)
|
||||
* Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch.
|
||||
* In that branch, `git merge --squash nightly-testing` to bring across changes from `nightly-testing`.
|
||||
* Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch.
|
||||
* Solicit review, merge the PR into `bump/v4,7,0`.
|
||||
* It is always okay to merge in the following directions:
|
||||
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
|
||||
Please remember to push any merges you make to intermediate steps!
|
||||
@@ -277,14 +277,13 @@ theorem BinTree.find_insert (b : BinTree β) (k : Nat) (v : β)
|
||||
. by_cases' key < k
|
||||
cases h; apply ihr; assumption
|
||||
|
||||
theorem BinTree.find_insert_of_ne (b : BinTree β) (h : k ≠ k') (v : β)
|
||||
theorem BinTree.find_insert_of_ne (b : BinTree β) (ne : k ≠ k') (v : β)
|
||||
: (b.insert k v).find? k' = b.find? k' := by
|
||||
let ⟨t, h⟩ := b; simp
|
||||
induction t with simp
|
||||
| leaf =>
|
||||
split <;> (try simp) <;> split <;> (try simp)
|
||||
have_eq k k'
|
||||
contradiction
|
||||
intros le
|
||||
exact Nat.lt_of_le_of_ne le ne
|
||||
| node left key value right ihl ihr =>
|
||||
let .node hl hr bl br := h
|
||||
specialize ihl bl
|
||||
|
||||
@@ -33,7 +33,7 @@ convert the pure non-monadic value `x / y` into the required `Except` object. S
|
||||
|
||||
Now this return typing would get tedious if you had to include it everywhere that you call this
|
||||
function, however, Lean type inference can clean this up. For example, you can define a test
|
||||
function can calls the `divide` function and you don't need to say anything here about the fact that
|
||||
function that calls the `divide` function and you don't need to say anything here about the fact that
|
||||
it might throw an error, because that is inferred:
|
||||
-/
|
||||
def test := divide 5 0
|
||||
|
||||
@@ -65,12 +65,7 @@ rec {
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin $out/lib/lean
|
||||
mv bin/lean $out/bin/
|
||||
mv lib/lean/libleanshared.* $out/lib/lean
|
||||
'' + lib.optionalString stdenv.isDarwin ''
|
||||
for lib in $(otool -L $out/bin/lean | tail -n +2 | cut -d' ' -f1); do
|
||||
if [[ "$lib" == *lean* ]]; then install_name_tool -change "$lib" "$out/lib/lean/$(basename $lib)" $out/bin/lean; fi
|
||||
done
|
||||
otool -L $out/bin/lean
|
||||
mv lib/lean/*.so $out/lib/lean
|
||||
'';
|
||||
meta.mainProgram = "lean";
|
||||
});
|
||||
@@ -120,29 +115,35 @@ rec {
|
||||
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
|
||||
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
|
||||
stdlibLinkFlags = "-L${Init.staticLib} -L${Lean.staticLib} -L${Lake.staticLib} -L${leancpp}/lib/lean";
|
||||
libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
|
||||
mkdir $out
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
|
||||
-Wl,--whole-archive -lInit ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
|
||||
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
|
||||
-o $out/$libName
|
||||
'';
|
||||
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
|
||||
mkdir $out
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
|
||||
${if stdenv.isDarwin then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
|
||||
else "-Wl,--whole-archive -lInit -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} -lm ${stdlibLinkFlags} \
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
|
||||
${libInit_shared}/* -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
|
||||
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
|
||||
-o $out/$libName
|
||||
'';
|
||||
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
|
||||
print-paths = Lean.makePrintPathsFor [] mods;
|
||||
leanc = writeShellScriptBin "leanc" ''
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${leanshared} "$@"
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared} "$@"
|
||||
'';
|
||||
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
|
||||
mkdir -p $out/bin
|
||||
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${leanshared}/* -o $out/bin/lean
|
||||
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${libInit_shared}/* ${leanshared}/* -o $out/bin/lean
|
||||
'';
|
||||
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
|
||||
lean-all = stdenv.mkDerivation {
|
||||
name = "lean-${desc}";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin $out/lib/lean
|
||||
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${leanshared}/* $out/lib/lean/
|
||||
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared}/* $out/lib/lean/
|
||||
# put everything in a single final derivation so `IO.appDir` references work
|
||||
cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin
|
||||
# NOTE: `lndir` will not override existing `bin/leanc`
|
||||
|
||||
@@ -10,7 +10,7 @@ lib.makeOverridable (
|
||||
staticLibDeps ? [],
|
||||
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
|
||||
groupStaticLibs ? false,
|
||||
# Shared library dependencies included at interpretation with --load-dynlib and linked to. Each derivation `shared` should contain a
|
||||
# Shared library dependencies included at interpretation with --load-dynlib and linked to. Each derivation `shared` should contain a
|
||||
# shared library at the path `${shared}/${shared.libName or shared.name}` and a name to link to like `-l${shared.linkName or shared.name}`.
|
||||
# These libs are also linked to in packages that depend on this one.
|
||||
nativeSharedLibs ? [],
|
||||
@@ -88,9 +88,9 @@ with builtins; let
|
||||
allNativeSharedLibs =
|
||||
lib.unique (lib.flatten (nativeSharedLibs ++ (map (dep: dep.allNativeSharedLibs or []) allExternalDeps)));
|
||||
|
||||
# A flattened list of all static library dependencies: this and every dep module's explicitly provided `staticLibDeps`,
|
||||
# A flattened list of all static library dependencies: this and every dep module's explicitly provided `staticLibDeps`,
|
||||
# plus every dep module itself: `dep.staticLib`
|
||||
allStaticLibDeps =
|
||||
allStaticLibDeps =
|
||||
lib.unique (lib.flatten (staticLibDeps ++ (map (dep: [dep.staticLib] ++ dep.staticLibDeps or []) allExternalDeps)));
|
||||
|
||||
pathOfSharedLib = dep: dep.libPath or "${dep}/${dep.libName or dep.name}";
|
||||
@@ -249,7 +249,7 @@ in rec {
|
||||
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
|
||||
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
|
||||
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
|
||||
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
|
||||
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.libInit_shared}/* ${lean-final.leanshared}/*";
|
||||
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
|
||||
mkdir -p $out/bin
|
||||
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2022 Sebastian Ullrich. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sebastian Ullrich
|
||||
-/
|
||||
import Lean.Runtime
|
||||
|
||||
abbrev M := ReaderT IO.FS.Stream IO
|
||||
@@ -16,7 +21,7 @@ def mkTypedefFn (i : Nat) : M Unit := do
|
||||
emit s!"typedef obj* (*fn{i})({args}); // NOLINT\n"
|
||||
emit s!"#define FN{i}(f) reinterpret_cast<fn{i}>(lean_closure_fun(f))\n"
|
||||
|
||||
def genSeq (n : Nat) (f : Nat → String) (sep := ", ") : String :=
|
||||
def genSeq (n : Nat) (f : Nat → String) (sep := ", ") : String :=
|
||||
List.range n |>.map f |>.intersperse sep |> .join
|
||||
|
||||
-- make string: "obj* a1, obj* a2, ..., obj* an"
|
||||
|
||||
@@ -25,6 +25,8 @@ cp -L llvm/bin/llvm-ar stage1/bin/
|
||||
# dependencies of the above
|
||||
$CP llvm/lib/lib{clang-cpp,LLVM}*.so* stage1/lib/
|
||||
$CP $ZLIB/lib/libz.so* stage1/lib/
|
||||
# general clang++ dependency, breaks cross-library C++ exceptions if linked statically
|
||||
$CP $GCC_LIB/lib/libgcc_s.so* stage1/lib/
|
||||
# bundle libatomic (referenced by LLVM >= 15, and required by the lean executable to run)
|
||||
$CP $GCC_LIB/lib/libatomic.so* stage1/lib/
|
||||
|
||||
@@ -60,7 +62,7 @@ fi
|
||||
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
|
||||
# but do not change sysroot so users can still link against system libs
|
||||
echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
# when not using the above flags, link GMP dynamically/as usual
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -Wl,--no-as-needed'"
|
||||
# do not set `LEAN_CC` for tests
|
||||
|
||||
@@ -9,7 +9,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 7)
|
||||
set(LEAN_VERSION_MINOR 8)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -299,13 +299,12 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
|
||||
endif()
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
|
||||
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
|
||||
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
|
||||
else()
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
|
||||
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
|
||||
endif()
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lLake")
|
||||
|
||||
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
|
||||
|
||||
@@ -313,8 +312,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(LEAN_CXX_STDLIB "-lc++")
|
||||
endif()
|
||||
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
|
||||
|
||||
# flags for user binaries = flags for toolchain binaries + Lake
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
|
||||
|
||||
if (LLVM)
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
|
||||
@@ -342,9 +344,9 @@ endif()
|
||||
|
||||
# get rid of unused parts of C++ stdlib
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-dead_strip")
|
||||
elseif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--gc-sections")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,--gc-sections")
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
@@ -354,26 +356,20 @@ endif()
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
if(BSYMBOLIC)
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
|
||||
endif()
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
|
||||
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
|
||||
# We do not use dynamic linking via leanshared for Emscripten to keep things
|
||||
# simple. (And we are not interested in `Lake` anyway.) To use dynamic
|
||||
# linking, we would probably have to set MAIN_MODULE=2 on `leanshared`,
|
||||
# SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js".
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--whole-archive -lInit -lLean -lleancpp -lleanrt ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
@@ -399,7 +395,7 @@ endif()
|
||||
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
# import library created by the `leanshared` target
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lleanshared")
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared")
|
||||
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
|
||||
endif()
|
||||
@@ -505,13 +501,25 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
|
||||
# (also looks nicer in the build log)
|
||||
file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
# set up libInit_shared only on Windows; see also stdlib.make.in
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
|
||||
else()
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# We do not use dynamic linking via leanshared for Emscripten to keep things
|
||||
# simple. (And we are not interested in `Lake` anyway.) To use dynamic
|
||||
# linking, we would probably have to set MAIN_MODULE=2 on `leanshared`,
|
||||
# SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js".
|
||||
string(APPEND LEAN_EXE_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
|
||||
endif()
|
||||
|
||||
# Build the compiler using the bootstrapped C sources for stage0, and use
|
||||
@@ -520,10 +528,6 @@ if (LLVM AND ${STAGE} GREATER 0)
|
||||
set(EXTRA_LEANMAKE_OPTS "LLVM=1")
|
||||
endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
|
||||
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
|
||||
add_custom_target(make_stdlib ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
|
||||
@@ -541,13 +545,33 @@ endif()
|
||||
# We declare these as separate custom targets so they use separate `make` invocations, which makes `make` recompute which dependencies
|
||||
# (e.g. `libLean.a`) are now newer than the target file
|
||||
|
||||
add_custom_target(leanshared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS make_stdlib leancpp leanrt_initial-exec
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
|
||||
VERBATIM)
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# dummy targets, see `MAIN_MODULE` discussion above
|
||||
add_custom_target(Init_shared ALL
|
||||
DEPENDS make_stdlib leanrt_initial-exec
|
||||
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libInit_shared${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_custom_target(leanshared ALL
|
||||
DEPENDS Init_shared leancpp
|
||||
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
else()
|
||||
add_custom_target(Init_shared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS make_stdlib leanrt_initial-exec
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init_shared
|
||||
VERBATIM)
|
||||
|
||||
if(${STAGE} GREATER 0)
|
||||
add_custom_target(leanshared ALL
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS Init_shared leancpp
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
|
||||
VERBATIM)
|
||||
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared")
|
||||
endif()
|
||||
|
||||
if(${STAGE} GREATER 0 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
if(NOT EXISTS ${LEAN_SOURCE_DIR}/lake/Lake.lean)
|
||||
message(FATAL_ERROR "src/lake does not exist. Please check out the Lake submodule using `git submodule update --init src/lake`.")
|
||||
endif()
|
||||
@@ -568,7 +592,7 @@ endif()
|
||||
|
||||
# use Bash version for building, use Lean version in bin/ for tests & distribution
|
||||
configure_file("${LEAN_SOURCE_DIR}/bin/leanc.in" "${CMAKE_BINARY_DIR}/leanc.sh" @ONLY)
|
||||
if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean)
|
||||
if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
configure_file("${LEAN_SOURCE_DIR}/Leanc.lean" "${CMAKE_BINARY_DIR}/leanc/Leanc.lean" @ONLY)
|
||||
add_custom_target(leanc ALL
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/leanc
|
||||
@@ -619,3 +643,8 @@ if(LEAN_INSTALL_PREFIX)
|
||||
set(LEAN_INSTALL_SUFFIX "-${LOWER_SYSTEM_NAME}" CACHE STRING "If LEAN_INSTALL_PREFIX is set, append this value to CMAKE_INSTALL_PREFIX")
|
||||
set(CMAKE_INSTALL_PREFIX "${LEAN_INSTALL_PREFIX}/lean-${LEAN_VERSION_STRING}${LEAN_INSTALL_SUFFIX}")
|
||||
endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
|
||||
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
|
||||
|
||||
@@ -8,6 +8,7 @@ import Init.Prelude
|
||||
import Init.Notation
|
||||
import Init.Tactics
|
||||
import Init.TacticsExtra
|
||||
import Init.ByCases
|
||||
import Init.RCases
|
||||
import Init.Core
|
||||
import Init.Control
|
||||
@@ -23,8 +24,12 @@ import Init.MetaTypes
|
||||
import Init.Meta
|
||||
import Init.NotationExtra
|
||||
import Init.SimpLemmas
|
||||
import Init.PropLemmas
|
||||
import Init.Hints
|
||||
import Init.Conv
|
||||
import Init.Guard
|
||||
import Init.Simproc
|
||||
import Init.SizeOfLemmas
|
||||
import Init.BinderPredicates
|
||||
import Init.Ext
|
||||
import Init.Omega
|
||||
|
||||
82
src/Init/BinderPredicates.lean
Normal file
82
src/Init/BinderPredicates.lean
Normal file
@@ -0,0 +1,82 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.NotationExtra
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
The syntax category of binder predicates contains predicates like `> 0`, `∈ s`, etc.
|
||||
(`: t` should not be a binder predicate because it would clash with the built-in syntax for ∀/∃.)
|
||||
-/
|
||||
declare_syntax_cat binderPred
|
||||
|
||||
/--
|
||||
`satisfies_binder_pred% t pred` expands to a proposition expressing that `t` satisfies `pred`.
|
||||
-/
|
||||
syntax "satisfies_binder_pred% " term:max binderPred : term
|
||||
|
||||
-- Extend ∀ and ∃ to binder predicates.
|
||||
|
||||
/--
|
||||
The notation `∃ x < 2, p x` is shorthand for `∃ x, x < 2 ∧ p x`,
|
||||
and similarly for other binary operators.
|
||||
-/
|
||||
syntax "∃ " binderIdent binderPred ", " term : term
|
||||
/--
|
||||
The notation `∀ x < 2, p x` is shorthand for `∀ x, x < 2 → p x`,
|
||||
and similarly for other binary operators.
|
||||
-/
|
||||
syntax "∀ " binderIdent binderPred ", " term : term
|
||||
|
||||
macro_rules
|
||||
| `(∃ $x:ident $pred:binderPred, $p) =>
|
||||
`(∃ $x:ident, satisfies_binder_pred% $x $pred ∧ $p)
|
||||
| `(∃ _ $pred:binderPred, $p) =>
|
||||
`(∃ x, satisfies_binder_pred% x $pred ∧ $p)
|
||||
|
||||
macro_rules
|
||||
| `(∀ $x:ident $pred:binderPred, $p) =>
|
||||
`(∀ $x:ident, satisfies_binder_pred% $x $pred → $p)
|
||||
| `(∀ _ $pred:binderPred, $p) =>
|
||||
`(∀ x, satisfies_binder_pred% x $pred → $p)
|
||||
|
||||
/-- Declare `∃ x > y, ...` as syntax for `∃ x, x > y ∧ ...` -/
|
||||
binder_predicate x " > " y:term => `($x > $y)
|
||||
/-- Declare `∃ x ≥ y, ...` as syntax for `∃ x, x ≥ y ∧ ...` -/
|
||||
binder_predicate x " ≥ " y:term => `($x ≥ $y)
|
||||
/-- Declare `∃ x < y, ...` as syntax for `∃ x, x < y ∧ ...` -/
|
||||
binder_predicate x " < " y:term => `($x < $y)
|
||||
/-- Declare `∃ x ≤ y, ...` as syntax for `∃ x, x ≤ y ∧ ...` -/
|
||||
binder_predicate x " ≤ " y:term => `($x ≤ $y)
|
||||
/-- Declare `∃ x ≠ y, ...` as syntax for `∃ x, x ≠ y ∧ ...` -/
|
||||
binder_predicate x " ≠ " y:term => `($x ≠ $y)
|
||||
|
||||
/-- Declare `∀ x ∈ y, ...` as syntax for `∀ x, x ∈ y → ...` and `∃ x ∈ y, ...` as syntax for
|
||||
`∃ x, x ∈ y ∧ ...` -/
|
||||
binder_predicate x " ∈ " y:term => `($x ∈ $y)
|
||||
|
||||
/-- Declare `∀ x ∉ y, ...` as syntax for `∀ x, x ∉ y → ...` and `∃ x ∉ y, ...` as syntax for
|
||||
`∃ x, x ∉ y ∧ ...` -/
|
||||
binder_predicate x " ∉ " y:term => `($x ∉ $y)
|
||||
|
||||
/-- Declare `∀ x ⊆ y, ...` as syntax for `∀ x, x ⊆ y → ...` and `∃ x ⊆ y, ...` as syntax for
|
||||
`∃ x, x ⊆ y ∧ ...` -/
|
||||
binder_predicate x " ⊆ " y:term => `($x ⊆ $y)
|
||||
|
||||
/-- Declare `∀ x ⊂ y, ...` as syntax for `∀ x, x ⊂ y → ...` and `∃ x ⊂ y, ...` as syntax for
|
||||
`∃ x, x ⊂ y ∧ ...` -/
|
||||
binder_predicate x " ⊂ " y:term => `($x ⊂ $y)
|
||||
|
||||
/-- Declare `∀ x ⊇ y, ...` as syntax for `∀ x, x ⊇ y → ...` and `∃ x ⊇ y, ...` as syntax for
|
||||
`∃ x, x ⊇ y ∧ ...` -/
|
||||
binder_predicate x " ⊇ " y:term => `($x ⊇ $y)
|
||||
|
||||
/-- Declare `∀ x ⊃ y, ...` as syntax for `∀ x, x ⊃ y → ...` and `∃ x ⊃ y, ...` as syntax for
|
||||
`∃ x, x ⊃ y ∧ ...` -/
|
||||
binder_predicate x " ⊃ " y:term => `($x ⊃ $y)
|
||||
|
||||
end Lean
|
||||
65
src/Init/ByCases.lean
Normal file
65
src/Init/ByCases.lean
Normal file
@@ -0,0 +1,65 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Classical
|
||||
|
||||
/-! # by_cases tactic and if-then-else support -/
|
||||
|
||||
/--
|
||||
`by_cases (h :)? p` splits the main goal into two cases, assuming `h : p` in the first branch, and `h : ¬ p` in the second branch.
|
||||
-/
|
||||
syntax "by_cases " (atomic(ident " : "))? term : tactic
|
||||
|
||||
macro_rules
|
||||
| `(tactic| by_cases $e) => `(tactic| by_cases h : $e)
|
||||
macro_rules
|
||||
| `(tactic| by_cases $h : $e) =>
|
||||
`(tactic| open Classical in refine if $h:ident : $e then ?pos else ?neg)
|
||||
|
||||
/-! ## if-then-else -/
|
||||
|
||||
@[simp] theorem if_true {h : Decidable True} (t e : α) : ite True t e = t := if_pos trivial
|
||||
|
||||
@[simp] theorem if_false {h : Decidable False} (t e : α) : ite False t e = e := if_neg id
|
||||
|
||||
theorem ite_id [Decidable c] {α} (t : α) : (if c then t else t) = t := by split <;> rfl
|
||||
|
||||
/-- A function applied to a `dite` is a `dite` of that function applied to each of the branches. -/
|
||||
theorem apply_dite (f : α → β) (P : Prop) [Decidable P] (x : P → α) (y : ¬P → α) :
|
||||
f (dite P x y) = dite P (fun h => f (x h)) (fun h => f (y h)) := by
|
||||
by_cases h : P <;> simp [h]
|
||||
|
||||
/-- A function applied to a `ite` is a `ite` of that function applied to each of the branches. -/
|
||||
theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
|
||||
f (ite P x y) = ite P (f x) (f y) :=
|
||||
apply_dite f P (fun _ => x) (fun _ => y)
|
||||
|
||||
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P → α} :
|
||||
dite P (fun _ => a) B = a ↔ ∀ h, B h = a := by
|
||||
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
|
||||
|
||||
@[simp] theorem dite_eq_right_iff {P : Prop} [Decidable P] {A : P → α} :
|
||||
(dite P A fun _ => b) = b ↔ ∀ h, A h = b := by
|
||||
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
|
||||
|
||||
@[simp] theorem ite_eq_left_iff {P : Prop} [Decidable P] : ite P a b = a ↔ ¬P → b = a :=
|
||||
dite_eq_left_iff
|
||||
|
||||
@[simp] theorem ite_eq_right_iff {P : Prop} [Decidable P] : ite P a b = b ↔ P → a = b :=
|
||||
dite_eq_right_iff
|
||||
|
||||
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
|
||||
@[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl
|
||||
|
||||
-- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
|
||||
theorem ite_some_none_eq_none [Decidable P] :
|
||||
(if P then some x else none) = none ↔ ¬ P := by
|
||||
simp only [ite_eq_right_iff]
|
||||
rfl
|
||||
|
||||
@[simp] theorem ite_some_none_eq_some [Decidable P] :
|
||||
(if P then some x else none) = some y ↔ P ∧ x = y := by
|
||||
split <;> simp_all
|
||||
@@ -4,8 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
import Init.NotationExtra
|
||||
import Init.PropLemmas
|
||||
|
||||
universe u v
|
||||
|
||||
@@ -112,8 +111,8 @@ theorem skolem {α : Sort u} {b : α → Sort v} {p : ∀ x, b x → Prop} : (
|
||||
|
||||
theorem propComplete (a : Prop) : a = True ∨ a = False :=
|
||||
match em a with
|
||||
| Or.inl ha => Or.inl (propext (Iff.intro (fun _ => ⟨⟩) (fun _ => ha)))
|
||||
| Or.inr hn => Or.inr (propext (Iff.intro (fun h => hn h) (fun h => False.elim h)))
|
||||
| Or.inl ha => Or.inl (eq_true ha)
|
||||
| Or.inr hn => Or.inr (eq_false hn)
|
||||
|
||||
-- this supercedes byCases in Decidable
|
||||
theorem byCases {p q : Prop} (hpq : p → q) (hnpq : ¬p → q) : q :=
|
||||
@@ -123,15 +122,49 @@ theorem byCases {p q : Prop} (hpq : p → q) (hnpq : ¬p → q) : q :=
|
||||
theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
|
||||
Decidable.byContradiction (dec := propDecidable _) h
|
||||
|
||||
/-- The Double Negation Theorem: `¬¬P` is equivalent to `P`.
|
||||
The left-to-right direction, double negation elimination (DNE),
|
||||
is classically true but not constructively. -/
|
||||
@[simp] theorem not_not : ¬¬a ↔ a := Decidable.not_not
|
||||
|
||||
@[simp low] theorem not_forall {p : α → Prop} : (¬∀ x, p x) ↔ ∃ x, ¬p x := Decidable.not_forall
|
||||
|
||||
theorem not_forall_not {p : α → Prop} : (¬∀ x, ¬p x) ↔ ∃ x, p x := Decidable.not_forall_not
|
||||
theorem not_exists_not {p : α → Prop} : (¬∃ x, ¬p x) ↔ ∀ x, p x := Decidable.not_exists_not
|
||||
|
||||
theorem forall_or_exists_not (P : α → Prop) : (∀ a, P a) ∨ ∃ a, ¬ P a := by
|
||||
rw [← not_forall]; exact em _
|
||||
theorem exists_or_forall_not (P : α → Prop) : (∃ a, P a) ∨ ∀ a, ¬ P a := by
|
||||
rw [← not_exists]; exact em _
|
||||
|
||||
theorem or_iff_not_imp_left : a ∨ b ↔ (¬a → b) := Decidable.or_iff_not_imp_left
|
||||
theorem or_iff_not_imp_right : a ∨ b ↔ (¬b → a) := Decidable.or_iff_not_imp_right
|
||||
|
||||
theorem not_imp_iff_and_not : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff_and_not
|
||||
|
||||
theorem not_and_iff_or_not_not : ¬(a ∧ b) ↔ ¬a ∨ ¬b := Decidable.not_and_iff_or_not_not
|
||||
|
||||
theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
|
||||
|
||||
@[simp] theorem imp_iff_left_iff : (b ↔ a → b) ↔ a ∨ b := Decidable.imp_iff_left_iff
|
||||
@[simp] theorem imp_iff_right_iff : (a → b ↔ b) ↔ a ∨ b := Decidable.imp_iff_right_iff
|
||||
|
||||
@[simp] theorem and_or_imp : a ∧ b ∨ (a → c) ↔ a → b ∨ c := Decidable.and_or_imp
|
||||
|
||||
@[simp] theorem not_imp : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff_and_not
|
||||
|
||||
@[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p → q) ∧ (¬p → q) ↔ q :=
|
||||
Iff.intro (fun (a : _ ∧ _) => (Classical.em p).rec a.left a.right)
|
||||
(fun a => And.intro (fun _ => a) (fun _ => a))
|
||||
|
||||
end Classical
|
||||
|
||||
/--
|
||||
`by_cases (h :)? p` splits the main goal into two cases, assuming `h : p` in the first branch, and `h : ¬ p` in the second branch.
|
||||
-/
|
||||
syntax "by_cases " (atomic(ident " : "))? term : tactic
|
||||
/- Export for Mathlib compat. -/
|
||||
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
|
||||
|
||||
macro_rules
|
||||
| `(tactic| by_cases $e) => `(tactic| by_cases h : $e)
|
||||
macro_rules
|
||||
| `(tactic| by_cases $h : $e) =>
|
||||
`(tactic| open Classical in refine if $h:ident : $e then ?pos else ?neg)
|
||||
/-- Extract an element from a existential statement, using `Classical.choose`. -/
|
||||
-- This enables projection notation.
|
||||
@[reducible] noncomputable def Exists.choose {p : α → Prop} (P : ∃ a, p a) : α := Classical.choose P
|
||||
|
||||
/-- Show that an element extracted from `P : ∃ a, p a` using `P.choose` satisfies `p`. -/
|
||||
theorem Exists.choose_spec {p : α → Prop} (P : ∃ a, p a) : p P.choose := Classical.choose_spec P
|
||||
|
||||
@@ -321,7 +321,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
|
||||
This is used for coercions between monads, in the case where we want to apply
|
||||
a monad lift and a coercion on the result type at the same time.
|
||||
-/
|
||||
@[inline, coe_decl] def Lean.Internal.liftCoeM {m : Type u → Type v} {n : Type u → Type w} {α β : Type u}
|
||||
@[coe_decl] abbrev Lean.Internal.liftCoeM {m : Type u → Type v} {n : Type u → Type w} {α β : Type u}
|
||||
[MonadLiftT m n] [∀ a, CoeT α a β] [Monad n] (x : m α) : n β := do
|
||||
let a ← liftM x
|
||||
pure (CoeT.coe a)
|
||||
@@ -331,7 +331,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
|
||||
|
||||
This is used for coercing the result type under a monad.
|
||||
-/
|
||||
@[inline, coe_decl] def Lean.Internal.coeM {m : Type u → Type v} {α β : Type u}
|
||||
@[coe_decl] abbrev Lean.Internal.coeM {m : Type u → Type v} {α β : Type u}
|
||||
[∀ a, CoeT α a β] [Monad m] (x : m α) : m β := do
|
||||
let a ← x
|
||||
pure (CoeT.coe a)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sebastian Ullrich, Leonardo de Moura
|
||||
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.SimpLemmas
|
||||
@@ -84,6 +84,36 @@ theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *>
|
||||
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]
|
||||
|
||||
/--
|
||||
An alternative constructor for `LawfulMonad` which has more
|
||||
defaultable fields in the common case.
|
||||
-/
|
||||
theorem LawfulMonad.mk' (m : Type u → Type v) [Monad m]
|
||||
(id_map : ∀ {α} (x : m α), id <$> x = x)
|
||||
(pure_bind : ∀ {α β} (x : α) (f : α → m β), pure x >>= f = f x)
|
||||
(bind_assoc : ∀ {α β γ} (x : m α) (f : α → m β) (g : β → m γ),
|
||||
x >>= f >>= g = x >>= fun x => f x >>= g)
|
||||
(map_const : ∀ {α β} (x : α) (y : m β),
|
||||
Functor.mapConst x y = Function.const β x <$> y := by intros; rfl)
|
||||
(seqLeft_eq : ∀ {α β} (x : m α) (y : m β),
|
||||
x <* y = (x >>= fun a => y >>= fun _ => pure a) := by intros; rfl)
|
||||
(seqRight_eq : ∀ {α β} (x : m α) (y : m β), x *> y = (x >>= fun _ => y) := by intros; rfl)
|
||||
(bind_pure_comp : ∀ {α β} (f : α → β) (x : m α),
|
||||
x >>= (fun y => pure (f y)) = f <$> x := by intros; rfl)
|
||||
(bind_map : ∀ {α β} (f : m (α → β)) (x : m α), f >>= (. <$> x) = f <*> x := by intros; rfl)
|
||||
: LawfulMonad m :=
|
||||
have map_pure {α β} (g : α → β) (x : α) : g <$> (pure x : m α) = pure (g x) := by
|
||||
rw [← bind_pure_comp]; simp [pure_bind]
|
||||
{ id_map, bind_pure_comp, bind_map, pure_bind, bind_assoc, map_pure,
|
||||
comp_map := by simp [← bind_pure_comp, bind_assoc, pure_bind]
|
||||
pure_seq := by intros; rw [← bind_map]; simp [pure_bind]
|
||||
seq_pure := by intros; rw [← bind_map]; simp [map_pure, bind_pure_comp]
|
||||
seq_assoc := by simp [← bind_pure_comp, ← bind_map, bind_assoc, pure_bind]
|
||||
map_const := funext fun x => funext (map_const x)
|
||||
seqLeft_eq := by simp [seqLeft_eq, ← bind_map, ← bind_pure_comp, pure_bind, bind_assoc]
|
||||
seqRight_eq := fun x y => by
|
||||
rw [seqRight_eq, ← bind_map, ← bind_pure_comp, bind_assoc]; simp [pure_bind, id_map] }
|
||||
|
||||
/-! # Id -/
|
||||
|
||||
namespace Id
|
||||
@@ -173,6 +203,16 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
|
||||
|
||||
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)
|
||||
|
||||
instance : LawfulApplicative (Except ε) := inferInstance
|
||||
instance : LawfulFunctor (Except ε) := inferInstance
|
||||
|
||||
/-! # ReaderT -/
|
||||
|
||||
namespace ReaderT
|
||||
@@ -307,3 +347,30 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
|
||||
bind_assoc := by intros; apply ext; intros; simp
|
||||
|
||||
end StateT
|
||||
|
||||
/-! # EStateM -/
|
||||
|
||||
instance : LawfulMonad (EStateM ε σ) := .mk'
|
||||
(id_map := fun x => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.map]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(bind_assoc := fun x _ _ => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.bind]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
(map_const := fun _ _ => rfl)
|
||||
|
||||
/-! # Option -/
|
||||
|
||||
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)
|
||||
|
||||
instance : LawfulApplicative Option := inferInstance
|
||||
instance : LawfulFunctor Option := inferInstance
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
Notation for operators defined at Prelude.lean
|
||||
-/
|
||||
prelude
|
||||
import Init.NotationExtra
|
||||
import Init.Meta
|
||||
|
||||
namespace Lean.Parser.Tactic.Conv
|
||||
|
||||
@@ -54,6 +54,10 @@ syntax (name := lhs) "lhs" : conv
|
||||
(In general, for an `n`-ary operator, it traverses into the last argument.) -/
|
||||
syntax (name := rhs) "rhs" : conv
|
||||
|
||||
/-- Traverses into the function of a (unary) function application.
|
||||
For example, `| f a b` turns into `| f a`. (Use `arg 0` to traverse into `f`.) -/
|
||||
syntax (name := «fun») "fun" : conv
|
||||
|
||||
/-- Reduces the target to Weak Head Normal Form. This reduces definitions
|
||||
in "head position" until a constructor is exposed. For example, `List.map f [a, b, c]`
|
||||
weak head normalizes to `f a :: List.map f [b, c]`. -/
|
||||
@@ -74,7 +78,8 @@ syntax (name := congr) "congr" : conv
|
||||
* `arg i` traverses into the `i`'th argument of the target. For example if the
|
||||
target is `f a b c d` then `arg 1` traverses to `a` and `arg 3` traverses to `c`.
|
||||
* `arg @i` is the same as `arg i` but it counts all arguments instead of just the
|
||||
explicit arguments. -/
|
||||
explicit arguments.
|
||||
* `arg 0` traverses into the function. If the target is `f a b c d`, `arg 0` traverses into `f`. -/
|
||||
syntax (name := arg) "arg " "@"? num : conv
|
||||
|
||||
/-- `ext x` traverses into a binder (a `fun x => e` or `∀ x, e` expression)
|
||||
@@ -303,4 +308,7 @@ Basic forms:
|
||||
-- refer to the syntax category instead of this syntax
|
||||
syntax (name := conv) "conv" (" at " ident)? (" in " (occs)? term)? " => " convSeq : tactic
|
||||
|
||||
/-- `norm_cast` tactic in `conv` mode. -/
|
||||
syntax (name := normCast) "norm_cast" : conv
|
||||
|
||||
end Lean.Parser.Tactic.Conv
|
||||
|
||||
@@ -17,7 +17,9 @@ universe u v w
|
||||
at the application site itself (by comparison to the `@[inline]` attribute,
|
||||
which applies to all applications of the function).
|
||||
-/
|
||||
def inline {α : Sort u} (a : α) : α := a
|
||||
@[simp] def inline {α : Sort u} (a : α) : α := a
|
||||
|
||||
theorem id.def {α : Sort u} (a : α) : id a = a := rfl
|
||||
|
||||
/--
|
||||
`flip f a b` is `f b a`. It is useful for "point-free" programming,
|
||||
@@ -32,8 +34,32 @@ and `flip (·<·)` is the greater-than relation.
|
||||
|
||||
@[simp] theorem Function.comp_apply {f : β → δ} {g : α → β} {x : α} : comp f g x = f (g x) := rfl
|
||||
|
||||
theorem Function.comp_def {α β δ} (f : β → δ) (g : α → β) : f ∘ g = fun x => f (g x) := rfl
|
||||
|
||||
attribute [simp] namedPattern
|
||||
|
||||
/--
|
||||
`Empty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`Empty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `Empty.rec`.
|
||||
-/
|
||||
@[macro_inline] def Empty.elim {C : Sort u} : Empty → C := Empty.rec
|
||||
|
||||
/-- Decidable equality for Empty -/
|
||||
instance : DecidableEq Empty := fun a => a.elim
|
||||
|
||||
/--
|
||||
`PEmpty.elim : Empty → C` says that a value of any type can be constructed from
|
||||
`PEmpty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
|
||||
|
||||
This is a non-dependent variant of `PEmpty.rec`.
|
||||
-/
|
||||
@[macro_inline] def PEmpty.elim {C : Sort _} : PEmpty → C := fun a => nomatch a
|
||||
|
||||
/-- Decidable equality for PEmpty -/
|
||||
instance : DecidableEq PEmpty := fun a => a.elim
|
||||
|
||||
/--
|
||||
Thunks are "lazy" values that are evaluated when first accessed using `Thunk.get/map/bind`.
|
||||
The value is then stored and not recomputed for all further accesses. -/
|
||||
@@ -78,6 +104,8 @@ instance thunkCoe : CoeTail α (Thunk α) where
|
||||
abbrev Eq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : α → Sort u1} {b : α} (h : a = b) (m : motive a) : motive b :=
|
||||
Eq.ndrec m h
|
||||
|
||||
/-! # definitions -/
|
||||
|
||||
/--
|
||||
If and only if, or logical bi-implication. `a ↔ b` means that `a` implies `b` and vice versa.
|
||||
By `propext`, this implies that `a` and `b` are equal and hence any expression involving `a`
|
||||
@@ -126,6 +154,10 @@ inductive PSum (α : Sort u) (β : Sort v) where
|
||||
|
||||
@[inherit_doc] infixr:30 " ⊕' " => PSum
|
||||
|
||||
instance {α β} [Inhabited α] : Inhabited (PSum α β) := ⟨PSum.inl default⟩
|
||||
|
||||
instance {α β} [Inhabited β] : Inhabited (PSum α β) := ⟨PSum.inr default⟩
|
||||
|
||||
/--
|
||||
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
|
||||
whose first component is `a : α` and whose second component is `b : β a`
|
||||
@@ -342,6 +374,70 @@ class HasEquiv (α : Sort u) where
|
||||
|
||||
@[inherit_doc] infix:50 " ≈ " => HasEquiv.Equiv
|
||||
|
||||
/-! # set notation -/
|
||||
|
||||
/-- Notation type class for the subset relation `⊆`. -/
|
||||
class HasSubset (α : Type u) where
|
||||
/-- Subset relation: `a ⊆ b` -/
|
||||
Subset : α → α → Prop
|
||||
export HasSubset (Subset)
|
||||
|
||||
/-- Notation type class for the strict subset relation `⊂`. -/
|
||||
class HasSSubset (α : Type u) where
|
||||
/-- Strict subset relation: `a ⊂ b` -/
|
||||
SSubset : α → α → Prop
|
||||
export HasSSubset (SSubset)
|
||||
|
||||
/-- Superset relation: `a ⊇ b` -/
|
||||
abbrev Superset [HasSubset α] (a b : α) := Subset b a
|
||||
|
||||
/-- Strict superset relation: `a ⊃ b` -/
|
||||
abbrev SSuperset [HasSSubset α] (a b : α) := SSubset b a
|
||||
|
||||
/-- Notation type class for the union operation `∪`. -/
|
||||
class Union (α : Type u) where
|
||||
/-- `a ∪ b` is the union of`a` and `b`. -/
|
||||
union : α → α → α
|
||||
|
||||
/-- Notation type class for the intersection operation `∩`. -/
|
||||
class Inter (α : Type u) where
|
||||
/-- `a ∩ b` is the intersection of`a` and `b`. -/
|
||||
inter : α → α → α
|
||||
|
||||
/-- Notation type class for the set difference `\`. -/
|
||||
class SDiff (α : Type u) where
|
||||
/--
|
||||
`a \ b` is the set difference of `a` and `b`,
|
||||
consisting of all elements in `a` that are not in `b`.
|
||||
-/
|
||||
sdiff : α → α → α
|
||||
|
||||
/-- Subset relation: `a ⊆ b` -/
|
||||
infix:50 " ⊆ " => Subset
|
||||
|
||||
/-- Strict subset relation: `a ⊂ b` -/
|
||||
infix:50 " ⊂ " => SSubset
|
||||
|
||||
/-- Superset relation: `a ⊇ b` -/
|
||||
infix:50 " ⊇ " => Superset
|
||||
|
||||
/-- Strict superset relation: `a ⊃ b` -/
|
||||
infix:50 " ⊃ " => SSuperset
|
||||
|
||||
/-- `a ∪ b` is the union of`a` and `b`. -/
|
||||
infixl:65 " ∪ " => Union.union
|
||||
|
||||
/-- `a ∩ b` is the intersection of`a` and `b`. -/
|
||||
infixl:70 " ∩ " => Inter.inter
|
||||
|
||||
/--
|
||||
`a \ b` is the set difference of `a` and `b`,
|
||||
consisting of all elements in `a` that are not in `b`.
|
||||
-/
|
||||
infix:70 " \\ " => SDiff.sdiff
|
||||
|
||||
/-! # collections -/
|
||||
|
||||
/-- `EmptyCollection α` is the typeclass which supports the notation `∅`, also written as `{}`. -/
|
||||
class EmptyCollection (α : Type u) where
|
||||
/-- `∅` or `{}` is the empty set or empty collection.
|
||||
@@ -351,6 +447,36 @@ class EmptyCollection (α : Type u) where
|
||||
@[inherit_doc] notation "{" "}" => EmptyCollection.emptyCollection
|
||||
@[inherit_doc] notation "∅" => EmptyCollection.emptyCollection
|
||||
|
||||
/--
|
||||
Type class for the `insert` operation.
|
||||
Used to implement the `{ a, b, c }` syntax.
|
||||
-/
|
||||
class Insert (α : outParam <| Type u) (γ : Type v) where
|
||||
/-- `insert x xs` inserts the element `x` into the collection `xs`. -/
|
||||
insert : α → γ → γ
|
||||
export Insert (insert)
|
||||
|
||||
/--
|
||||
Type class for the `singleton` operation.
|
||||
Used to implement the `{ a, b, c }` syntax.
|
||||
-/
|
||||
class Singleton (α : outParam <| Type u) (β : Type v) where
|
||||
/-- `singleton x` is a collection with the single element `x` (notation: `{x}`). -/
|
||||
singleton : α → β
|
||||
export Singleton (singleton)
|
||||
|
||||
/-- `insert x ∅ = {x}` -/
|
||||
class IsLawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] :
|
||||
Prop where
|
||||
/-- `insert x ∅ = {x}` -/
|
||||
insert_emptyc_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export IsLawfulSingleton (insert_emptyc_eq)
|
||||
|
||||
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
|
||||
class Sep (α : outParam <| Type u) (γ : Type v) where
|
||||
/-- Computes `{ a ∈ c | p a }`. -/
|
||||
sep : (α → Prop) → γ → γ
|
||||
|
||||
/--
|
||||
`Task α` is a primitive for asynchronous computation.
|
||||
It represents a computation that will resolve to a value of type `α`,
|
||||
@@ -525,9 +651,7 @@ theorem not_not_intro {p : Prop} (h : p) : ¬ ¬ p :=
|
||||
fun hn : ¬ p => hn h
|
||||
|
||||
-- proof irrelevance is built in
|
||||
theorem proofIrrel {a : Prop} (h₁ h₂ : a) : h₁ = h₂ := rfl
|
||||
|
||||
theorem id.def {α : Sort u} (a : α) : id a = a := rfl
|
||||
theorem proof_irrel {a : Prop} (h₁ h₂ : a) : h₁ = h₂ := rfl
|
||||
|
||||
/--
|
||||
If `h : α = β` is a proof of type equality, then `h.mp : α → β` is the induced
|
||||
@@ -553,7 +677,7 @@ You can prove theorems about the resulting element by induction on `h`, since
|
||||
theorem Eq.substr {α : Sort u} {p : α → Prop} {a b : α} (h₁ : b = a) (h₂ : p a) : p b :=
|
||||
h₁ ▸ h₂
|
||||
|
||||
theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
|
||||
@[simp] theorem cast_eq {α : Sort u} (h : α = α) (a : α) : cast h a = a :=
|
||||
rfl
|
||||
|
||||
/--
|
||||
@@ -575,8 +699,9 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
|
||||
|
||||
theorem Ne.irrefl (h : a ≠ a) : False := h rfl
|
||||
|
||||
theorem Ne.symm (h : a ≠ b) : b ≠ a :=
|
||||
fun h₁ => h (h₁.symm)
|
||||
theorem Ne.symm (h : a ≠ b) : b ≠ a := fun h₁ => h (h₁.symm)
|
||||
|
||||
theorem ne_comm {α} {a b : α} : a ≠ b ↔ b ≠ a := ⟨Ne.symm, Ne.symm⟩
|
||||
|
||||
theorem false_of_ne : a ≠ a → False := Ne.irrefl
|
||||
|
||||
@@ -588,8 +713,8 @@ theorem ne_true_of_not : ¬p → p ≠ True :=
|
||||
have : ¬True := h ▸ hnp
|
||||
this trivial
|
||||
|
||||
theorem true_ne_false : ¬True = False :=
|
||||
ne_false_of_self trivial
|
||||
theorem true_ne_false : ¬True = False := ne_false_of_self trivial
|
||||
theorem false_ne_true : False ≠ True := fun h => h.symm ▸ trivial
|
||||
|
||||
end Ne
|
||||
|
||||
@@ -668,22 +793,29 @@ protected theorem Iff.rfl {a : Prop} : a ↔ a :=
|
||||
|
||||
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
|
||||
|
||||
theorem Iff.of_eq (h : a = b) : a ↔ b := h ▸ Iff.rfl
|
||||
|
||||
theorem Iff.trans (h₁ : a ↔ b) (h₂ : b ↔ c) : a ↔ c :=
|
||||
Iff.intro
|
||||
(fun ha => Iff.mp h₂ (Iff.mp h₁ ha))
|
||||
(fun hc => Iff.mpr h₁ (Iff.mpr h₂ hc))
|
||||
Iff.intro (h₂.mp ∘ h₁.mp) (h₁.mpr ∘ h₂.mpr)
|
||||
|
||||
theorem Iff.symm (h : a ↔ b) : b ↔ a :=
|
||||
Iff.intro (Iff.mpr h) (Iff.mp h)
|
||||
-- This is needed for `calc` to work with `iff`.
|
||||
instance : Trans Iff Iff Iff where
|
||||
trans := Iff.trans
|
||||
|
||||
theorem Iff.comm : (a ↔ b) ↔ (b ↔ a) :=
|
||||
Iff.intro Iff.symm Iff.symm
|
||||
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 Iff.of_eq (h : a = b) : a ↔ b :=
|
||||
h ▸ Iff.refl _
|
||||
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
|
||||
|
||||
theorem And.comm : a ∧ b ↔ b ∧ a := by
|
||||
constructor <;> intro ⟨h₁, h₂⟩ <;> exact ⟨h₂, h₁⟩
|
||||
theorem And.symm : a ∧ b → b ∧ a := fun ⟨ha, hb⟩ => ⟨hb, ha⟩
|
||||
theorem And.comm : a ∧ b ↔ b ∧ a := Iff.intro And.symm And.symm
|
||||
theorem and_comm : a ∧ b ↔ b ∧ a := And.comm
|
||||
|
||||
theorem Or.symm : a ∨ b → b ∨ a := .rec .inr .inl
|
||||
theorem Or.comm : a ∨ b ↔ b ∨ a := Iff.intro Or.symm Or.symm
|
||||
theorem or_comm : a ∨ b ↔ b ∨ a := Or.comm
|
||||
|
||||
/-! # Exists -/
|
||||
|
||||
@@ -883,8 +1015,13 @@ protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (
|
||||
apply heq_of_eq
|
||||
apply Subsingleton.elim
|
||||
|
||||
instance (p : Prop) : Subsingleton p :=
|
||||
⟨fun a b => proofIrrel a b⟩
|
||||
instance (p : Prop) : Subsingleton p := ⟨fun a b => proof_irrel a b⟩
|
||||
|
||||
instance : Subsingleton Empty := ⟨(·.elim)⟩
|
||||
instance : Subsingleton PEmpty := ⟨(·.elim)⟩
|
||||
|
||||
instance [Subsingleton α] [Subsingleton β] : Subsingleton (α × β) :=
|
||||
⟨fun {..} {..} => by congr <;> apply Subsingleton.elim⟩
|
||||
|
||||
instance (p : Prop) : Subsingleton (Decidable p) :=
|
||||
Subsingleton.intro fun
|
||||
@@ -895,6 +1032,9 @@ instance (p : Prop) : Subsingleton (Decidable p) :=
|
||||
| isTrue t₂ => absurd t₂ f₁
|
||||
| isFalse _ => rfl
|
||||
|
||||
example [Subsingleton α] (p : α → Prop) : Subsingleton (Subtype p) :=
|
||||
⟨fun ⟨x, _⟩ ⟨y, _⟩ => by congr; exact Subsingleton.elim x y⟩
|
||||
|
||||
theorem recSubsingleton
|
||||
{p : Prop} [h : Decidable p]
|
||||
{h₁ : p → Sort u}
|
||||
@@ -1174,12 +1314,117 @@ gen_injective_theorems% Lean.Syntax
|
||||
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] (a b : α) : a == b ↔ a = b :=
|
||||
⟨eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl⟩
|
||||
|
||||
/-! # Quotients -/
|
||||
/-! # Prop lemmas -/
|
||||
|
||||
/-- *Ex falso* for negation: from `¬a` and `a` anything follows. This is the same as `absurd` with
|
||||
the arguments flipped, but it is in the `Not` namespace so that projection notation can be used. -/
|
||||
def Not.elim {α : Sort _} (H1 : ¬a) (H2 : a) : α := absurd H2 H1
|
||||
|
||||
/-- Non-dependent eliminator for `And`. -/
|
||||
abbrev And.elim (f : a → b → α) (h : a ∧ b) : α := f h.left h.right
|
||||
|
||||
/-- Non-dependent eliminator for `Iff`. -/
|
||||
def Iff.elim (f : (a → b) → (b → a) → α) (h : a ↔ b) : α := f h.mp h.mpr
|
||||
|
||||
/-- Iff can now be used to do substitutions in a calculation -/
|
||||
theorem Iff.subst {a b : Prop} {p : Prop → Prop} (h₁ : a ↔ b) (h₂ : p a) : p b :=
|
||||
Eq.subst (propext h₁) h₂
|
||||
|
||||
theorem Not.intro {a : Prop} (h : a → False) : ¬a := h
|
||||
|
||||
theorem Not.imp {a b : Prop} (H2 : ¬b) (H1 : a → b) : ¬a := mt H1 H2
|
||||
|
||||
theorem not_congr (h : a ↔ b) : ¬a ↔ ¬b := ⟨mt h.2, mt h.1⟩
|
||||
|
||||
theorem not_not_not : ¬¬¬a ↔ ¬a := ⟨mt not_not_intro, not_not_intro⟩
|
||||
|
||||
theorem iff_of_true (ha : a) (hb : b) : a ↔ b := Iff.intro (fun _ => hb) (fun _ => ha)
|
||||
theorem iff_of_false (ha : ¬a) (hb : ¬b) : a ↔ b := Iff.intro ha.elim hb.elim
|
||||
|
||||
theorem iff_true_left (ha : a) : (a ↔ b) ↔ b := Iff.intro (·.mp ha) (iff_of_true ha)
|
||||
theorem iff_true_right (ha : a) : (b ↔ a) ↔ b := Iff.comm.trans (iff_true_left ha)
|
||||
|
||||
theorem iff_false_left (ha : ¬a) : (a ↔ b) ↔ ¬b := Iff.intro (mt ·.mpr ha) (iff_of_false ha)
|
||||
theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_false_left ha)
|
||||
|
||||
theorem of_iff_true (h : a ↔ True) : a := h.mpr trivial
|
||||
theorem iff_true_intro (h : a) : a ↔ True := iff_of_true h trivial
|
||||
|
||||
theorem not_of_iff_false : (p ↔ False) → ¬p := Iff.mp
|
||||
theorem iff_false_intro (h : ¬a) : a ↔ False := iff_of_false h id
|
||||
|
||||
theorem not_iff_false_intro (h : a) : ¬a ↔ False := iff_false_intro (not_not_intro h)
|
||||
theorem not_true : (¬True) ↔ False := iff_false_intro (not_not_intro trivial)
|
||||
|
||||
theorem not_false_iff : (¬False) ↔ True := iff_true_intro not_false
|
||||
|
||||
theorem Eq.to_iff : a = b → (a ↔ b) := Iff.of_eq
|
||||
theorem iff_of_eq : a = b → (a ↔ b) := Iff.of_eq
|
||||
theorem neq_of_not_iff : ¬(a ↔ b) → a ≠ b := mt Iff.of_eq
|
||||
|
||||
theorem iff_iff_eq : (a ↔ b) ↔ a = b := Iff.intro propext Iff.of_eq
|
||||
@[simp] theorem eq_iff_iff : (a = b) ↔ (a ↔ b) := iff_iff_eq.symm
|
||||
|
||||
theorem eq_self_iff_true (a : α) : a = a ↔ True := iff_true_intro rfl
|
||||
theorem ne_self_iff_false (a : α) : a ≠ a ↔ False := not_iff_false_intro rfl
|
||||
|
||||
theorem false_of_true_iff_false (h : True ↔ False) : False := h.mp trivial
|
||||
theorem false_of_true_eq_false (h : True = False) : False := false_of_true_iff_false (Iff.of_eq h)
|
||||
|
||||
theorem true_eq_false_of_false : False → (True = False) := False.elim
|
||||
|
||||
theorem iff_def : (a ↔ b) ↔ (a → b) ∧ (b → a) := iff_iff_implies_and_implies a b
|
||||
theorem iff_def' : (a ↔ b) ↔ (b → a) ∧ (a → b) := Iff.trans iff_def And.comm
|
||||
|
||||
theorem true_iff_false : (True ↔ False) ↔ False := iff_false_intro (·.mp True.intro)
|
||||
theorem false_iff_true : (False ↔ True) ↔ False := iff_false_intro (·.mpr True.intro)
|
||||
|
||||
theorem iff_not_self : ¬(a ↔ ¬a) | H => let f h := H.1 h h; f (H.2 f)
|
||||
theorem heq_self_iff_true (a : α) : HEq a a ↔ True := iff_true_intro HEq.rfl
|
||||
|
||||
/-! ## implies -/
|
||||
|
||||
theorem not_not_of_not_imp : ¬(a → b) → ¬¬a := mt Not.elim
|
||||
|
||||
theorem not_of_not_imp {a : Prop} : ¬(a → b) → ¬b := mt fun h _ => h
|
||||
|
||||
@[simp] theorem imp_not_self : (a → ¬a) ↔ ¬a := Iff.intro (fun h ha => h ha ha) (fun h _ => h)
|
||||
|
||||
theorem imp_intro {α β : Prop} (h : α) : β → α := fun _ => h
|
||||
|
||||
theorem imp_imp_imp {a b c d : Prop} (h₀ : c → a) (h₁ : b → d) : (a → b) → (c → d) := (h₁ ∘ · ∘ h₀)
|
||||
|
||||
theorem imp_iff_right {a : Prop} (ha : a) : (a → b) ↔ b := Iff.intro (· ha) (fun a _ => a)
|
||||
|
||||
-- This is not marked `@[simp]` because we have `implies_true : (α → True) = True`
|
||||
theorem imp_true_iff (α : Sort u) : (α → True) ↔ True := iff_true_intro (fun _ => trivial)
|
||||
|
||||
theorem false_imp_iff (a : Prop) : (False → a) ↔ True := iff_true_intro False.elim
|
||||
|
||||
theorem true_imp_iff (α : Prop) : (True → α) ↔ α := imp_iff_right True.intro
|
||||
|
||||
@[simp high] theorem imp_self : (a → a) ↔ True := iff_true_intro id
|
||||
|
||||
@[simp] theorem imp_false : (a → False) ↔ ¬a := Iff.rfl
|
||||
|
||||
theorem imp.swap : (a → b → c) ↔ (b → a → c) := Iff.intro flip flip
|
||||
|
||||
theorem imp_not_comm : (a → ¬b) ↔ (b → ¬a) := imp.swap
|
||||
|
||||
theorem imp_congr_left (h : a ↔ b) : (a → c) ↔ (b → c) := Iff.intro (· ∘ h.mpr) (· ∘ h.mp)
|
||||
|
||||
theorem imp_congr_right (h : a → (b ↔ c)) : (a → b) ↔ (a → c) :=
|
||||
Iff.intro (fun hab ha => (h ha).mp (hab ha)) (fun hcd ha => (h ha).mpr (hcd ha))
|
||||
|
||||
theorem imp_congr_ctx (h₁ : a ↔ c) (h₂ : c → (b ↔ d)) : (a → b) ↔ (c → d) :=
|
||||
Iff.trans (imp_congr_left h₁) (imp_congr_right h₂)
|
||||
|
||||
theorem imp_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : (a → b) ↔ (c → d) := imp_congr_ctx h₁ fun _ => h₂
|
||||
|
||||
theorem imp_iff_not (hb : ¬b) : a → b ↔ ¬a := imp_congr_right fun _ => iff_false_intro hb
|
||||
|
||||
/-! # Quotients -/
|
||||
|
||||
namespace Quot
|
||||
/--
|
||||
The **quotient axiom**, or at least the nontrivial part of the quotient
|
||||
@@ -1687,6 +1932,18 @@ axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
|
||||
|
||||
end Lean
|
||||
|
||||
@[simp] theorem ge_iff_le [LE α] {x y : α} : x ≥ y ↔ y ≤ x := Iff.rfl
|
||||
|
||||
@[simp] theorem gt_iff_lt [LT α] {x y : α} : x > y ↔ y < x := Iff.rfl
|
||||
|
||||
theorem le_of_eq_of_le {a b c : α} [LE α] (h₁ : a = b) (h₂ : b ≤ c) : a ≤ c := h₁ ▸ h₂
|
||||
|
||||
theorem le_of_le_of_eq {a b c : α} [LE α] (h₁ : a ≤ b) (h₂ : b = c) : a ≤ c := h₂ ▸ h₁
|
||||
|
||||
theorem lt_of_eq_of_lt {a b c : α} [LT α] (h₁ : a = b) (h₂ : b < c) : a < c := h₁ ▸ h₂
|
||||
|
||||
theorem lt_of_lt_of_eq {a b c : α} [LT α] (h₁ : a < b) (h₂ : b = c) : a < c := h₂ ▸ h₁
|
||||
|
||||
namespace Std
|
||||
variable {α : Sort u}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Basic
|
||||
import Init.Data.Nat
|
||||
import Init.Data.Bool
|
||||
import Init.Data.BitVec
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Char
|
||||
import Init.Data.String
|
||||
import Init.Data.List
|
||||
@@ -29,3 +32,5 @@ import Init.Data.Prod
|
||||
import Init.Data.AC
|
||||
import Init.Data.Queue
|
||||
import Init.Data.Channel
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
|
||||
@@ -11,3 +11,4 @@ import Init.Data.Array.InsertionSort
|
||||
import Init.Data.Array.DecidableEq
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.BasicAux
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
@@ -5,7 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Classical
|
||||
import Init.ByCases
|
||||
|
||||
namespace Array
|
||||
|
||||
|
||||
268
src/Init/Data/Array/Lemmas.lean
Normal file
268
src/Init/Data/Array/Lemmas.lean
Normal file
@@ -0,0 +1,268 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Array.Mem
|
||||
|
||||
/-!
|
||||
## Bootstrapping theorems about arrays
|
||||
|
||||
This file contains some theorems about `Array` and `List` needed for `Std.List.Basic`.
|
||||
-/
|
||||
|
||||
namespace Array
|
||||
|
||||
attribute [simp] data_toArray uset
|
||||
|
||||
@[simp] theorem mkEmpty_eq (α n) : @mkEmpty α n = #[] := rfl
|
||||
|
||||
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
|
||||
|
||||
@[simp] theorem size_mk (as : List α) : (Array.mk as).size = as.length := by simp [size]
|
||||
|
||||
theorem getElem_eq_data_get (a : Array α) (h : i < a.size) : a[i] = a.data.get ⟨i, h⟩ := by
|
||||
by_cases i < a.size <;> (try simp [*]) <;> rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_data.aux [Monad m]
|
||||
(f : β → α → m β) (arr : Array α) (i j) (H : arr.size ≤ i + j) (b) :
|
||||
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.data.drop j).foldlM f b := by
|
||||
unfold foldlM.loop
|
||||
split; split
|
||||
· cases Nat.not_le_of_gt ‹_› (Nat.zero_add _ ▸ H)
|
||||
· rename_i i; rw [Nat.succ_add] at H
|
||||
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
|
||||
rw (config := {occs := .pos [2]}) [← List.get_drop_eq_drop _ _ ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_data [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.foldlM f init = arr.data.foldlM f init := by
|
||||
simp [foldlM, foldlM_eq_foldlM_data.aux]
|
||||
|
||||
theorem foldl_eq_foldl_data (f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.foldl f init = arr.data.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_eq_foldlM_data ..
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_data.aux [Monad m]
|
||||
(f : α → β → m β) (arr : Array α) (init : β) (i h) :
|
||||
(arr.data.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
|
||||
unfold foldrM.fold
|
||||
match i with
|
||||
| 0 => simp [List.foldlM, List.take]
|
||||
| i+1 => rw [← List.take_concat_get _ _ h]; simp [← (aux f arr · i)]; rfl
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_data [Monad m] (f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.data.reverse.foldlM (fun x y => f y x) init := by
|
||||
have : arr = #[] ∨ 0 < arr.size :=
|
||||
match arr with | ⟨[]⟩ => .inl rfl | ⟨a::l⟩ => .inr (Nat.zero_lt_succ _)
|
||||
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
|
||||
simp [foldrM, h, ← foldrM_eq_reverse_foldlM_data.aux, List.take_length]
|
||||
|
||||
theorem foldrM_eq_foldrM_data [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.data.foldrM f init := by
|
||||
rw [foldrM_eq_reverse_foldlM_data, List.foldlM_reverse]
|
||||
|
||||
theorem foldr_eq_foldr_data (f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.foldr f init = arr.data.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_eq_foldrM_data ..
|
||||
|
||||
@[simp] theorem push_data (arr : Array α) (a : α) : (arr.push a).data = arr.data ++ [a] := by
|
||||
simp [push, List.concat_eq_append]
|
||||
|
||||
theorem foldrM_push [Monad m] (f : α → β → m β) (init : β) (arr : Array α) (a : α) :
|
||||
(arr.push a).foldrM f init = f a init >>= arr.foldrM f := by
|
||||
simp [foldrM_eq_reverse_foldlM_data, -size_push]
|
||||
|
||||
@[simp] theorem foldrM_push' [Monad m] (f : α → β → m β) (init : β) (arr : Array α) (a : α) :
|
||||
(arr.push a).foldrM f init (start := arr.size + 1) = f a init >>= arr.foldrM f := by
|
||||
simp [← foldrM_push]
|
||||
|
||||
theorem foldr_push (f : α → β → β) (init : β) (arr : Array α) (a : α) :
|
||||
(arr.push a).foldr f init = arr.foldr f (f a init) := foldrM_push ..
|
||||
|
||||
@[simp] theorem foldr_push' (f : α → β → β) (init : β) (arr : Array α) (a : α) :
|
||||
(arr.push a).foldr f init (start := arr.size + 1) = arr.foldr f (f a init) := foldrM_push' ..
|
||||
|
||||
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.data ++ l := by
|
||||
simp [toListAppend, foldr_eq_foldr_data]
|
||||
|
||||
@[simp] theorem toList_eq (arr : Array α) : arr.toList = arr.data := by
|
||||
simp [toList, foldr_eq_foldr_data]
|
||||
|
||||
/-- A more efficient version of `arr.toList.reverse`. -/
|
||||
@[inline] def toListRev (arr : Array α) : List α := arr.foldl (fun l t => t :: l) []
|
||||
|
||||
@[simp] theorem toListRev_eq (arr : Array α) : arr.toListRev = arr.data.reverse := by
|
||||
rw [toListRev, foldl_eq_foldl_data, ← List.foldr_reverse, List.foldr_self]
|
||||
|
||||
theorem get_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
|
||||
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
|
||||
(a.push x)[i] = a[i] := by
|
||||
simp only [push, getElem_eq_data_get, List.concat_eq_append, List.get_append_left, h]
|
||||
|
||||
@[simp] theorem get_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
|
||||
simp only [push, getElem_eq_data_get, List.concat_eq_append]
|
||||
rw [List.get_append_right] <;> simp [getElem_eq_data_get, Nat.zero_lt_one]
|
||||
|
||||
theorem get_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
|
||||
(a.push x)[i] = if h : i < a.size then a[i] else x := by
|
||||
by_cases h' : i < a.size
|
||||
· simp [get_push_lt, h']
|
||||
· simp at h
|
||||
simp [get_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
|
||||
|
||||
theorem mapM_eq_foldlM [Monad m] [LawfulMonad m] (f : α → m β) (arr : Array α) :
|
||||
arr.mapM f = arr.foldlM (fun bs a => bs.push <$> f a) #[] := by
|
||||
rw [mapM, aux, foldlM_eq_foldlM_data]; rfl
|
||||
where
|
||||
aux (i r) :
|
||||
mapM.map f arr i r = (arr.data.drop i).foldlM (fun bs a => bs.push <$> f a) r := by
|
||||
unfold mapM.map; split
|
||||
· rw [← List.get_drop_eq_drop _ i ‹_›]
|
||||
simp [aux (i+1), map_eq_pure_bind]; rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
termination_by arr.size - i
|
||||
|
||||
@[simp] theorem map_data (f : α → β) (arr : Array α) : (arr.map f).data = arr.data.map f := by
|
||||
rw [map, mapM_eq_foldlM]
|
||||
apply congrArg data (foldl_eq_foldl_data (fun bs a => push bs (f a)) #[] arr) |>.trans
|
||||
have H (l arr) : List.foldl (fun bs a => push bs (f a)) arr l = ⟨arr.data ++ l.map f⟩ := by
|
||||
induction l generalizing arr <;> simp [*]
|
||||
simp [H]
|
||||
|
||||
@[simp] theorem size_map (f : α → β) (arr : Array α) : (arr.map f).size = arr.size := by
|
||||
simp [size]
|
||||
|
||||
@[simp] theorem pop_data (arr : Array α) : arr.pop.data = arr.data.dropLast := rfl
|
||||
|
||||
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
|
||||
|
||||
@[simp] theorem append_data (arr arr' : Array α) :
|
||||
(arr ++ arr').data = arr.data ++ arr'.data := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [foldl_eq_foldl_data]
|
||||
induction arr'.data generalizing arr <;> simp [*]
|
||||
|
||||
@[simp] theorem appendList_eq_append
|
||||
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
|
||||
|
||||
@[simp] theorem appendList_data (arr : Array α) (l : List α) :
|
||||
(arr ++ l).data = arr.data ++ l := by
|
||||
rw [← appendList_eq_append]; unfold Array.appendList
|
||||
induction l generalizing arr <;> simp [*]
|
||||
|
||||
@[simp] theorem appendList_nil (arr : Array α) : arr ++ ([] : List α) = arr := Array.ext' (by simp)
|
||||
|
||||
@[simp] theorem appendList_cons (arr : Array α) (a : α) (l : List α) :
|
||||
arr ++ (a :: l) = arr.push a ++ l := Array.ext' (by simp)
|
||||
|
||||
theorem foldl_data_eq_bind (l : List α) (acc : Array β)
|
||||
(F : Array β → α → Array β) (G : α → List β)
|
||||
(H : ∀ acc a, (F acc a).data = acc.data ++ G a) :
|
||||
(l.foldl F acc).data = acc.data ++ l.bind G := by
|
||||
induction l generalizing acc <;> simp [*, List.bind]
|
||||
|
||||
theorem foldl_data_eq_map (l : List α) (acc : Array β) (G : α → β) :
|
||||
(l.foldl (fun acc a => acc.push (G a)) acc).data = acc.data ++ l.map G := by
|
||||
induction l generalizing acc <;> simp [*]
|
||||
|
||||
theorem size_uset (a : Array α) (v i h) : (uset a i v h).size = a.size := by simp
|
||||
|
||||
theorem anyM_eq_anyM_loop [Monad m] (p : α → m Bool) (as : Array α) (start stop) :
|
||||
anyM p as start stop = anyM.loop p as (min stop as.size) (Nat.min_le_right ..) start := by
|
||||
simp only [anyM, Nat.min_def]; split <;> rfl
|
||||
|
||||
theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start stop)
|
||||
(h : min stop as.size ≤ start) : anyM p as start stop = pure false := by
|
||||
rw [anyM_eq_anyM_loop, anyM.loop, dif_neg (Nat.not_lt.2 h)]
|
||||
|
||||
theorem mem_def (a : α) (as : Array α) : a ∈ as ↔ a ∈ as.data :=
|
||||
⟨fun | .mk h => h, Array.Mem.mk⟩
|
||||
|
||||
/-- # get -/
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
|
||||
|
||||
theorem getElem?_lt
|
||||
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some (a[i]) := dif_pos h
|
||||
|
||||
theorem getElem?_ge
|
||||
(a : Array α) {i : Nat} (h : i ≥ a.size) : a[i]? = none := dif_neg (Nat.not_lt_of_le h)
|
||||
|
||||
@[simp] theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
|
||||
|
||||
theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size ≤ i) : a[i]? = none := by
|
||||
simp [getElem?_ge, h]
|
||||
|
||||
theorem getD_get? (a : Array α) (i : Nat) (d : α) :
|
||||
Option.getD a[i]? d = if p : i < a.size then a[i]'p else d := by
|
||||
if h : i < a.size then
|
||||
simp [setD, h, getElem?]
|
||||
else
|
||||
have p : i ≥ a.size := Nat.le_of_not_gt h
|
||||
simp [setD, getElem?_len_le _ p, h]
|
||||
|
||||
@[simp] theorem getD_eq_get? (a : Array α) (n d) : a.getD n d = (a[n]?).getD d := by
|
||||
simp only [getD, get_eq_getElem, get?_eq_getElem?]; split <;> simp [getD_get?, *]
|
||||
|
||||
theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default := rfl
|
||||
|
||||
@[simp] theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) : a.get! i = (a.get? i).getD default := by
|
||||
by_cases p : i < a.size <;> simp [getD_get?, get!_eq_getD, p]
|
||||
|
||||
/-- # set -/
|
||||
|
||||
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
|
||||
(eq : i.val = j) (p : j < (a.set i v).size) :
|
||||
(a.set i v)[j]'p = v := by
|
||||
simp [set, getElem_eq_data_get, ←eq]
|
||||
|
||||
@[simp] theorem getElem_set_ne (a : Array α) (i : Fin a.size) (v : α) {j : Nat} (pj : j < (a.set i v).size)
|
||||
(h : i.val ≠ j) : (a.set i v)[j]'pj = a[j]'(size_set a i v ▸ pj) := by
|
||||
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
|
||||
|
||||
theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
|
||||
(h : j < (a.set i v).size) :
|
||||
(a.set i v)[j]'h = if i = j then v else a[j]'(size_set a i v ▸ h) := by
|
||||
by_cases p : i.1 = j <;> simp [p]
|
||||
|
||||
@[simp] theorem getElem?_set_eq (a : Array α) (i : Fin a.size) (v : α) :
|
||||
(a.set i v)[i.1]? = v := by simp [getElem?_lt, i.2]
|
||||
|
||||
@[simp] theorem getElem?_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α)
|
||||
(ne : i.val ≠ j) : (a.set i v)[j]? = a[j]? := by
|
||||
by_cases h : j < a.size <;> simp [getElem?_lt, getElem?_ge, Nat.ge_of_not_lt, ne, h]
|
||||
|
||||
/- # setD -/
|
||||
|
||||
@[simp] theorem set!_is_setD : @set! = @setD := rfl
|
||||
|
||||
@[simp] theorem size_setD (a : Array α) (index : Nat) (val : α) :
|
||||
(Array.setD a index val).size = a.size := by
|
||||
if h : index < a.size then
|
||||
simp [setD, h]
|
||||
else
|
||||
simp [setD, h]
|
||||
|
||||
@[simp] theorem getElem_setD_eq (a : Array α) {i : Nat} (v : α) (h : _) :
|
||||
(setD a i v)[i]'h = v := by
|
||||
simp at h
|
||||
simp only [setD, h, dite_true, getElem_set, ite_true]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_setD_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) : (a.setD i v)[i]? = some v := by
|
||||
simp [getElem?_lt, p]
|
||||
|
||||
/-- Simplifies a normal form from `get!` -/
|
||||
@[simp] theorem getD_get?_setD (a : Array α) (i : Nat) (v d : α) :
|
||||
Option.getD (setD a i v)[i]? d = if i < a.size then v else d := by
|
||||
by_cases h : i < a.size <;>
|
||||
simp [setD, Nat.not_lt_of_le, h, getD_get?]
|
||||
|
||||
end Array
|
||||
@@ -8,16 +8,6 @@ import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Data.List.BasicAux
|
||||
|
||||
theorem List.sizeOf_get_lt [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
|
||||
match as, i with
|
||||
| [], i => apply Fin.elim0 i
|
||||
| a::as, ⟨0, _⟩ => simp_arith [get]
|
||||
| a::as, ⟨i+1, h⟩ =>
|
||||
simp [get]
|
||||
have h : i < as.length := Nat.lt_of_succ_lt_succ h
|
||||
have ih := sizeOf_get_lt as ⟨i, h⟩
|
||||
exact Nat.lt_of_lt_of_le ih (Nat.le_add_left ..)
|
||||
|
||||
namespace Array
|
||||
|
||||
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
|
||||
@@ -29,10 +19,6 @@ structure Mem (a : α) (as : Array α) : Prop where
|
||||
instance : Membership α (Array α) where
|
||||
mem a as := Mem a as
|
||||
|
||||
theorem sizeOf_get_lt [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_get_lt as i) (by simp_arith)
|
||||
|
||||
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
|
||||
|
||||
@@ -143,6 +143,7 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
|
||||
else
|
||||
{ as := as, start := as.size, stop := as.size, h₁ := Nat.le_refl _, h₂ := Nat.le_refl _ }
|
||||
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
let mut as := mkEmpty (s.stop - s.start)
|
||||
for a in s do
|
||||
|
||||
10
src/Init/Data/BitVec.lean
Normal file
10
src/Init/Data/BitVec.lean
Normal file
@@ -0,0 +1,10 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Basic
|
||||
import Init.Data.BitVec.Bitblast
|
||||
import Init.Data.BitVec.Folds
|
||||
import Init.Data.BitVec.Lemmas
|
||||
617
src/Init/Data/BitVec/Basic.lean
Normal file
617
src/Init/Data/BitVec/Basic.lean
Normal file
@@ -0,0 +1,617 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Nat.Bitwise.Lemmas
|
||||
import Init.Data.Nat.Power2
|
||||
|
||||
/-!
|
||||
We define bitvectors. We choose the `Fin` representation over others for its relative efficiency
|
||||
(Lean has special support for `Nat`), alignment with `UIntXY` types which are also represented
|
||||
with `Fin`, and the fact that bitwise operations on `Fin` are already defined. Some other possible
|
||||
representations are `List Bool`, `{ l : List Bool // l.length = w }`, `Fin w → Bool`.
|
||||
|
||||
We define many of the bitvector operations from the
|
||||
[`QF_BV` logic](https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV).
|
||||
of SMT-LIBv2.
|
||||
-/
|
||||
|
||||
/--
|
||||
A bitvector of the specified width.
|
||||
|
||||
This is represented as the underlying `Nat` number in both the runtime
|
||||
and the kernel, inheriting all the special support for `Nat`.
|
||||
-/
|
||||
structure BitVec (w : Nat) where
|
||||
/-- Construct a `BitVec w` from a number less than `2^w`.
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector. -/
|
||||
ofFin ::
|
||||
/-- Interpret a bitvector as a number less than `2^w`.
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector. -/
|
||||
toFin : Fin (2^w)
|
||||
|
||||
@[deprecated] abbrev Std.BitVec := _root_.BitVec
|
||||
|
||||
-- We manually derive the `DecidableEq` instances for `BitVec` because
|
||||
-- we want to have builtin support for bit-vector literals, and we
|
||||
-- need a name for this function to implement `canUnfoldAtMatcher` at `WHNF.lean`.
|
||||
def BitVec.decEq (a b : BitVec n) : Decidable (a = b) :=
|
||||
match a, b with
|
||||
| ⟨n⟩, ⟨m⟩ =>
|
||||
if h : n = m then
|
||||
isTrue (h ▸ rfl)
|
||||
else
|
||||
isFalse (fun h' => BitVec.noConfusion h' (fun h' => absurd h' h))
|
||||
|
||||
instance : DecidableEq (BitVec n) := BitVec.decEq
|
||||
|
||||
namespace BitVec
|
||||
|
||||
section Nat
|
||||
|
||||
/-- The `BitVec` with value `i`, given a proof that `i < 2^n`. -/
|
||||
@[match_pattern]
|
||||
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
|
||||
toFin := ⟨i, p⟩
|
||||
|
||||
/-- The `BitVec` with value `i mod 2^n`. -/
|
||||
@[match_pattern]
|
||||
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
|
||||
toFin := Fin.ofNat' i (Nat.two_pow_pos n)
|
||||
|
||||
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
|
||||
instance natCastInst : NatCast (BitVec w) := ⟨BitVec.ofNat w⟩
|
||||
|
||||
/-- Given a bitvector `a`, return the underlying `Nat`. This is O(1) because `BitVec` is a
|
||||
(zero-cost) wrapper around a `Nat`. -/
|
||||
protected def toNat (a : BitVec n) : Nat := a.toFin.val
|
||||
|
||||
/-- Return the bound in terms of toNat. -/
|
||||
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt
|
||||
|
||||
/-- Theorem for normalizing the bit vector literal representation. -/
|
||||
-- TODO: This needs more usage data to assess which direction the simp should go.
|
||||
@[simp, bv_toNat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
|
||||
|
||||
-- Note. Mathlib would like this to go the other direction.
|
||||
@[simp] theorem natCast_eq_ofNat (w x : Nat) : @Nat.cast (BitVec w) _ x = .ofNat w x := rfl
|
||||
|
||||
end Nat
|
||||
|
||||
section subsingleton
|
||||
|
||||
/-- All empty bitvectors are equal -/
|
||||
instance : Subsingleton (BitVec 0) where
|
||||
allEq := by intro ⟨0, _⟩ ⟨0, _⟩; rfl
|
||||
|
||||
/-- The empty bitvector -/
|
||||
abbrev nil : BitVec 0 := 0
|
||||
|
||||
/-- Every bitvector of length 0 is equal to `nil`, i.e., there is only one empty bitvector -/
|
||||
theorem eq_nil (x : BitVec 0) : x = nil := Subsingleton.allEq ..
|
||||
|
||||
end subsingleton
|
||||
|
||||
section zero_allOnes
|
||||
|
||||
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
|
||||
protected def zero (n : Nat) : BitVec n := .ofNatLt 0 (Nat.two_pow_pos n)
|
||||
instance : Inhabited (BitVec n) where default := .zero n
|
||||
|
||||
/-- Bit vector of size `n` where all bits are `1`s -/
|
||||
def allOnes (n : Nat) : BitVec n :=
|
||||
.ofNatLt (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
|
||||
|
||||
end zero_allOnes
|
||||
|
||||
section getXsb
|
||||
|
||||
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getLsb (x : BitVec w) (i : Nat) : Bool := x.toNat.testBit i
|
||||
|
||||
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getMsb (x : BitVec w) (i : Nat) : Bool := i < w && getLsb x (w-1-i)
|
||||
|
||||
/-- Return most-significant bit in bitvector. -/
|
||||
@[inline] protected def msb (a : BitVec n) : Bool := getMsb a 0
|
||||
|
||||
end getXsb
|
||||
|
||||
section Int
|
||||
|
||||
/-- Interpret the bitvector as an integer stored in two's complement form. -/
|
||||
protected def toInt (a : BitVec n) : Int :=
|
||||
if 2 * a.toNat < 2^n then
|
||||
a.toNat
|
||||
else
|
||||
(a.toNat : Int) - (2^n : Nat)
|
||||
|
||||
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
|
||||
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
|
||||
apply (Int.toNat_lt _).mpr
|
||||
· apply Int.emod_lt_of_pos
|
||||
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
|
||||
· apply Int.emod_nonneg
|
||||
intro eq
|
||||
apply Nat.ne_of_gt (Nat.two_pow_pos n)
|
||||
exact Int.ofNat_inj.mp eq)
|
||||
|
||||
instance : IntCast (BitVec w) := ⟨BitVec.ofInt w⟩
|
||||
|
||||
end Int
|
||||
|
||||
section Syntax
|
||||
|
||||
/-- Notation for bit vector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
|
||||
scoped syntax:max term:max noWs "#" noWs term:max : term
|
||||
macro_rules | `($i#$n) => `(BitVec.ofNat $n $i)
|
||||
|
||||
/-- Unexpander for bit vector literals. -/
|
||||
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $n $i) => `($i#$n)
|
||||
| _ => throw ()
|
||||
|
||||
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLt i lt`. -/
|
||||
scoped syntax:max term:max noWs "#'" noWs term:max : term
|
||||
macro_rules | `($i#'$p) => `(BitVec.ofNatLt $i $p)
|
||||
|
||||
/-- Unexpander for bit vector literals without truncation. -/
|
||||
@[app_unexpander BitVec.ofNatLt] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $i $p) => `($i#'$p)
|
||||
| _ => throw ()
|
||||
|
||||
end Syntax
|
||||
|
||||
section repr_toString
|
||||
|
||||
/-- Convert bitvector into a fixed-width hex number. -/
|
||||
protected def toHex {n : Nat} (x : BitVec n) : String :=
|
||||
let s := (Nat.toDigits 16 x.toNat).asString
|
||||
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
|
||||
t ++ s
|
||||
|
||||
instance : Repr (BitVec n) where reprPrec a _ := "0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
|
||||
instance : ToString (BitVec n) where toString a := toString (repr a)
|
||||
|
||||
end repr_toString
|
||||
|
||||
section arithmetic
|
||||
|
||||
/--
|
||||
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvadd`.
|
||||
-/
|
||||
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
|
||||
instance : Add (BitVec n) := ⟨BitVec.add⟩
|
||||
|
||||
/--
|
||||
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
|
||||
modulo `2^n`.
|
||||
-/
|
||||
protected def sub (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + (2^n - y.toNat))
|
||||
instance : Sub (BitVec n) := ⟨BitVec.sub⟩
|
||||
|
||||
/--
|
||||
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvneg`.
|
||||
-/
|
||||
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
|
||||
instance : Neg (BitVec n) := ⟨.neg⟩
|
||||
|
||||
/--
|
||||
Return the absolute value of a signed bitvector.
|
||||
-/
|
||||
protected def abs (s : BitVec n) : BitVec n := if s.msb then .neg s else s
|
||||
|
||||
/--
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvmul`.
|
||||
-/
|
||||
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
|
||||
instance : Mul (BitVec n) := ⟨.mul⟩
|
||||
|
||||
/--
|
||||
Unsigned division for bit vectors using the Lean convention where division by zero returns zero.
|
||||
-/
|
||||
def udiv (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat / y.toNat)#'(Nat.lt_of_le_of_lt (Nat.div_le_self _ _) x.isLt)
|
||||
instance : Div (BitVec n) := ⟨.udiv⟩
|
||||
|
||||
/--
|
||||
Unsigned modulo for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvurem`.
|
||||
-/
|
||||
def umod (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
|
||||
instance : Mod (BitVec n) := ⟨.umod⟩
|
||||
|
||||
/--
|
||||
Unsigned division for bit vectors using the
|
||||
[SMT-Lib convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
|
||||
where division by zero returns the `allOnes` bitvector.
|
||||
|
||||
SMT-Lib name: `bvudiv`.
|
||||
-/
|
||||
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
|
||||
|
||||
/--
|
||||
Signed t-division for bit vectors using the Lean convention where division
|
||||
by zero returns zero.
|
||||
|
||||
```lean
|
||||
sdiv 7#4 2 = 3#4
|
||||
sdiv (-9#4) 2 = -4#4
|
||||
sdiv 5#4 -2 = -2#4
|
||||
sdiv (-7#4) (-2) = 3#4
|
||||
```
|
||||
-/
|
||||
def sdiv (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => udiv s t
|
||||
| false, true => .neg (udiv s (.neg t))
|
||||
| true, false => .neg (udiv (.neg s) t)
|
||||
| true, true => udiv (.neg s) (.neg t)
|
||||
|
||||
/--
|
||||
Signed division for bit vectors using SMTLIB rules for division by zero.
|
||||
|
||||
Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
|
||||
|
||||
SMT-Lib name: `bvsdiv`.
|
||||
-/
|
||||
def smtSDiv (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => smtUDiv s t
|
||||
| false, true => .neg (smtUDiv s (.neg t))
|
||||
| true, false => .neg (smtUDiv (.neg s) t)
|
||||
| true, true => smtUDiv (.neg s) (.neg t)
|
||||
|
||||
/--
|
||||
Remainder for signed division rounding to zero.
|
||||
|
||||
SMT_Lib name: `bvsrem`.
|
||||
-/
|
||||
def srem (s t : BitVec n) : BitVec n :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => umod s t
|
||||
| false, true => umod s (.neg t)
|
||||
| true, false => .neg (umod (.neg s) t)
|
||||
| true, true => .neg (umod (.neg s) (.neg t))
|
||||
|
||||
/--
|
||||
Remainder for signed division rounded to negative infinity.
|
||||
|
||||
SMT_Lib name: `bvsmod`.
|
||||
-/
|
||||
def smod (s t : BitVec m) : BitVec m :=
|
||||
match s.msb, t.msb with
|
||||
| false, false => umod s t
|
||||
| false, true =>
|
||||
let u := umod s (.neg t)
|
||||
(if u = .zero m then u else .add u t)
|
||||
| true, false =>
|
||||
let u := umod (.neg s) t
|
||||
(if u = .zero m then u else .sub t u)
|
||||
| true, true => .neg (umod (.neg s) (.neg t))
|
||||
|
||||
end arithmetic
|
||||
|
||||
|
||||
section bool
|
||||
|
||||
/-- Turn a `Bool` into a bitvector of length `1` -/
|
||||
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
|
||||
|
||||
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
|
||||
@[simp] theorem ofBool_true : ofBool true = 1 := by trivial
|
||||
|
||||
/-- Fills a bitvector with `w` copies of the bit `b`. -/
|
||||
def fill (w : Nat) (b : Bool) : BitVec w := bif b then -1 else 0
|
||||
|
||||
end bool
|
||||
|
||||
section relations
|
||||
|
||||
/--
|
||||
Unsigned less-than for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvult`.
|
||||
-/
|
||||
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
|
||||
|
||||
instance : LT (BitVec n) where lt := (·.toNat < ·.toNat)
|
||||
instance (x y : BitVec n) : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.toNat < y.toNat))
|
||||
|
||||
/--
|
||||
Unsigned less-than-or-equal-to for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvule`.
|
||||
-/
|
||||
protected def ule (x y : BitVec n) : Bool := x.toNat ≤ y.toNat
|
||||
|
||||
instance : LE (BitVec n) where le := (·.toNat ≤ ·.toNat)
|
||||
instance (x y : BitVec n) : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.toNat ≤ y.toNat))
|
||||
|
||||
/--
|
||||
Signed less-than for bit vectors.
|
||||
|
||||
```lean
|
||||
BitVec.slt 6#4 7 = true
|
||||
BitVec.slt 7#4 8 = false
|
||||
```
|
||||
SMT-Lib name: `bvslt`.
|
||||
-/
|
||||
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
|
||||
|
||||
/--
|
||||
Signed less-than-or-equal-to for bit vectors.
|
||||
|
||||
SMT-Lib name: `bvsle`.
|
||||
-/
|
||||
protected def sle (x y : BitVec n) : Bool := x.toInt ≤ y.toInt
|
||||
|
||||
end relations
|
||||
|
||||
section cast
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `BitVec` type. -/
|
||||
@[inline] def cast (eq : n = m) (i : BitVec n) : BitVec m := .ofNatLt i.toNat (eq ▸ i.isLt)
|
||||
|
||||
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
|
||||
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
|
||||
subst h; rfl
|
||||
|
||||
@[simp] theorem cast_cast {n m k : Nat} (h₁ : n = m) (h₂ : m = k) (x : BitVec n) :
|
||||
cast h₂ (cast h₁ x) = cast (h₁ ▸ h₂) x :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : cast h x = x := rfl
|
||||
|
||||
/--
|
||||
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
|
||||
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
|
||||
high bits.
|
||||
-/
|
||||
def extractLsb' (start len : Nat) (a : BitVec n) : BitVec len := .ofNat _ (a.toNat >>> start)
|
||||
|
||||
/--
|
||||
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
|
||||
yield a new bitvector of size `hi - lo + 1`.
|
||||
|
||||
SMT-Lib name: `extract`.
|
||||
-/
|
||||
def extractLsb (hi lo : Nat) (a : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ a
|
||||
|
||||
/--
|
||||
A version of `zeroExtend` that requires a proof, but is a noop.
|
||||
-/
|
||||
def zeroExtend' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
|
||||
x.toNat#'(by
|
||||
apply Nat.lt_of_lt_of_le x.isLt
|
||||
exact Nat.pow_le_pow_of_le_right (by trivial) le)
|
||||
|
||||
/--
|
||||
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
|
||||
needing to compute `x % 2^(2+n)`.
|
||||
-/
|
||||
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w+m) :=
|
||||
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w+m) := by
|
||||
simp [Nat.shiftLeft_eq, Nat.pow_add]
|
||||
apply Nat.mul_lt_mul_of_pos_right p
|
||||
exact (Nat.two_pow_pos m)
|
||||
(msbs.toNat <<< m)#'(shiftLeftLt msbs.isLt m)
|
||||
|
||||
/--
|
||||
Zero extend vector `x` of length `w` by adding zeros in the high bits until it has length `v`.
|
||||
If `v < w` then it truncates the high bits instead.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
-/
|
||||
def zeroExtend (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
if h : w ≤ v then
|
||||
zeroExtend' h x
|
||||
else
|
||||
.ofNat v x.toNat
|
||||
|
||||
/--
|
||||
Truncate the high bits of bitvector `x` of length `w`, resulting in a vector of length `v`.
|
||||
If `v > w` then it zero-extends the vector instead.
|
||||
-/
|
||||
abbrev truncate := @zeroExtend
|
||||
|
||||
/--
|
||||
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
|
||||
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
|
||||
|
||||
SMT-Lib name: `sign_extend`.
|
||||
-/
|
||||
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
|
||||
|
||||
end cast
|
||||
|
||||
section bitwise
|
||||
|
||||
/--
|
||||
Bitwise AND for bit vectors.
|
||||
|
||||
```lean
|
||||
0b1010#4 &&& 0b0110#4 = 0b0010#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvand`.
|
||||
-/
|
||||
protected def and (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
|
||||
instance : AndOp (BitVec w) := ⟨.and⟩
|
||||
|
||||
/--
|
||||
Bitwise OR for bit vectors.
|
||||
|
||||
```lean
|
||||
0b1010#4 ||| 0b0110#4 = 0b1110#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvor`.
|
||||
-/
|
||||
protected def or (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
|
||||
instance : OrOp (BitVec w) := ⟨.or⟩
|
||||
|
||||
/--
|
||||
Bitwise XOR for bit vectors.
|
||||
|
||||
```lean
|
||||
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
|
||||
```
|
||||
|
||||
SMT-Lib name: `bvxor`.
|
||||
-/
|
||||
protected def xor (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
|
||||
instance : Xor (BitVec w) := ⟨.xor⟩
|
||||
|
||||
/--
|
||||
Bitwise NOT for bit vectors.
|
||||
|
||||
```lean
|
||||
~~~(0b0101#4) == 0b1010
|
||||
```
|
||||
SMT-Lib name: `bvnot`.
|
||||
-/
|
||||
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
|
||||
instance : Complement (BitVec w) := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
|
||||
equivalent to `a * 2^s`, modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
protected def shiftLeft (a : BitVec n) (s : Nat) : BitVec n := (a.toNat <<< s)#n
|
||||
instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
|
||||
|
||||
/--
|
||||
(Logical) right shift for bit vectors. The high bits are filled with zeros.
|
||||
As a numeric operation, this is equivalent to `a / 2^s`, rounding down.
|
||||
|
||||
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def ushiftRight (a : BitVec n) (s : Nat) : BitVec n :=
|
||||
(a.toNat >>> s)#'(by
|
||||
let ⟨a, lt⟩ := a
|
||||
simp only [BitVec.toNat, Nat.shiftRight_eq_div_pow, Nat.div_lt_iff_lt_mul (Nat.two_pow_pos s)]
|
||||
rw [←Nat.mul_one a]
|
||||
exact Nat.mul_lt_mul_of_lt_of_le' lt (Nat.two_pow_pos s) (Nat.le_refl 1))
|
||||
|
||||
instance : HShiftRight (BitVec w) Nat (BitVec w) := ⟨.ushiftRight⟩
|
||||
|
||||
/--
|
||||
Arithmetic right shift for bit vectors. The high bits are filled with the
|
||||
most-significant bit.
|
||||
As a numeric operation, this is equivalent to `a.toInt >>> s`.
|
||||
|
||||
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def sshiftRight (a : BitVec n) (s : Nat) : BitVec n := .ofInt n (a.toInt >>> s)
|
||||
|
||||
instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x <<< y.toNat⟩
|
||||
instance {n} : HShiftRight (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x >>> y.toNat⟩
|
||||
|
||||
/--
|
||||
Rotate left for bit vectors. All the bits of `x` are shifted to higher positions, with the top `n`
|
||||
bits wrapping around to fill the low bits.
|
||||
|
||||
```lean
|
||||
rotateLeft 0b0011#4 3 = 0b1001
|
||||
```
|
||||
SMT-Lib name: `rotate_left` except this operator uses a `Nat` shift amount.
|
||||
-/
|
||||
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := x <<< n ||| x >>> (w - n)
|
||||
|
||||
/--
|
||||
Rotate right for bit vectors. All the bits of `x` are shifted to lower positions, with the
|
||||
bottom `n` bits wrapping around to fill the high bits.
|
||||
|
||||
```lean
|
||||
rotateRight 0b01001#5 1 = 0b10100
|
||||
```
|
||||
SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
|
||||
-/
|
||||
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := x >>> n ||| x <<< (w - n)
|
||||
|
||||
/--
|
||||
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
|
||||
input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
|
||||
|
||||
SMT-Lib name: `concat`.
|
||||
-/
|
||||
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
|
||||
shiftLeftZeroExtend msbs m ||| zeroExtend' (Nat.le_add_left m n) lsbs
|
||||
|
||||
instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := ⟨.append⟩
|
||||
|
||||
-- TODO: write this using multiplication
|
||||
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
|
||||
def replicate : (i : Nat) → BitVec w → BitVec (w*i)
|
||||
| 0, _ => 0
|
||||
| n+1, x =>
|
||||
have hEq : w + w*n = w*(n + 1) := by
|
||||
rw [Nat.mul_add, Nat.add_comm, Nat.mul_one]
|
||||
hEq ▸ (x ++ replicate n x)
|
||||
|
||||
/-!
|
||||
### Cons and Concat
|
||||
We give special names to the operations of adding a single bit to either end of a bitvector.
|
||||
We follow the precedent of `Vector.cons`/`Vector.concat` both for the name, and for the decision
|
||||
to have the resulting size be `n + 1` for both operations (rather than `1 + n`, which would be the
|
||||
result of appending a single bit to the front in the naive implementation).
|
||||
-/
|
||||
|
||||
/-- Append a single bit to the end of a bitvector, using big endian order (see `append`).
|
||||
That is, the new bit is the least significant bit. -/
|
||||
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
|
||||
|
||||
/-- 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) :=
|
||||
((ofBool msb) ++ lsbs).cast (Nat.add_comm ..)
|
||||
|
||||
theorem append_ofBool (msbs : BitVec w) (lsb : Bool) :
|
||||
msbs ++ ofBool lsb = concat msbs lsb :=
|
||||
rfl
|
||||
|
||||
theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
|
||||
ofBool msb ++ lsbs = (cons msb lsbs).cast (Nat.add_comm ..) :=
|
||||
rfl
|
||||
|
||||
end bitwise
|
||||
|
||||
section normalization_eqs
|
||||
/-! We add simp-lemmas that rewrite bitvector operations into the equivalent notation -/
|
||||
@[simp] theorem append_eq (x : BitVec w) (y : BitVec v) : BitVec.append x y = x ++ y := rfl
|
||||
@[simp] theorem shiftLeft_eq (x : BitVec w) (n : Nat) : BitVec.shiftLeft x n = x <<< n := rfl
|
||||
@[simp] theorem ushiftRight_eq (x : BitVec w) (n : Nat) : BitVec.ushiftRight x n = x >>> n := rfl
|
||||
@[simp] theorem not_eq (x : BitVec w) : BitVec.not x = ~~~x := rfl
|
||||
@[simp] theorem and_eq (x y : BitVec w) : BitVec.and x y = x &&& y := rfl
|
||||
@[simp] theorem or_eq (x y : BitVec w) : BitVec.or x y = x ||| y := rfl
|
||||
@[simp] theorem xor_eq (x y : BitVec w) : BitVec.xor x y = x ^^^ y := rfl
|
||||
@[simp] theorem neg_eq (x : BitVec w) : BitVec.neg x = -x := rfl
|
||||
@[simp] theorem add_eq (x y : BitVec w) : BitVec.add x y = x + y := rfl
|
||||
@[simp] theorem sub_eq (x y : BitVec w) : BitVec.sub x y = x - y := rfl
|
||||
@[simp] theorem mul_eq (x y : BitVec w) : BitVec.mul x y = x * y := rfl
|
||||
@[simp] theorem zero_eq : BitVec.zero n = 0#n := rfl
|
||||
end normalization_eqs
|
||||
|
||||
end BitVec
|
||||
284
src/Init/Data/BitVec/Bitblast.lean
Normal file
284
src/Init/Data/BitVec/Bitblast.lean
Normal file
@@ -0,0 +1,284 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Folds
|
||||
import Init.Data.Nat.Mod
|
||||
|
||||
/-!
|
||||
# Bitblasting of bitvectors
|
||||
|
||||
This module provides theorems for showing the equivalence between BitVec operations using
|
||||
the `Fin 2^n` representation and Boolean vectors. It is still under development, but
|
||||
intended to provide a path for converting SAT and SMT solver proofs about BitVectors
|
||||
as vectors of bits into proofs about Lean `BitVec` values.
|
||||
|
||||
The module is named for the bit-blasting operation in an SMT solver that converts bitvector
|
||||
expressions into expressions about individual bits in each vector.
|
||||
|
||||
## Main results
|
||||
* `x + y : BitVec w` is `(adc x y false).2`.
|
||||
|
||||
|
||||
## Future work
|
||||
All other operations are to be PR'ed later and are already proved in
|
||||
https://github.com/mhk119/lean-smt/blob/bitvec/Smt/Data/Bitwise.lean.
|
||||
|
||||
-/
|
||||
|
||||
open Nat Bool
|
||||
|
||||
namespace Bool
|
||||
|
||||
/-- At least two out of three booleans are true. -/
|
||||
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
|
||||
|
||||
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
|
||||
@[simp] theorem atLeastTwo_false_mid : atLeastTwo a false c = (a && c) := by simp [atLeastTwo]
|
||||
@[simp] theorem atLeastTwo_false_right : atLeastTwo a b false = (a && b) := by simp [atLeastTwo]
|
||||
@[simp] theorem atLeastTwo_true_left : atLeastTwo true b c = (b || c) := by cases b <;> cases c <;> simp [atLeastTwo]
|
||||
@[simp] theorem atLeastTwo_true_mid : atLeastTwo a true c = (a || c) := by cases a <;> cases c <;> simp [atLeastTwo]
|
||||
@[simp] theorem atLeastTwo_true_right : atLeastTwo a b true = (a || b) := by cases a <;> cases b <;> simp [atLeastTwo]
|
||||
|
||||
end Bool
|
||||
|
||||
/-! ### Preliminaries -/
|
||||
|
||||
namespace BitVec
|
||||
|
||||
private theorem testBit_limit {x i : Nat} (x_lt_succ : x < 2^(i+1)) :
|
||||
testBit x i = decide (x ≥ 2^i) := by
|
||||
cases xi : testBit x i with
|
||||
| true =>
|
||||
simp [testBit_implies_ge xi]
|
||||
| false =>
|
||||
simp
|
||||
cases Nat.lt_or_ge x (2^i) with
|
||||
| inl x_lt =>
|
||||
exact x_lt
|
||||
| inr x_ge =>
|
||||
have ⟨j, ⟨j_ge, jp⟩⟩ := ge_two_pow_implies_high_bit_true x_ge
|
||||
cases Nat.lt_or_eq_of_le j_ge with
|
||||
| inr x_eq =>
|
||||
simp [x_eq, jp] at xi
|
||||
| inl x_lt =>
|
||||
exfalso
|
||||
apply Nat.lt_irrefl
|
||||
calc x < 2^(i+1) := x_lt_succ
|
||||
_ ≤ 2 ^ j := Nat.pow_le_pow_of_le_right Nat.zero_lt_two x_lt
|
||||
_ ≤ x := testBit_implies_ge jp
|
||||
|
||||
private theorem mod_two_pow_succ (x i : Nat) :
|
||||
x % 2^(i+1) = 2^i*(x.testBit i).toNat + x % (2 ^ i):= by
|
||||
rw [Nat.mod_pow_succ, Nat.add_comm, Nat.toNat_testBit]
|
||||
|
||||
private theorem mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ
|
||||
(x y i : Nat) (c : Bool) : x % 2^i + (y % 2^i + c.toNat) < 2^(i+1) := by
|
||||
have : c.toNat ≤ 1 := Bool.toNat_le c
|
||||
rw [Nat.pow_succ]
|
||||
omega
|
||||
|
||||
/-! ### Addition -/
|
||||
|
||||
/-- carry i x y c returns true if the `i` carry bit is true when computing `x + y + c`. -/
|
||||
def carry (i : Nat) (x y : BitVec w) (c : Bool) : Bool :=
|
||||
decide (x.toNat % 2^i + y.toNat % 2^i + c.toNat ≥ 2^i)
|
||||
|
||||
@[simp] theorem carry_zero : carry 0 x y c = c := by
|
||||
cases c <;> simp [carry, mod_one]
|
||||
|
||||
theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
|
||||
carry (i+1) x y c = atLeastTwo (x.getLsb i) (y.getLsb i) (carry i x y c) := by
|
||||
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsb]
|
||||
simp only [Nat.pow_succ']
|
||||
have sum_bnd : x.toNat%2^i + (y.toNat%2^i + c.toNat) < 2*2^i := by
|
||||
simp only [← Nat.pow_succ']
|
||||
exact mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ ..
|
||||
cases x.toNat.testBit i <;> cases y.toNat.testBit i <;> (simp; omega)
|
||||
|
||||
/--
|
||||
Does the addition of two `BitVec`s overflow?
|
||||
|
||||
The nice feature of this definition is that
|
||||
it can be unfolded recursively to a circuit:
|
||||
```
|
||||
example (x y : BitVec 4) :
|
||||
addOverflow x y =
|
||||
atLeastTwo (x.getLsb 3) (y.getLsb 3) (atLeastTwo (x.getLsb 2) (y.getLsb 2)
|
||||
(atLeastTwo (x.getLsb 1) (y.getLsb 1) (x.getLsb 0 && y.getLsb 0))) := by
|
||||
simp [addOverflow, msb_truncate, BitVec.msb, getMsb]
|
||||
```
|
||||
-/
|
||||
def addOverflow (x y : BitVec w) (c : Bool := false) : Bool :=
|
||||
match w with
|
||||
| 0 => c
|
||||
| (w + 1) => atLeastTwo x.msb y.msb (addOverflow (x.truncate w) (y.truncate w) c)
|
||||
|
||||
@[simp] theorem addOverflow_length_zero {x y : BitVec 0} : addOverflow x y c = c := rfl
|
||||
|
||||
theorem addOverflow_length_succ {x y : BitVec (w+1)} :
|
||||
addOverflow x y c = atLeastTwo x.msb y.msb (addOverflow (x.truncate w) (y.truncate w) c) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem addOverflow_zero_left_succ :
|
||||
addOverflow 0#(w+1) y c = (y.msb && addOverflow 0#w (y.truncate w) c) := by
|
||||
simp [addOverflow]
|
||||
|
||||
@[simp] theorem addOverflow_zero_right_succ {x : BitVec (w+1)} :
|
||||
addOverflow x 0#(w+1) c = (x.msb && addOverflow (x.truncate w) 0#w c) := by
|
||||
simp [addOverflow]
|
||||
|
||||
@[simp] theorem addOverflow_zero_zero :
|
||||
addOverflow 0#i 0#i c = (decide (i = 0) && c) := by
|
||||
cases i <;> simp
|
||||
|
||||
theorem carry_eq_addOverflow (i) (x y : BitVec w) (c) :
|
||||
carry i x y c = addOverflow (x.truncate i) (y.truncate i) c := by
|
||||
match i with
|
||||
| 0 => simp
|
||||
| (i + 1) =>
|
||||
rw [addOverflow_length_succ, carry_succ, carry_eq_addOverflow]
|
||||
simp [msb_zeroExtend, Nat.le_succ]
|
||||
|
||||
theorem addOverflow_eq_carry {x y : BitVec w} :
|
||||
addOverflow x y c = carry w x y c := by
|
||||
have := carry_eq_addOverflow w x y c
|
||||
simpa using this.symm
|
||||
|
||||
theorem addOverflow_cons_cons :
|
||||
addOverflow (cons a x) (cons b y) = atLeastTwo a b (addOverflow x y) := by
|
||||
simp [addOverflow]
|
||||
|
||||
theorem add_cons_cons (w) (x y : BitVec w) :
|
||||
(cons a x) + (cons b y) = cons (Bool.xor a (Bool.xor b (addOverflow x y))) (x + y) := by
|
||||
have pos : 0 < 2^w := Nat.pow_pos Nat.zero_lt_two
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_add, toNat_cons']
|
||||
rw [addOverflow_eq_carry, carry]
|
||||
simp [Nat.mod_pow_succ]
|
||||
by_cases h : 2 ^ w ≤ x.toNat + y.toNat
|
||||
· simp [h]
|
||||
have p : (x.toNat + y.toNat) / 2 ^ w = 1 := by
|
||||
apply Nat.div_eq_of_lt_le <;> omega
|
||||
cases a <;> cases b
|
||||
<;> simp [Nat.one_shiftLeft, Nat.add_left_comm x.toNat, Nat.add_assoc, p, pos]
|
||||
<;> simp [Nat.add_comm]
|
||||
· simp [h]
|
||||
have p : (x.toNat + y.toNat) / 2 ^ w = 0 := by
|
||||
apply Nat.div_eq_of_lt_le <;> omega
|
||||
cases a <;> cases b
|
||||
<;> simp [Nat.one_shiftLeft, Nat.add_left_comm x.toNat, Nat.add_assoc, p, pos]
|
||||
<;> simp [Nat.add_comm]
|
||||
|
||||
theorem msb_add (x y : BitVec w) :
|
||||
(x + y).msb =
|
||||
Bool.xor x.msb (Bool.xor y.msb (addOverflow (x.truncate (w-1)) (y.truncate (w-1)))) := by
|
||||
cases w with
|
||||
| zero => simp
|
||||
| succ w =>
|
||||
conv =>
|
||||
lhs
|
||||
rw [eq_msb_cons_truncate x, eq_msb_cons_truncate y, add_cons_cons]
|
||||
simp [succ_eq_add_one, Nat.add_one_sub_one]
|
||||
|
||||
/--
|
||||
Variant of `getLsb_add` in terms of `addOverflow` rather than `carry`.
|
||||
-/
|
||||
theorem getLsb_add' (i : Nat) (x y : BitVec w) :
|
||||
getLsb (x + y) i = (decide (i < w) && Bool.xor (x.getLsb i)
|
||||
(Bool.xor (y.getLsb i) (addOverflow (x.truncate i) (y.truncate i)))) := by
|
||||
by_cases h : i < w
|
||||
· rw [← msb_truncate (x + y), truncate_add, msb_add, msb_truncate, msb_truncate]
|
||||
rw [Nat.add_one_sub_one, truncate_truncate_of_le, truncate_truncate_of_le]
|
||||
simp [h]
|
||||
all_goals omega
|
||||
· simp [h]
|
||||
simp at h
|
||||
simp [h]
|
||||
|
||||
theorem addOverflow_eq_false_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
|
||||
addOverflow x y = false := by
|
||||
induction w with
|
||||
| zero => rfl
|
||||
| succ w ih =>
|
||||
have h₁ := congrArg BitVec.msb h
|
||||
have h₂ := congrArg (·.truncate w) h
|
||||
simp at h₁ h₂
|
||||
simp_all [addOverflow_length_succ]
|
||||
|
||||
theorem or_eq_add_of_and_eq_zero (x y : BitVec w) (h : x &&& y = 0) :
|
||||
x ||| y = x + y := by
|
||||
ext i
|
||||
have h₁ := congrArg (getLsb · i) h
|
||||
have h₂ := congrArg (truncate i) h
|
||||
simp at h₁ h₂
|
||||
simp only [getLsb_add', getLsb_or]
|
||||
rw [addOverflow_eq_false_of_and_eq_zero h₂]
|
||||
-- sat
|
||||
revert h₁
|
||||
cases x.getLsb i <;> cases y.getLsb i <;> simp
|
||||
|
||||
/-- Carry function for bitwise addition. -/
|
||||
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xor y c))
|
||||
|
||||
/-- Bitwise addition implemented via a ripple carry adder. -/
|
||||
def adc (x y : BitVec w) : Bool → Bool × BitVec w :=
|
||||
iunfoldr fun (i : Fin w) c => adcb (x.getLsb i) (y.getLsb i) c
|
||||
|
||||
theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
|
||||
getLsb (x + y + zeroExtend w (ofBool c)) i =
|
||||
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y c)) := by
|
||||
let ⟨x, x_lt⟩ := x
|
||||
let ⟨y, y_lt⟩ := y
|
||||
simp only [getLsb, toNat_add, toNat_zeroExtend, i_lt, toNat_ofFin, toNat_ofBool,
|
||||
Nat.mod_add_mod, Nat.add_mod_mod]
|
||||
apply Eq.trans
|
||||
rw [← Nat.div_add_mod x (2^i), ← Nat.div_add_mod y (2^i)]
|
||||
simp only
|
||||
[ Nat.testBit_mod_two_pow,
|
||||
Nat.testBit_mul_two_pow_add_eq,
|
||||
i_lt,
|
||||
decide_True,
|
||||
Bool.true_and,
|
||||
Nat.add_assoc,
|
||||
Nat.add_left_comm (_%_) (_ * _) _,
|
||||
testBit_limit (mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ x y i c)
|
||||
]
|
||||
simp [testBit_to_div_mod, carry, Nat.add_assoc]
|
||||
|
||||
theorem getLsb_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
getLsb (x + y) i =
|
||||
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y false)) := by
|
||||
simpa using getLsb_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 + zeroExtend w (ofBool c)) := by
|
||||
simp only [adc]
|
||||
apply iunfoldr_replace
|
||||
(fun i => carry i x y c)
|
||||
(x + y + zeroExtend w (ofBool c))
|
||||
c
|
||||
case init =>
|
||||
simp [carry, Nat.mod_one]
|
||||
cases c <;> rfl
|
||||
case step =>
|
||||
simp [adcb, Prod.mk.injEq, carry_succ, getLsb_add_add_bool]
|
||||
|
||||
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
|
||||
simp [adc_spec]
|
||||
|
||||
/-! ### add -/
|
||||
|
||||
/-- Adding a bitvector to its own complement yields the all ones bitpattern -/
|
||||
@[simp] theorem add_not_self (x : BitVec w) : x + ~~~x = allOnes w := by
|
||||
rw [add_eq_adc, adc, iunfoldr_replace (fun _ => false) (allOnes w)]
|
||||
· rfl
|
||||
· simp [adcb, atLeastTwo]
|
||||
|
||||
/-- Subtracting `x` from the all ones bitvector is equivalent to taking its complement -/
|
||||
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
|
||||
rw [← add_not_self x, BitVec.add_comm, add_sub_cancel]
|
||||
|
||||
end BitVec
|
||||
61
src/Init/Data/BitVec/Folds.lean
Normal file
61
src/Init/Data/BitVec/Folds.lean
Normal file
@@ -0,0 +1,61 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.BitVec.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Fin.Iterate
|
||||
|
||||
namespace BitVec
|
||||
|
||||
/--
|
||||
iunfoldr is an iterative operation that applies a function `f` repeatedly.
|
||||
|
||||
It produces a sequence of state values `[s_0, s_1 .. s_w]` and a bitvector
|
||||
`v` where `f i s_i = (s_{i+1}, b_i)` and `b_i` is bit `i`th least-significant bit
|
||||
in `v` (e.g., `getLsb v i = b_i`).
|
||||
|
||||
Theorems involving `iunfoldr` can be eliminated using `iunfoldr_replace` below.
|
||||
-/
|
||||
def iunfoldr (f : Fin w -> α → α × Bool) (s : α) : α × BitVec w :=
|
||||
Fin.hIterate (fun i => α × BitVec i) (s, nil) fun i q =>
|
||||
(fun p => ⟨p.fst, cons p.snd q.snd⟩) (f i q.fst)
|
||||
|
||||
theorem iunfoldr.fst_eq
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (s : α)
|
||||
(init : s = state 0)
|
||||
(ind : ∀(i : Fin w), (f i (state i.val)).fst = state (i.val+1)) :
|
||||
(iunfoldr f s).fst = state w := by
|
||||
unfold iunfoldr
|
||||
apply Fin.hIterate_elim (fun i (p : α × BitVec i) => p.fst = state i)
|
||||
case init =>
|
||||
exact init
|
||||
case step =>
|
||||
intro i ⟨s, v⟩ p
|
||||
simp_all [ind i]
|
||||
|
||||
private theorem iunfoldr.eq_test
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (value : BitVec w) (a : α)
|
||||
(init : state 0 = a)
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
|
||||
iunfoldr f a = (state w, BitVec.truncate w value) := by
|
||||
apply Fin.hIterate_eq (fun i => ((state i, BitVec.truncate i value) : α × BitVec i))
|
||||
case init =>
|
||||
simp only [init, eq_nil]
|
||||
case step =>
|
||||
intro i
|
||||
simp_all [truncate_succ]
|
||||
|
||||
/--
|
||||
Correctness theorem for `iunfoldr`.
|
||||
-/
|
||||
theorem iunfoldr_replace
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (value : BitVec w) (a : α)
|
||||
(init : state 0 = a)
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
|
||||
iunfoldr f a = (state w, value) := by
|
||||
simp [iunfoldr.eq_test state value a init step]
|
||||
|
||||
end BitVec
|
||||
844
src/Init/Data/BitVec/Lemmas.lean
Normal file
844
src/Init/Data/BitVec/Lemmas.lean
Normal file
@@ -0,0 +1,844 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Bool
|
||||
import Init.Data.BitVec.Basic
|
||||
import Init.Data.Fin.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
namespace BitVec
|
||||
|
||||
/--
|
||||
This normalized a bitvec using `ofFin` to `ofNat`.
|
||||
-/
|
||||
theorem ofFin_eq_ofNat : @BitVec.ofFin w (Fin.mk x lt) = BitVec.ofNat w x := by
|
||||
simp only [BitVec.ofNat, Fin.ofNat', lt, Nat.mod_eq_of_lt]
|
||||
|
||||
/-- Prove equality of bitvectors in terms of nat operations. -/
|
||||
theorem eq_of_toNat_eq {n} : ∀ {i j : BitVec n}, i.toNat = j.toNat → i = j
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
@[simp] theorem val_toFin (x : BitVec w) : x.toFin.val = x.toNat := rfl
|
||||
|
||||
@[bv_toNat] theorem toNat_eq (x y : BitVec n) : x = y ↔ x.toNat = y.toNat :=
|
||||
Iff.intro (congrArg BitVec.toNat) eq_of_toNat_eq
|
||||
|
||||
@[bv_toNat] theorem toNat_ne (x y : BitVec n) : x ≠ y ↔ x.toNat ≠ y.toNat := by
|
||||
rw [Ne, toNat_eq]
|
||||
|
||||
theorem toNat_lt (x : BitVec n) : x.toNat < 2^n := x.toFin.2
|
||||
|
||||
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsb i := rfl
|
||||
|
||||
@[simp] theorem getLsb_ofFin (x : Fin (2^n)) (i : Nat) :
|
||||
getLsb (BitVec.ofFin x) i = x.val.testBit i := rfl
|
||||
|
||||
@[simp] theorem getLsb_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getLsb x i = false := by
|
||||
let ⟨x, x_lt⟩ := x
|
||||
simp
|
||||
apply Nat.testBit_lt_two_pow
|
||||
have p : 2^w ≤ 2^i := Nat.pow_le_pow_of_le_right (by omega) ge
|
||||
omega
|
||||
|
||||
theorem lt_of_getLsb (x : BitVec w) (i : Nat) : getLsb x i = true → i < w := by
|
||||
if h : i < w then
|
||||
simp [h]
|
||||
else
|
||||
simp [Nat.ge_of_not_lt h]
|
||||
|
||||
-- We choose `eq_of_getLsb_eq` as the `@[ext]` theorem for `BitVec`
|
||||
-- somewhat arbitrarily over `eq_of_getMsg_eq`.
|
||||
@[ext] theorem eq_of_getLsb_eq {x y : BitVec w}
|
||||
(pred : ∀(i : Fin w), x.getLsb i.val = y.getLsb i.val) : x = y := by
|
||||
apply eq_of_toNat_eq
|
||||
apply Nat.eq_of_testBit_eq
|
||||
intro i
|
||||
if i_lt : i < w then
|
||||
exact pred ⟨i, i_lt⟩
|
||||
else
|
||||
have p : i ≥ w := Nat.le_of_not_gt i_lt
|
||||
simp [testBit_toNat, getLsb_ge _ _ p]
|
||||
|
||||
theorem eq_of_getMsb_eq {x y : BitVec w}
|
||||
(pred : ∀(i : Fin w), x.getMsb i = y.getMsb i.val) : x = y := by
|
||||
simp only [getMsb] at pred
|
||||
apply eq_of_getLsb_eq
|
||||
intro ⟨i, i_lt⟩
|
||||
if w_zero : w = 0 then
|
||||
simp [w_zero]
|
||||
else
|
||||
have w_pos := Nat.pos_of_ne_zero w_zero
|
||||
have r : i ≤ w - 1 := by
|
||||
simp [Nat.le_sub_iff_add_le w_pos, Nat.add_succ]
|
||||
exact i_lt
|
||||
have q_lt : w - 1 - i < w := by
|
||||
simp only [Nat.sub_sub]
|
||||
apply Nat.sub_lt w_pos
|
||||
simp [Nat.succ_add]
|
||||
have q := pred ⟨w - 1 - i, q_lt⟩
|
||||
simpa [q_lt, Nat.sub_sub_self, r] using q
|
||||
|
||||
@[simp] theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
|
||||
|
||||
theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
@[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat := by
|
||||
cases b <;> rfl
|
||||
|
||||
@[simp] theorem msb_ofBool (b : Bool) : (ofBool b).msb = b := by
|
||||
cases b <;> simp [BitVec.msb]
|
||||
|
||||
theorem ofNat_one (n : Nat) : BitVec.ofNat 1 n = BitVec.ofBool (n % 2 = 1) := by
|
||||
rcases (Nat.mod_two_eq_zero_or_one n) with h | h <;> simp [h, BitVec.ofNat, Fin.ofNat']
|
||||
|
||||
theorem ofBool_eq_iff_eq : ∀(b b' : Bool), BitVec.ofBool b = BitVec.ofBool b' ↔ b = b' := by
|
||||
decide
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
|
||||
|
||||
@[simp] theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
|
||||
|
||||
@[simp] theorem getLsb_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||||
getLsb (x#'lt) i = x.testBit i := by
|
||||
simp [getLsb, BitVec.ofNatLt]
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_ofNat (x w : Nat) : (x#w).toNat = x % 2^w := by
|
||||
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
|
||||
|
||||
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
|
||||
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
|
||||
theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
|
||||
getLsb (x#n) i = (i < n && x.testBit i) := by
|
||||
simp [getLsb, BitVec.ofNat, Fin.val_ofNat']
|
||||
|
||||
@[simp, deprecated toNat_ofNat] theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
|
||||
|
||||
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
|
||||
|
||||
@[simp] theorem getMsb_zero : (0#w).getMsb i = false := by simp [getMsb]
|
||||
|
||||
@[simp] theorem toNat_mod_cancel (x : BitVec n) : x.toNat % (2^n) = x.toNat :=
|
||||
Nat.mod_eq_of_lt x.isLt
|
||||
|
||||
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m ≤ n) : x < 2 ^ n :=
|
||||
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_of_le_right (by trivial : 0 < 2) le)
|
||||
|
||||
/-! ### msb -/
|
||||
|
||||
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsb]
|
||||
|
||||
theorem msb_eq_getLsb_last (x : BitVec w) :
|
||||
x.msb = x.getLsb (w - 1) := by
|
||||
simp [BitVec.msb, getMsb, getLsb]
|
||||
rcases w with rfl | w
|
||||
· simp [BitVec.eq_nil x]
|
||||
· simp
|
||||
|
||||
@[bv_toNat] theorem getLsb_last (x : BitVec w) :
|
||||
x.getLsb (w-1) = decide (2 ^ (w-1) ≤ x.toNat) := by
|
||||
rcases w with rfl | w
|
||||
· simp
|
||||
· simp only [Nat.zero_lt_succ, decide_True, getLsb, Nat.testBit, Nat.succ_sub_succ_eq_sub,
|
||||
Nat.sub_zero, Nat.and_one_is_mod, Bool.true_and, Nat.shiftRight_eq_div_pow]
|
||||
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
|
||||
· simp [Nat.div_eq_of_lt h, h]
|
||||
· simp only [h]
|
||||
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
|
||||
· decide
|
||||
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
|
||||
omega
|
||||
|
||||
@[bv_toNat] theorem getLsb_succ_last (x : BitVec (w + 1)) :
|
||||
x.getLsb w = decide (2 ^ w ≤ x.toNat) := getLsb_last x
|
||||
|
||||
@[bv_toNat] theorem msb_eq_decide (x : BitVec w) : BitVec.msb x = decide (2 ^ (w-1) ≤ x.toNat) := by
|
||||
simp [msb_eq_getLsb_last, getLsb_last]
|
||||
|
||||
theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat ≥ 2^(n-1) := by
|
||||
match n with
|
||||
| 0 =>
|
||||
simp [BitVec.msb, BitVec.getMsb] at p
|
||||
| n + 1 =>
|
||||
simp [BitVec.msb_eq_decide] at p
|
||||
simp only [Nat.add_sub_cancel]
|
||||
exact p
|
||||
|
||||
/-! ### cast -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
|
||||
@[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
|
||||
(cast h x).toFin = x.toFin.cast (by rw [h]) :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem getLsb_cast (h : w = v) (x : BitVec w) : (cast h x).getLsb i = x.getLsb i := by
|
||||
subst h; simp
|
||||
|
||||
@[simp] theorem getMsb_cast (h : w = v) (x : BitVec w) : (cast h x).getMsb i = x.getMsb i := by
|
||||
subst h; simp
|
||||
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
/-! ### toInt/ofInt -/
|
||||
|
||||
/-- Prove equality of bitvectors in terms of nat operations. -/
|
||||
theorem toInt_eq_toNat_cond (i : BitVec n) :
|
||||
i.toInt =
|
||||
if 2*i.toNat < 2^n then
|
||||
(i.toNat : Int)
|
||||
else
|
||||
(i.toNat : Int) - (2^n : Nat) := by
|
||||
unfold BitVec.toInt
|
||||
split <;> omega
|
||||
|
||||
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
|
||||
simp only [toInt_eq_toNat_cond]
|
||||
split
|
||||
case inl g =>
|
||||
rw [Int.bmod_pos] <;> simp only [←Int.ofNat_emod, toNat_mod_cancel]
|
||||
omega
|
||||
case inr g =>
|
||||
rw [Int.bmod_neg] <;> simp only [←Int.ofNat_emod, toNat_mod_cancel]
|
||||
omega
|
||||
|
||||
/-- Prove equality of bitvectors in terms of nat operations. -/
|
||||
theorem eq_of_toInt_eq {i j : BitVec n} : i.toInt = j.toInt → i = j := by
|
||||
intro eq
|
||||
simp [toInt_eq_toNat_cond] at eq
|
||||
apply eq_of_toNat_eq
|
||||
revert eq
|
||||
have _ilt := i.isLt
|
||||
have _jlt := j.isLt
|
||||
split <;> split <;> omega
|
||||
|
||||
@[simp] theorem toNat_ofInt {n : Nat} (i : Int) :
|
||||
(BitVec.ofInt n i).toNat = (i % (2^n : Nat)).toNat := by
|
||||
unfold BitVec.ofInt
|
||||
simp
|
||||
|
||||
theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||||
(BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
|
||||
simp [toInt_eq_toNat_bmod]
|
||||
|
||||
@[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
|
||||
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
|
||||
have _ := Nat.two_pow_pos n
|
||||
have p : 0 ≤ i % (2^n : Nat) := by omega
|
||||
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
|
||||
|
||||
/-! ### zeroExtend and truncate -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m ≤ n) (x : BitVec m) :
|
||||
(zeroExtend' p x).toNat = x.toNat := by
|
||||
unfold zeroExtend'
|
||||
simp [p, x.isLt, Nat.mod_eq_of_lt]
|
||||
|
||||
@[bv_toNat] theorem toNat_zeroExtend (i : Nat) (x : BitVec n) :
|
||||
BitVec.toNat (zeroExtend i x) = x.toNat % 2^i := by
|
||||
let ⟨x, lt_n⟩ := x
|
||||
simp only [zeroExtend]
|
||||
if n_le_i : n ≤ i then
|
||||
have x_lt_two_i : x < 2 ^ i := lt_two_pow_of_le lt_n n_le_i
|
||||
simp [n_le_i, Nat.mod_eq_of_lt, x_lt_two_i]
|
||||
else
|
||||
simp [n_le_i, toNat_ofNat]
|
||||
|
||||
theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroExtend v := by
|
||||
apply eq_of_toNat_eq
|
||||
rw [toNat_zeroExtend, toNat_zeroExtend']
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
exact Nat.lt_of_lt_of_le x.isLt (Nat.pow_le_pow_right (Nat.zero_lt_two) h)
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_truncate (x : BitVec n) : (truncate i x).toNat = x.toNat % 2^i :=
|
||||
toNat_zeroExtend i x
|
||||
|
||||
@[simp] theorem zeroExtend_eq (x : BitVec n) : zeroExtend n x = x := by
|
||||
apply eq_of_toNat_eq
|
||||
let ⟨x, lt_n⟩ := x
|
||||
simp [truncate, zeroExtend]
|
||||
|
||||
@[simp] theorem zeroExtend_zero (m n : Nat) : zeroExtend m (0#n) = 0#m := by
|
||||
apply eq_of_toNat_eq
|
||||
simp [toNat_zeroExtend]
|
||||
|
||||
@[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
|
||||
|
||||
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : x.toNat#m = truncate m x := by
|
||||
apply eq_of_toNat_eq
|
||||
simp
|
||||
|
||||
/-- Moves one-sided left toNat equality to BitVec equality. -/
|
||||
theorem toNat_eq_nat (x : BitVec w) (y : Nat)
|
||||
: (x.toNat = y) ↔ (y < 2^w ∧ (x = y#w)) := by
|
||||
apply Iff.intro
|
||||
· intro eq
|
||||
simp at eq
|
||||
have lt := x.isLt
|
||||
simp [eq] at lt
|
||||
simp [←eq, lt, x.isLt]
|
||||
· intro eq
|
||||
simp [Nat.mod_eq_of_lt, eq]
|
||||
|
||||
/-- Moves one-sided right toNat equality to BitVec equality. -/
|
||||
theorem nat_eq_toNat (x : BitVec w) (y : Nat)
|
||||
: (y = x.toNat) ↔ (y < 2^w ∧ (x = y#w)) := by
|
||||
rw [@eq_comm _ _ x.toNat]
|
||||
apply toNat_eq_nat
|
||||
|
||||
@[simp] theorem getLsb_zeroExtend' (ge : m ≥ n) (x : BitVec n) (i : Nat) :
|
||||
getLsb (zeroExtend' ge x) i = getLsb x i := by
|
||||
simp [getLsb, toNat_zeroExtend']
|
||||
|
||||
@[simp] theorem getLsb_zeroExtend (m : Nat) (x : BitVec n) (i : Nat) :
|
||||
getLsb (zeroExtend m x) i = (decide (i < m) && getLsb x i) := by
|
||||
simp [getLsb, toNat_zeroExtend, Nat.testBit_mod_two_pow]
|
||||
|
||||
@[simp] theorem getMsb_zeroExtend_add {x : BitVec w} (h : k ≤ i) :
|
||||
(x.zeroExtend (w + k)).getMsb i = x.getMsb (i - k) := by
|
||||
by_cases h : w = 0
|
||||
· subst h; simp
|
||||
simp only [getMsb, getLsb_zeroExtend]
|
||||
by_cases h₁ : i < w + k <;> by_cases h₂ : i - k < w <;> by_cases h₃ : w + k - 1 - i < w + k
|
||||
<;> simp [h₁, h₂, h₃]
|
||||
· congr 1
|
||||
omega
|
||||
all_goals (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
|
||||
<;> omega
|
||||
|
||||
@[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
|
||||
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
|
||||
getLsb_zeroExtend m x i
|
||||
|
||||
theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := by
|
||||
simp [BitVec.msb, getMsb]
|
||||
|
||||
@[simp] theorem zeroExtend_zeroExtend_of_le (x : BitVec w) (h : k ≤ l) :
|
||||
(x.zeroExtend l).zeroExtend k = x.zeroExtend k := by
|
||||
ext i
|
||||
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and]
|
||||
have p := lt_of_getLsb x i
|
||||
revert p
|
||||
cases getLsb x i <;> simp; omega
|
||||
|
||||
@[simp] theorem truncate_truncate_of_le (x : BitVec w) (h : k ≤ l) :
|
||||
(x.truncate l).truncate k = x.truncate k :=
|
||||
zeroExtend_zeroExtend_of_le x h
|
||||
|
||||
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
|
||||
apply eq_of_getLsb_eq
|
||||
simp
|
||||
|
||||
theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
|
||||
rw [msb_eq_getLsb_last]
|
||||
simp only [getLsb_zeroExtend]
|
||||
cases getLsb x (v - 1) <;> simp; omega
|
||||
|
||||
theorem msb_zeroExtend' (x : BitVec w) (h : w ≤ v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
|
||||
rw [zeroExtend'_eq, msb_zeroExtend]
|
||||
|
||||
/-! ## extractLsb -/
|
||||
|
||||
@[simp]
|
||||
protected theorem extractLsb_ofFin {n} (x : Fin (2^n)) (hi lo : Nat) :
|
||||
extractLsb hi lo (@BitVec.ofFin n x) = .ofNat (hi-lo+1) (x.val >>> lo) := rfl
|
||||
|
||||
@[simp]
|
||||
protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
|
||||
extractLsb hi lo x#n = .ofNat (hi - lo + 1) ((x % 2^n) >>> lo) := by
|
||||
apply eq_of_getLsb_eq
|
||||
intro ⟨i, _lt⟩
|
||||
simp [BitVec.ofNat]
|
||||
|
||||
@[simp] theorem extractLsb'_toNat (s m : Nat) (x : BitVec n) :
|
||||
(extractLsb' s m x).toNat = (x.toNat >>> s) % 2^m := rfl
|
||||
|
||||
@[simp] theorem extractLsb_toNat (hi lo : Nat) (x : BitVec n) :
|
||||
(extractLsb hi lo x).toNat = (x.toNat >>> lo) % 2^(hi-lo+1) := rfl
|
||||
|
||||
@[simp] theorem getLsb_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
|
||||
getLsb (extractLsb hi lo x) i = (i ≤ (hi-lo) && getLsb x (lo+i)) := by
|
||||
unfold getLsb
|
||||
simp [Nat.lt_succ]
|
||||
|
||||
/-! ### allOnes -/
|
||||
|
||||
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||||
unfold allOnes
|
||||
simp
|
||||
|
||||
@[simp] theorem getLsb_allOnes : (allOnes v).getLsb i = decide (i < v) := by
|
||||
simp [allOnes]
|
||||
|
||||
/-! ### or -/
|
||||
|
||||
@[simp] theorem toNat_or (x y : BitVec v) :
|
||||
BitVec.toNat (x ||| y) = BitVec.toNat x ||| BitVec.toNat y := rfl
|
||||
|
||||
@[simp] theorem toFin_or (x y : BitVec v) :
|
||||
BitVec.toFin (x ||| y) = BitVec.toFin x ||| BitVec.toFin y := by
|
||||
apply Fin.eq_of_val_eq
|
||||
exact (Nat.mod_eq_of_lt <| Nat.or_lt_two_pow x.isLt y.isLt).symm
|
||||
|
||||
@[simp] theorem getLsb_or {x y : BitVec v} : (x ||| y).getLsb i = (x.getLsb i || y.getLsb i) := by
|
||||
rw [← testBit_toNat, getLsb, getLsb]
|
||||
simp
|
||||
|
||||
@[simp] theorem getMsb_or {x y : BitVec w} : (x ||| y).getMsb i = (x.getMsb i || y.getMsb i) := by
|
||||
simp only [getMsb]
|
||||
by_cases h : i < w <;> simp [h]
|
||||
|
||||
@[simp] theorem msb_or {x y : BitVec w} : (x ||| y).msb = (x.msb || y.msb) := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
@[simp] theorem truncate_or {x y : BitVec w} :
|
||||
(x ||| y).truncate k = x.truncate k ||| y.truncate k := by
|
||||
ext
|
||||
simp
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem toNat_and (x y : BitVec v) :
|
||||
BitVec.toNat (x &&& y) = BitVec.toNat x &&& BitVec.toNat y := rfl
|
||||
|
||||
@[simp] theorem toFin_and (x y : BitVec v) :
|
||||
BitVec.toFin (x &&& y) = BitVec.toFin x &&& BitVec.toFin y := by
|
||||
apply Fin.eq_of_val_eq
|
||||
exact (Nat.mod_eq_of_lt <| Nat.and_lt_two_pow _ y.isLt).symm
|
||||
|
||||
@[simp] theorem getLsb_and {x y : BitVec v} : (x &&& y).getLsb i = (x.getLsb i && y.getLsb i) := by
|
||||
rw [← testBit_toNat, getLsb, getLsb]
|
||||
simp
|
||||
|
||||
@[simp] theorem getMsb_and {x y : BitVec w} : (x &&& y).getMsb i = (x.getMsb i && y.getMsb i) := by
|
||||
simp only [getMsb]
|
||||
by_cases h : i < w <;> simp [h]
|
||||
|
||||
@[simp] theorem msb_and {x y : BitVec w} : (x &&& y).msb = (x.msb && y.msb) := by
|
||||
simp [BitVec.msb]
|
||||
|
||||
@[simp] theorem truncate_and {x y : BitVec w} :
|
||||
(x &&& y).truncate k = x.truncate k &&& y.truncate k := by
|
||||
ext
|
||||
simp
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp] theorem toNat_xor (x y : BitVec v) :
|
||||
BitVec.toNat (x ^^^ y) = BitVec.toNat x ^^^ BitVec.toNat y := rfl
|
||||
|
||||
@[simp] theorem toFin_xor (x y : BitVec v) :
|
||||
BitVec.toFin (x ^^^ y) = BitVec.toFin x ^^^ BitVec.toFin y := by
|
||||
apply Fin.eq_of_val_eq
|
||||
exact (Nat.mod_eq_of_lt <| Nat.xor_lt_two_pow x.isLt y.isLt).symm
|
||||
|
||||
@[simp] theorem getLsb_xor {x y : BitVec v} :
|
||||
(x ^^^ y).getLsb i = (xor (x.getLsb i) (y.getLsb i)) := by
|
||||
rw [← testBit_toNat, getLsb, getLsb]
|
||||
simp
|
||||
|
||||
@[simp] theorem truncate_xor {x y : BitVec w} :
|
||||
(x ^^^ y).truncate k = x.truncate k ^^^ y.truncate k := by
|
||||
ext
|
||||
simp
|
||||
|
||||
/-! ### not -/
|
||||
|
||||
theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_not {x : BitVec v} : (~~~x).toNat = 2^v - 1 - x.toNat := by
|
||||
rw [Nat.sub_sub, Nat.add_comm, not_def, toNat_xor]
|
||||
apply Nat.eq_of_testBit_eq
|
||||
intro i
|
||||
simp only [toNat_allOnes, Nat.testBit_xor, Nat.testBit_two_pow_sub_one]
|
||||
match h : BitVec.toNat x with
|
||||
| 0 => simp
|
||||
| y+1 =>
|
||||
rw [Nat.succ_eq_add_one] at h
|
||||
rw [← h]
|
||||
rw [Nat.testBit_two_pow_sub_succ (toNat_lt _)]
|
||||
· cases w : decide (i < v)
|
||||
· simp at w
|
||||
simp [w]
|
||||
rw [Nat.testBit_lt_two_pow]
|
||||
calc BitVec.toNat x < 2 ^ v := toNat_lt _
|
||||
_ ≤ 2 ^ i := Nat.pow_le_pow_of_le_right Nat.zero_lt_two w
|
||||
· simp
|
||||
|
||||
@[simp] theorem toFin_not (x : BitVec w) :
|
||||
(~~~x).toFin = x.toFin.rev := by
|
||||
apply Fin.val_inj.mp
|
||||
simp only [val_toFin, toNat_not, Fin.val_rev]
|
||||
omega
|
||||
|
||||
@[simp] theorem getLsb_not {x : BitVec v} : (~~~x).getLsb i = (decide (i < v) && ! x.getLsb i) := by
|
||||
by_cases h' : i < v <;> simp_all [not_def]
|
||||
|
||||
@[simp] theorem truncate_not {x : BitVec w} (h : k ≤ w) :
|
||||
(~~~x).truncate k = ~~~(x.truncate k) := by
|
||||
ext
|
||||
simp [h]
|
||||
omega
|
||||
|
||||
/-! ### shiftLeft -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_shiftLeft {x : BitVec v} :
|
||||
BitVec.toNat (x <<< n) = BitVec.toNat x <<< n % 2^v :=
|
||||
BitVec.toNat_ofNat _ _
|
||||
|
||||
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
|
||||
BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) := rfl
|
||||
|
||||
@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
|
||||
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
|
||||
rw [← testBit_toNat, getLsb]
|
||||
simp only [toNat_shiftLeft, Nat.testBit_mod_two_pow, Nat.testBit_shiftLeft, ge_iff_le]
|
||||
-- This step could be a case bashing tactic.
|
||||
cases h₁ : decide (i < m) <;> cases h₂ : decide (n ≤ i) <;> cases h₃ : decide (i < n)
|
||||
all_goals { simp_all <;> omega }
|
||||
|
||||
@[simp] theorem getMsb_shiftLeft (x : BitVec w) (i) :
|
||||
(x <<< i).getMsb k = x.getMsb (k + i) := by
|
||||
simp only [getMsb, getLsb_shiftLeft]
|
||||
by_cases h : w = 0
|
||||
· subst h; simp
|
||||
have t : w - 1 - k < w := by omega
|
||||
simp only [t]
|
||||
simp only [decide_True, Nat.sub_sub, Bool.true_and, Nat.add_assoc]
|
||||
by_cases h₁ : k < w <;> by_cases h₂ : w - (1 + k) < i <;> by_cases h₃ : k + i < w
|
||||
<;> simp [h₁, h₂, h₃]
|
||||
<;> (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
|
||||
<;> omega
|
||||
|
||||
theorem shiftLeftZeroExtend_eq {x : BitVec w} :
|
||||
shiftLeftZeroExtend x n = zeroExtend (w+n) x <<< n := by
|
||||
apply eq_of_toNat_eq
|
||||
rw [shiftLeftZeroExtend, zeroExtend]
|
||||
split
|
||||
· simp
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
rw [Nat.shiftLeft_eq, Nat.pow_add]
|
||||
exact Nat.mul_lt_mul_of_pos_right (BitVec.toNat_lt x) (Nat.two_pow_pos _)
|
||||
· omega
|
||||
|
||||
@[simp] theorem getLsb_shiftLeftZeroExtend (x : BitVec m) (n : Nat) :
|
||||
getLsb (shiftLeftZeroExtend x n) i = ((! decide (i < n)) && getLsb x (i - n)) := by
|
||||
rw [shiftLeftZeroExtend_eq]
|
||||
simp only [getLsb_shiftLeft, getLsb_zeroExtend]
|
||||
cases h₁ : decide (i < n) <;> cases h₂ : decide (i - n < m + n) <;> cases h₃ : decide (i < m + n)
|
||||
<;> simp_all
|
||||
<;> (rw [getLsb_ge]; omega)
|
||||
|
||||
@[simp] theorem msb_shiftLeftZeroExtend (x : BitVec w) (i : Nat) :
|
||||
(shiftLeftZeroExtend x i).msb = x.msb := by
|
||||
simp [shiftLeftZeroExtend_eq, BitVec.msb]
|
||||
|
||||
/-! ### ushiftRight -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
|
||||
(x >>> i).toNat = x.toNat >>> i := rfl
|
||||
|
||||
@[simp] theorem getLsb_ushiftRight (x : BitVec n) (i j : Nat) :
|
||||
getLsb (x >>> i) j = getLsb x (i+j) := by
|
||||
unfold getLsb ; simp
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
theorem append_def (x : BitVec v) (y : BitVec w) :
|
||||
x ++ y = (shiftLeftZeroExtend x w ||| zeroExtend' (Nat.le_add_left w v) y) := rfl
|
||||
|
||||
@[simp] theorem toNat_append (x : BitVec m) (y : BitVec n) :
|
||||
(x ++ y).toNat = x.toNat <<< n ||| y.toNat :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem getLsb_append {v : BitVec n} {w : BitVec m} :
|
||||
getLsb (v ++ w) i = bif i < m then getLsb w i else getLsb v (i - m) := by
|
||||
simp [append_def]
|
||||
by_cases h : i < m
|
||||
· simp [h]
|
||||
· simp [h]; simp_all
|
||||
|
||||
theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||||
(x ++ y).msb = bif (w == 0) then (y.msb) else (x.msb) := by
|
||||
rw [← append_eq, append]
|
||||
simp [msb_zeroExtend']
|
||||
by_cases h : w = 0
|
||||
· subst h
|
||||
simp [BitVec.msb, getMsb]
|
||||
· rw [cond_eq_if]
|
||||
have q : 0 < w + v := by omega
|
||||
have t : y.getLsb (w + v - 1) = false := getLsb_ge _ _ (by omega)
|
||||
simp [h, q, t, BitVec.msb, getMsb]
|
||||
|
||||
@[simp] theorem truncate_append {x : BitVec w} {y : BitVec v} :
|
||||
(x ++ y).truncate k = if h : k ≤ v then y.truncate k else (x.truncate (k - v) ++ y).cast (by omega) := by
|
||||
apply eq_of_getLsb_eq
|
||||
intro i
|
||||
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, getLsb_append, Bool.true_and]
|
||||
split
|
||||
· have t : i < v := by omega
|
||||
simp [t]
|
||||
· by_cases t : i < v
|
||||
· simp [t]
|
||||
· have t' : i - v < k - v := by omega
|
||||
simp [t, t']
|
||||
|
||||
@[simp] theorem truncate_cons {x : BitVec w} : (cons a x).truncate w = x := by
|
||||
simp [cons]
|
||||
|
||||
/-! ### rev -/
|
||||
|
||||
theorem getLsb_rev (x : BitVec w) (i : Fin w) :
|
||||
x.getLsb i.rev = x.getMsb i := by
|
||||
simp [getLsb, getMsb]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem getMsb_rev (x : BitVec w) (i : Fin w) :
|
||||
x.getMsb i.rev = x.getLsb i := by
|
||||
simp only [← getLsb_rev]
|
||||
simp only [Fin.rev]
|
||||
congr
|
||||
omega
|
||||
|
||||
/-! ### cons -/
|
||||
|
||||
@[simp] theorem toNat_cons (b : Bool) (x : BitVec w) :
|
||||
(cons b x).toNat = (b.toNat <<< w) ||| x.toNat := by
|
||||
let ⟨x, _⟩ := x
|
||||
simp [cons, toNat_append, toNat_ofBool]
|
||||
|
||||
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
|
||||
theorem toNat_cons' {x : BitVec w} :
|
||||
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
|
||||
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.mul_add_lt_is_or, x.isLt]
|
||||
|
||||
@[simp] theorem getLsb_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
|
||||
getLsb (cons b x) i = if i = n then b else getLsb x i := by
|
||||
simp only [getLsb, toNat_cons, Nat.testBit_or]
|
||||
rw [Nat.testBit_shiftLeft]
|
||||
rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
|
||||
· have p1 : ¬(n ≤ i) := by omega
|
||||
have p2 : i ≠ n := by omega
|
||||
simp [p1, p2]
|
||||
· simp [i_eq_n, testBit_toNat]
|
||||
cases b <;> trivial
|
||||
· have p1 : i ≠ n := by omega
|
||||
have p2 : i - n ≠ 0 := by omega
|
||||
simp [p1, p2, Nat.testBit_bool_to_nat]
|
||||
|
||||
@[simp] theorem msb_cons : (cons a x).msb = a := by
|
||||
simp [cons, msb_cast, msb_append]
|
||||
|
||||
theorem truncate_succ (x : BitVec w) :
|
||||
truncate (i+1) x = cons (getLsb x i) (truncate i x) := by
|
||||
apply eq_of_getLsb_eq
|
||||
intro j
|
||||
simp only [getLsb_truncate, getLsb_cons, j.isLt, decide_True, Bool.true_and]
|
||||
if j_eq : j.val = i then
|
||||
simp [j_eq]
|
||||
else
|
||||
have j_lt : j.val < i := Nat.lt_of_le_of_ne (Nat.le_of_succ_le_succ j.isLt) j_eq
|
||||
simp [j_eq, j_lt]
|
||||
|
||||
theorem eq_msb_cons_truncate (x : BitVec (w+1)) : x = (cons x.msb (x.truncate w)) := by
|
||||
ext i
|
||||
simp
|
||||
split <;> rename_i h
|
||||
· simp [BitVec.msb, getMsb, h]
|
||||
· by_cases h' : i < w
|
||||
· simp_all
|
||||
· omega
|
||||
|
||||
/-! ### concat -/
|
||||
|
||||
@[simp] theorem toNat_concat (x : BitVec w) (b : Bool) :
|
||||
(concat x b).toNat = x.toNat * 2 + b.toNat := by
|
||||
apply Nat.eq_of_testBit_eq
|
||||
simp only [concat, toNat_append, Nat.shiftLeft_eq, Nat.pow_one, toNat_ofBool, Nat.testBit_or]
|
||||
cases b
|
||||
· simp
|
||||
· rintro (_ | i)
|
||||
<;> simp [Nat.add_mod, Nat.add_comm, Nat.add_mul_div_right]
|
||||
|
||||
theorem getLsb_concat (x : BitVec w) (b : Bool) (i : Nat) :
|
||||
(concat x b).getLsb i = if i = 0 then b else x.getLsb (i - 1) := by
|
||||
simp only [concat, getLsb, toNat_append, toNat_ofBool, Nat.testBit_or, Nat.shiftLeft_eq]
|
||||
cases i
|
||||
· simp [Nat.mod_eq_of_lt b.toNat_lt]
|
||||
· simp [Nat.div_eq_of_lt b.toNat_lt]
|
||||
|
||||
@[simp] theorem getLsb_concat_zero : (concat x b).getLsb 0 = b := by
|
||||
simp [getLsb_concat]
|
||||
|
||||
@[simp] theorem getLsb_concat_succ : (concat x b).getLsb (i + 1) = x.getLsb i := by
|
||||
simp [getLsb_concat]
|
||||
|
||||
@[simp] theorem not_concat (x : BitVec w) (b : Bool) : ~~~(concat x b) = concat (~~~x) !b := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp [*, Nat.succ_lt_succ]
|
||||
|
||||
@[simp] theorem concat_or_concat (x y : BitVec w) (a b : Bool) :
|
||||
(concat x a) ||| (concat y b) = concat (x ||| y) (a || b) := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp
|
||||
|
||||
@[simp] theorem concat_and_concat (x y : BitVec w) (a b : Bool) :
|
||||
(concat x a) &&& (concat y b) = concat (x &&& y) (a && b) := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp
|
||||
|
||||
@[simp] theorem concat_xor_concat (x y : BitVec w) (a b : Bool) :
|
||||
(concat x a) ^^^ (concat y b) = concat (x ^^^ y) (xor a b) := by
|
||||
ext i; cases i using Fin.succRecOn <;> simp
|
||||
|
||||
/-! ### add -/
|
||||
|
||||
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
|
||||
|
||||
/--
|
||||
Definition of bitvector addition as a nat.
|
||||
-/
|
||||
@[simp, bv_toNat] theorem toNat_add (x y : BitVec w) : (x + y).toNat = (x.toNat + y.toNat) % 2^w := rfl
|
||||
@[simp] theorem toFin_add (x y : BitVec w) : (x + y).toFin = toFin x + toFin y := rfl
|
||||
@[simp] theorem ofFin_add (x : Fin (2^n)) (y : BitVec n) :
|
||||
.ofFin x + y = .ofFin (x + y.toFin) := rfl
|
||||
@[simp] theorem add_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||||
x + .ofFin y = .ofFin (x.toFin + y) := rfl
|
||||
@[simp] theorem ofNat_add_ofNat {n} (x y : Nat) : x#n + y#n = (x + y)#n := by
|
||||
apply eq_of_toNat_eq ; simp [BitVec.ofNat]
|
||||
|
||||
protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) := by
|
||||
apply eq_of_toNat_eq ; simp [Nat.add_assoc]
|
||||
|
||||
protected theorem add_comm (x y : BitVec n) : x + y = y + x := by
|
||||
simp [add_def, Nat.add_comm]
|
||||
|
||||
@[simp] protected theorem add_zero (x : BitVec n) : x + 0#n = x := by simp [add_def]
|
||||
|
||||
@[simp] protected theorem zero_add (x : BitVec n) : 0#n + x = x := by simp [add_def]
|
||||
|
||||
theorem truncate_add (x y : BitVec w) (h : i ≤ w) :
|
||||
(x + y).truncate i = x.truncate i + y.truncate i := by
|
||||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||||
simp [bv_toNat, h, Nat.mod_mod_of_dvd _ dvd]
|
||||
|
||||
/-! ### sub/neg -/
|
||||
|
||||
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n (x.toNat + (2^n - y.toNat)) := by rfl
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_sub {n} (x y : BitVec n) :
|
||||
(x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) := rfl
|
||||
@[simp] theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y := rfl
|
||||
|
||||
@[simp] theorem ofFin_sub (x : Fin (2^n)) (y : BitVec n) : .ofFin x - y = .ofFin (x - y.toFin) :=
|
||||
rfl
|
||||
@[simp] theorem sub_ofFin (x : BitVec n) (y : Fin (2^n)) : x - .ofFin y = .ofFin (x.toFin - y) :=
|
||||
rfl
|
||||
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
|
||||
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
|
||||
theorem ofNat_sub_ofNat {n} (x y : Nat) : x#n - y#n = .ofNat n (x + (2^n - y % 2^n)) := by
|
||||
apply eq_of_toNat_eq ; simp [BitVec.ofNat]
|
||||
|
||||
@[simp] protected theorem sub_zero (x : BitVec n) : x - (0#n) = x := by apply eq_of_toNat_eq ; simp
|
||||
|
||||
@[simp] protected theorem sub_self (x : BitVec n) : x - x = 0#n := by
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_sub]
|
||||
rw [Nat.add_sub_of_le]
|
||||
· simp
|
||||
· exact Nat.le_of_lt x.isLt
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
|
||||
simp [Neg.neg, BitVec.neg]
|
||||
|
||||
theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y := by
|
||||
apply eq_of_toNat_eq
|
||||
simp
|
||||
|
||||
@[simp] theorem neg_zero (n:Nat) : -0#n = 0#n := by apply eq_of_toNat_eq ; simp
|
||||
|
||||
theorem add_sub_cancel (x y : BitVec w) : x + y - y = x := by
|
||||
apply eq_of_toNat_eq
|
||||
have y_toNat_le := Nat.le_of_lt y.toNat_lt
|
||||
rw [toNat_sub, toNat_add, Nat.mod_add_mod, Nat.add_assoc, ← Nat.add_sub_assoc y_toNat_le,
|
||||
Nat.add_sub_cancel_left, Nat.add_mod_right, toNat_mod_cancel]
|
||||
|
||||
theorem negOne_eq_allOnes : -1#w = allOnes w := by
|
||||
apply eq_of_toNat_eq
|
||||
if g : w = 0 then
|
||||
simp [g]
|
||||
else
|
||||
have q : 1 < 2^w := by simp [g]
|
||||
have r : (2^w - 1) < 2^w := by omega
|
||||
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := by rfl
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
|
||||
@[simp] theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) := rfl
|
||||
|
||||
protected theorem mul_comm (x y : BitVec w) : x * y = y * x := by
|
||||
apply eq_of_toFin_eq; simpa using Fin.mul_comm ..
|
||||
instance : Std.Commutative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_comm⟩
|
||||
|
||||
protected theorem mul_assoc (x y z : BitVec w) : x * y * z = x * (y * z) := by
|
||||
apply eq_of_toFin_eq; simpa using Fin.mul_assoc ..
|
||||
instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_assoc⟩
|
||||
|
||||
@[simp] protected theorem mul_one (x : BitVec w) : x * 1#w = x := by
|
||||
cases w
|
||||
· apply Subsingleton.elim
|
||||
· apply eq_of_toNat_eq; simp [Nat.mod_eq_of_lt]
|
||||
|
||||
@[simp] protected theorem one_mul (x : BitVec w) : 1#w * x = x := by
|
||||
rw [BitVec.mul_comm, BitVec.mul_one]
|
||||
|
||||
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
|
||||
right_id := BitVec.mul_one
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bv_toNat] theorem le_def (x y : BitVec n) :
|
||||
x ≤ y ↔ x.toNat ≤ y.toNat := Iff.rfl
|
||||
|
||||
@[simp] theorem le_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||||
x ≤ BitVec.ofFin y ↔ x.toFin ≤ y := Iff.rfl
|
||||
@[simp] theorem ofFin_le (x : Fin (2^n)) (y : BitVec n) :
|
||||
BitVec.ofFin x ≤ y ↔ x ≤ y.toFin := Iff.rfl
|
||||
@[simp] theorem ofNat_le_ofNat {n} (x y : Nat) : (x#n) ≤ (y#n) ↔ x % 2^n ≤ y % 2^n := by
|
||||
simp [le_def]
|
||||
|
||||
@[bv_toNat] theorem lt_def (x y : BitVec n) :
|
||||
x < y ↔ x.toNat < y.toNat := Iff.rfl
|
||||
|
||||
@[simp] theorem lt_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||||
x < BitVec.ofFin y ↔ x.toFin < y := Iff.rfl
|
||||
@[simp] theorem ofFin_lt (x : Fin (2^n)) (y : BitVec n) :
|
||||
BitVec.ofFin x < y ↔ x < y.toFin := Iff.rfl
|
||||
@[simp] theorem ofNat_lt_ofNat {n} (x y : Nat) : (x#n) < (y#n) ↔ x % 2^n < y % 2^n := by
|
||||
simp [lt_def]
|
||||
|
||||
protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x < y := by
|
||||
revert h1 h2
|
||||
let ⟨x, lt⟩ := x
|
||||
let ⟨y, lt⟩ := y
|
||||
simp
|
||||
exact Nat.lt_of_le_of_ne
|
||||
|
||||
/- ! ### intMax -/
|
||||
|
||||
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
|
||||
def intMax (w : Nat) : BitVec w := (2^w - 1)#w
|
||||
|
||||
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
|
||||
simp [intMax, getLsb]
|
||||
|
||||
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
|
||||
have h : 2^w - 1 < 2^w := by
|
||||
have pos : 2^w > 0 := Nat.pow_pos (by decide)
|
||||
omega
|
||||
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
|
||||
|
||||
end BitVec
|
||||
494
src/Init/Data/Bool.lean
Normal file
494
src/Init/Data/Bool.lean
Normal file
@@ -0,0 +1,494 @@
|
||||
/-
|
||||
Copyright (c) 2023 F. G. Dorais. No rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: F. G. Dorais
|
||||
-/
|
||||
prelude
|
||||
import Init.BinderPredicates
|
||||
|
||||
/-- Boolean exclusive or -/
|
||||
abbrev xor : Bool → Bool → Bool := bne
|
||||
|
||||
namespace Bool
|
||||
|
||||
/- Namespaced versions that can be used instead of prefixing `_root_` -/
|
||||
@[inherit_doc not] protected abbrev not := not
|
||||
@[inherit_doc or] protected abbrev or := or
|
||||
@[inherit_doc and] protected abbrev and := and
|
||||
@[inherit_doc xor] protected abbrev xor := xor
|
||||
|
||||
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∀ x, p x) :=
|
||||
match inst true, inst false with
|
||||
| isFalse ht, _ => isFalse fun h => absurd (h _) ht
|
||||
| _, isFalse hf => isFalse fun h => absurd (h _) hf
|
||||
| isTrue ht, isTrue hf => isTrue fun | true => ht | false => hf
|
||||
|
||||
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∃ x, p x) :=
|
||||
match inst true, inst false with
|
||||
| isTrue ht, _ => isTrue ⟨_, ht⟩
|
||||
| _, isTrue hf => isTrue ⟨_, hf⟩
|
||||
| isFalse ht, isFalse hf => isFalse fun | ⟨true, h⟩ => absurd h ht | ⟨false, h⟩ => absurd h hf
|
||||
|
||||
@[simp] theorem default_bool : default = false := rfl
|
||||
|
||||
instance : LE Bool := ⟨(. → .)⟩
|
||||
instance : LT Bool := ⟨(!. && .)⟩
|
||||
|
||||
instance (x y : Bool) : Decidable (x ≤ y) := inferInstanceAs (Decidable (x → y))
|
||||
instance (x y : Bool) : Decidable (x < y) := inferInstanceAs (Decidable (!x && y))
|
||||
|
||||
instance : Max Bool := ⟨or⟩
|
||||
instance : Min Bool := ⟨and⟩
|
||||
|
||||
theorem false_ne_true : false ≠ true := Bool.noConfusion
|
||||
|
||||
theorem eq_false_or_eq_true : (b : Bool) → b = true ∨ b = false := by decide
|
||||
|
||||
theorem eq_false_iff : {b : Bool} → b = false ↔ b ≠ true := by decide
|
||||
|
||||
theorem ne_false_iff : {b : Bool} → b ≠ false ↔ b = true := by decide
|
||||
|
||||
theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
|
||||
|
||||
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
|
||||
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !b := by cases b <;> simp
|
||||
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
|
||||
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem and_self_left : ∀(a b : Bool), (a && (a && b)) = (a && b) := by decide
|
||||
@[simp] theorem and_self_right : ∀(a b : Bool), ((a && b) && b) = (a && b) := by decide
|
||||
|
||||
@[simp] theorem not_and_self : ∀ (x : Bool), (!x && x) = false := by decide
|
||||
@[simp] theorem and_not_self : ∀ (x : Bool), (x && !x) = false := by decide
|
||||
|
||||
/-
|
||||
Added for confluence with `not_and_self` `and_not_self` on term
|
||||
`(b && !b) = true` due to reductions:
|
||||
|
||||
1. `(b = true ∨ !b = true)` via `Bool.and_eq_true`
|
||||
2. `false = true` via `Bool.and_not_self`
|
||||
-/
|
||||
@[simp] theorem eq_true_and_eq_false_self : ∀(b : Bool), (b = true ∧ b = false) ↔ False := by decide
|
||||
@[simp] theorem eq_false_and_eq_true_self : ∀(b : Bool), (b = false ∧ b = true) ↔ False := by decide
|
||||
|
||||
theorem and_comm : ∀ (x y : Bool), (x && y) = (y && x) := by decide
|
||||
|
||||
theorem and_left_comm : ∀ (x y z : Bool), (x && (y && z)) = (y && (x && z)) := by decide
|
||||
theorem and_right_comm : ∀ (x y z : Bool), ((x && y) && z) = ((x && z) && y) := by decide
|
||||
|
||||
/-
|
||||
Bool version `and_iff_left_iff_imp`.
|
||||
|
||||
Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` via
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem and_iff_left_iff_imp : ∀(a b : Bool), ((a && b) = a) ↔ (a → b) := by decide
|
||||
@[simp] theorem and_iff_right_iff_imp : ∀(a b : Bool), ((a && b) = b) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_self_and : ∀(a b : Bool), (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_and_self : ∀(a b : Bool), (b = (a && b)) ↔ (b → a) := by decide
|
||||
|
||||
/-! ### or -/
|
||||
|
||||
@[simp] theorem or_self_left : ∀(a b : Bool), (a || (a || b)) = (a || b) := by decide
|
||||
@[simp] theorem or_self_right : ∀(a b : Bool), ((a || b) || b) = (a || b) := by decide
|
||||
|
||||
@[simp] theorem not_or_self : ∀ (x : Bool), (!x || x) = true := by decide
|
||||
@[simp] theorem or_not_self : ∀ (x : Bool), (x || !x) = true := by decide
|
||||
|
||||
/-
|
||||
Added for confluence with `not_or_self` `or_not_self` on term
|
||||
`(b || !b) = true` due to reductions:
|
||||
1. `(b = true ∨ !b = true)` via `Bool.or_eq_true`
|
||||
2. `true = true` via `Bool.or_not_self`
|
||||
-/
|
||||
@[simp] theorem eq_true_or_eq_false_self : ∀(b : Bool), (b = true ∨ b = false) ↔ True := by decide
|
||||
@[simp] theorem eq_false_or_eq_true_self : ∀(b : Bool), (b = false ∨ b = true) ↔ True := by decide
|
||||
|
||||
/-
|
||||
Bool version `or_iff_left_iff_imp`.
|
||||
|
||||
Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` via
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem or_iff_left_iff_imp : ∀(a b : Bool), ((a || b) = a) ↔ (b → a) := by decide
|
||||
@[simp] theorem or_iff_right_iff_imp : ∀(a b : Bool), ((a || b) = b) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_self_or : ∀(a b : Bool), (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_or_self : ∀(a b : Bool), (b = (a || b)) ↔ (a → b) := by decide
|
||||
|
||||
theorem or_comm : ∀ (x y : Bool), (x || y) = (y || x) := by decide
|
||||
|
||||
theorem or_left_comm : ∀ (x y z : Bool), (x || (y || z)) = (y || (x || z)) := by decide
|
||||
theorem or_right_comm : ∀ (x y z : Bool), ((x || y) || z) = ((x || z) || y) := by decide
|
||||
|
||||
/-! ### distributivity -/
|
||||
|
||||
theorem and_or_distrib_left : ∀ (x y z : Bool), (x && (y || z)) = (x && y || x && z) := by decide
|
||||
theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z || y && z) := by decide
|
||||
|
||||
theorem or_and_distrib_left : ∀ (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
|
||||
theorem or_and_distrib_right : ∀ (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
|
||||
|
||||
/-- De Morgan's law for boolean and -/
|
||||
@[simp] theorem not_and : ∀ (x y : Bool), (!(x && y)) = (!x || !y) := by decide
|
||||
|
||||
/-- De Morgan's law for boolean or -/
|
||||
@[simp] theorem not_or : ∀ (x y : Bool), (!(x || y)) = (!x && !y) := by decide
|
||||
|
||||
theorem and_eq_true_iff (x y : Bool) : (x && y) = true ↔ x = true ∧ y = true :=
|
||||
Iff.of_eq (and_eq_true x y)
|
||||
|
||||
theorem and_eq_false_iff : ∀ (x y : Bool), (x && y) = false ↔ x = false ∨ y = false := by decide
|
||||
|
||||
/-
|
||||
New simp rule that replaces `Bool.and_eq_false_eq_eq_false_or_eq_false` in
|
||||
Mathlib due to confluence:
|
||||
|
||||
Consider the term: `¬((b && c) = true)`:
|
||||
|
||||
1. Reduces to `((b && c) = false)` via `Bool.not_eq_true`
|
||||
2. Reduces to `¬(b = true ∧ c = true)` via `Bool.and_eq_true`.
|
||||
|
||||
|
||||
1. Further reduces to `b = false ∨ c = false` via `Bool.and_eq_false_eq_eq_false_or_eq_false`.
|
||||
2. Further reduces to `b = true → c = false` via `not_and` and `Bool.not_eq_true`.
|
||||
-/
|
||||
@[simp] theorem and_eq_false_imp : ∀ (x y : Bool), (x && y) = false ↔ (x = true → y = false) := by decide
|
||||
|
||||
@[simp] theorem or_eq_true_iff : ∀ (x y : Bool), (x || y) = true ↔ x = true ∨ y = true := by decide
|
||||
|
||||
@[simp] theorem or_eq_false_iff : ∀ (x y : Bool), (x || y) = false ↔ x = false ∧ y = false := by decide
|
||||
|
||||
/-! ### eq/beq/bne -/
|
||||
|
||||
/--
|
||||
These two rules follow trivially by simp, but are needed to avoid non-termination
|
||||
in false_eq and true_eq.
|
||||
-/
|
||||
@[simp] theorem false_eq_true : (false = true) = False := by simp
|
||||
@[simp] theorem true_eq_false : (true = false) = False := by simp
|
||||
|
||||
-- The two lemmas below normalize terms with a constant to the
|
||||
-- right-hand side but risk non-termination if `false_eq_true` and
|
||||
-- `true_eq_false` are disabled.
|
||||
@[simp low] theorem false_eq (b : Bool) : (false = b) = (b = false) := by
|
||||
cases b <;> simp
|
||||
|
||||
@[simp low] theorem true_eq (b : Bool) : (true = b) = (b = true) := by
|
||||
cases b <;> simp
|
||||
|
||||
@[simp] theorem true_beq : ∀b, (true == b) = b := by decide
|
||||
@[simp] theorem false_beq : ∀b, (false == b) = !b := by decide
|
||||
@[simp] theorem beq_true : ∀b, (b == true) = b := by decide
|
||||
@[simp] theorem beq_false : ∀b, (b == false) = !b := by decide
|
||||
|
||||
@[simp] theorem true_bne : ∀(b : Bool), (true != b) = !b := by decide
|
||||
@[simp] theorem false_bne : ∀(b : Bool), (false != b) = b := by decide
|
||||
@[simp] theorem bne_true : ∀(b : Bool), (b != true) = !b := by decide
|
||||
@[simp] theorem bne_false : ∀(b : Bool), (b != false) = b := by decide
|
||||
|
||||
@[simp] theorem not_beq_self : ∀ (x : Bool), ((!x) == x) = false := by decide
|
||||
@[simp] theorem beq_not_self : ∀ (x : Bool), (x == !x) = false := by decide
|
||||
|
||||
@[simp] theorem not_bne_self : ∀ (x : Bool), ((!x) != x) = true := by decide
|
||||
@[simp] theorem bne_not_self : ∀ (x : Bool), (x != !x) = true := by decide
|
||||
|
||||
/-
|
||||
Added for equivalence with `Bool.not_beq_self` and needed for confluence
|
||||
due to `beq_iff_eq`.
|
||||
-/
|
||||
@[simp] theorem not_eq_self : ∀(b : Bool), ((!b) = b) ↔ False := by decide
|
||||
@[simp] theorem eq_not_self : ∀(b : Bool), (b = (!b)) ↔ False := by decide
|
||||
|
||||
@[simp] theorem beq_self_left : ∀(a b : Bool), (a == (a == b)) = b := by decide
|
||||
@[simp] theorem beq_self_right : ∀(a b : Bool), ((a == b) == b) = a := by decide
|
||||
@[simp] theorem bne_self_left : ∀(a b : Bool), (a != (a != b)) = b := by decide
|
||||
@[simp] theorem bne_self_right : ∀(a b : Bool), ((a != b) != b) = a := by decide
|
||||
|
||||
@[simp] theorem not_bne_not : ∀ (x y : Bool), ((!x) != (!y)) = (x != y) := by decide
|
||||
|
||||
@[simp] theorem bne_assoc : ∀ (x y z : Bool), ((x != y) != z) = (x != (y != z)) := by decide
|
||||
|
||||
@[simp] theorem bne_left_inj : ∀ (x y z : Bool), (x != y) = (x != z) ↔ y = z := by decide
|
||||
@[simp] theorem bne_right_inj : ∀ (x y z : Bool), (x != z) = (y != z) ↔ x = y := by decide
|
||||
|
||||
/-! ### coercision related normal forms -/
|
||||
|
||||
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem not_not_eq : ∀ {a b : Bool}, ¬(!a) = b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_iff_coe : ∀(a b : Bool), (a ↔ b) ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem coe_true_iff_false : ∀(a b : Bool), (a ↔ b = false) ↔ a = (!b) := by decide
|
||||
@[simp] theorem coe_false_iff_true : ∀(a b : Bool), (a = false ↔ b) ↔ (!a) = b := by decide
|
||||
@[simp] theorem coe_false_iff_false : ∀(a b : Bool), (a = false ↔ b = false) ↔ (!a) = (!b) := by decide
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
theorem false_xor : ∀ (x : Bool), xor false x = x := false_bne
|
||||
|
||||
theorem xor_false : ∀ (x : Bool), xor x false = x := bne_false
|
||||
|
||||
theorem true_xor : ∀ (x : Bool), xor true x = !x := true_bne
|
||||
|
||||
theorem xor_true : ∀ (x : Bool), xor x true = !x := bne_true
|
||||
|
||||
theorem not_xor_self : ∀ (x : Bool), xor (!x) x = true := not_bne_self
|
||||
|
||||
theorem xor_not_self : ∀ (x : Bool), xor x (!x) = true := bne_not_self
|
||||
|
||||
theorem not_xor : ∀ (x y : Bool), xor (!x) y = !(xor x y) := by decide
|
||||
|
||||
theorem xor_not : ∀ (x y : Bool), xor x (!y) = !(xor x y) := by decide
|
||||
|
||||
theorem not_xor_not : ∀ (x y : Bool), xor (!x) (!y) = (xor x y) := not_bne_not
|
||||
|
||||
theorem xor_self : ∀ (x : Bool), xor x x = false := by decide
|
||||
|
||||
theorem xor_comm : ∀ (x y : Bool), xor x y = xor y x := by decide
|
||||
|
||||
theorem xor_left_comm : ∀ (x y z : Bool), xor x (xor y z) = xor y (xor x z) := by decide
|
||||
|
||||
theorem xor_right_comm : ∀ (x y z : Bool), xor (xor x y) z = xor (xor x z) y := by decide
|
||||
|
||||
theorem xor_assoc : ∀ (x y z : Bool), xor (xor x y) z = xor x (xor y z) := bne_assoc
|
||||
|
||||
theorem xor_left_inj : ∀ (x y z : Bool), xor x y = xor x z ↔ y = z := bne_left_inj
|
||||
|
||||
theorem xor_right_inj : ∀ (x y z : Bool), xor x z = xor y z ↔ x = y := bne_right_inj
|
||||
|
||||
/-! ### le/lt -/
|
||||
|
||||
@[simp] protected theorem le_true : ∀ (x : Bool), x ≤ true := by decide
|
||||
|
||||
@[simp] protected theorem false_le : ∀ (x : Bool), false ≤ x := by decide
|
||||
|
||||
@[simp] protected theorem le_refl : ∀ (x : Bool), x ≤ x := by decide
|
||||
|
||||
@[simp] protected theorem lt_irrefl : ∀ (x : Bool), ¬ x < x := by decide
|
||||
|
||||
protected theorem le_trans : ∀ {x y z : Bool}, x ≤ y → y ≤ z → x ≤ z := by decide
|
||||
|
||||
protected theorem le_antisymm : ∀ {x y : Bool}, x ≤ y → y ≤ x → x = y := by decide
|
||||
|
||||
protected theorem le_total : ∀ (x y : Bool), x ≤ y ∨ y ≤ x := by decide
|
||||
|
||||
protected theorem lt_asymm : ∀ {x y : Bool}, x < y → ¬ y < x := by decide
|
||||
|
||||
protected theorem lt_trans : ∀ {x y z : Bool}, x < y → y < z → x < z := by decide
|
||||
|
||||
protected theorem lt_iff_le_not_le : ∀ {x y : Bool}, x < y ↔ x ≤ y ∧ ¬ y ≤ x := by decide
|
||||
|
||||
protected theorem lt_of_le_of_lt : ∀ {x y z : Bool}, x ≤ y → y < z → x < z := by decide
|
||||
|
||||
protected theorem lt_of_lt_of_le : ∀ {x y z : Bool}, x < y → y ≤ z → x < z := by decide
|
||||
|
||||
protected theorem le_of_lt : ∀ {x y : Bool}, x < y → x ≤ y := by decide
|
||||
|
||||
protected theorem le_of_eq : ∀ {x y : Bool}, x = y → x ≤ y := by decide
|
||||
|
||||
protected theorem ne_of_lt : ∀ {x y : Bool}, x < y → x ≠ y := by decide
|
||||
|
||||
protected theorem lt_of_le_of_ne : ∀ {x y : Bool}, x ≤ y → x ≠ y → x < y := by decide
|
||||
|
||||
protected theorem le_of_lt_or_eq : ∀ {x y : Bool}, x < y ∨ x = y → x ≤ y := by decide
|
||||
|
||||
protected theorem eq_true_of_true_le : ∀ {x : Bool}, true ≤ x → x = true := by decide
|
||||
|
||||
protected theorem eq_false_of_le_false : ∀ {x : Bool}, x ≤ false → x = false := by decide
|
||||
|
||||
/-! ### min/max -/
|
||||
|
||||
@[simp] protected theorem max_eq_or : max = or := rfl
|
||||
|
||||
@[simp] protected theorem min_eq_and : min = and := rfl
|
||||
|
||||
/-! ### injectivity lemmas -/
|
||||
|
||||
theorem not_inj : ∀ {x y : Bool}, (!x) = (!y) → x = y := by decide
|
||||
|
||||
theorem not_inj_iff : ∀ {x y : Bool}, (!x) = (!y) ↔ x = y := by decide
|
||||
|
||||
theorem and_or_inj_right : ∀ {m x y : Bool}, (x && m) = (y && m) → (x || m) = (y || m) → x = y := by
|
||||
decide
|
||||
|
||||
theorem and_or_inj_right_iff :
|
||||
∀ {m x y : Bool}, (x && m) = (y && m) ∧ (x || m) = (y || m) ↔ x = y := by decide
|
||||
|
||||
theorem and_or_inj_left : ∀ {m x y : Bool}, (m && x) = (m && y) → (m || x) = (m || y) → x = y := by
|
||||
decide
|
||||
|
||||
theorem and_or_inj_left_iff :
|
||||
∀ {m x y : Bool}, (m && x) = (m && y) ∧ (m || x) = (m || y) ↔ x = y := by decide
|
||||
|
||||
/-! ## toNat -/
|
||||
|
||||
/-- 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] theorem toNat_true : true.toNat = 1 := rfl
|
||||
|
||||
theorem toNat_le (c : Bool) : c.toNat ≤ 1 := by
|
||||
cases c <;> trivial
|
||||
|
||||
@[deprecated toNat_le] abbrev toNat_le_one := toNat_le
|
||||
|
||||
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
Nat.lt_succ_of_le (toNat_le _)
|
||||
|
||||
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 ↔ b = false := by
|
||||
cases b <;> simp
|
||||
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 ↔ b = true := by
|
||||
cases b <;> simp
|
||||
|
||||
/-! ### ite -/
|
||||
|
||||
@[simp] theorem if_true_left (p : Prop) [h : Decidable p] (f : Bool) :
|
||||
(ite p true f) = (p || f) := by cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem if_false_left (p : Prop) [h : Decidable p] (f : Bool) :
|
||||
(ite p false f) = (!p && f) := by cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem if_true_right (p : Prop) [h : Decidable p] (t : Bool) :
|
||||
(ite p t true) = (!(p : Bool) || t) := by cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem if_false_right (p : Prop) [h : Decidable p] (t : Bool) :
|
||||
(ite p t false) = (p && t) := by cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem ite_eq_true_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
|
||||
(ite p t f = true) = ite p (t = true) (f = true) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem ite_eq_false_distrib (p : Prop) [h : Decidable p] (t f : Bool) :
|
||||
(ite p t f = false) = ite p (t = false) (f = false) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
/-
|
||||
`not_ite_eq_true_eq_true` and related theorems below are added for
|
||||
non-confluence. A motivating example is
|
||||
`¬((if u then b else c) = true)`.
|
||||
|
||||
This reduces to:
|
||||
1. `¬((if u then (b = true) else (c = true))` via `ite_eq_true_distrib`
|
||||
2. `(if u then b c) = false)` via `Bool.not_eq_true`.
|
||||
|
||||
Similar logic holds for `¬((if u then b else c) = false)` and related
|
||||
lemmas.
|
||||
-/
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_true_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = true) (c = true)) ↔ (ite p (b = false) (c = false)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_false_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = false) (c = false)) ↔ (ite p (b = true) (c = true)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_true_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = true) (c = false)) ↔ (ite p (b = false) (c = true)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
@[simp]
|
||||
theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
|
||||
¬(ite p (b = false) (c = true)) ↔ (ite p (b = true) (c = false)) := by
|
||||
cases h with | _ p => simp [p]
|
||||
|
||||
/-
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = true then True else b = true`
|
||||
-/
|
||||
@[simp] theorem eq_false_imp_eq_true : ∀(b:Bool), (b = false → b = true) ↔ (b = true) := by decide
|
||||
|
||||
/-
|
||||
Added for confluence between `if_true_left` and `ite_false_same` on
|
||||
`if b = false then True else b = false`
|
||||
-/
|
||||
@[simp] theorem eq_true_imp_eq_false : ∀(b:Bool), (b = true → b = false) ↔ (b = false) := by decide
|
||||
|
||||
|
||||
/-! ### cond -/
|
||||
|
||||
theorem cond_eq_ite {α} (b : Bool) (t e : α) : cond b t e = if b then t else e := by
|
||||
cases b <;> simp
|
||||
|
||||
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite b x y
|
||||
|
||||
@[simp] theorem cond_not (b : Bool) (t e : α) : cond (!b) t e = cond b e t := by
|
||||
cases b <;> rfl
|
||||
|
||||
@[simp] theorem cond_self (c : Bool) (t : α) : cond c t t = t := by cases c <;> rfl
|
||||
|
||||
/-
|
||||
This is a simp rule in Mathlib, but results in non-confluence that is
|
||||
difficult to fix as decide distributes over propositions.
|
||||
|
||||
A possible fix would be to completely simplify away `cond`, but that
|
||||
is not taken since it could result in major rewriting of code that is
|
||||
otherwise purely about `Bool`.
|
||||
-/
|
||||
theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
|
||||
cond (decide p) t e = if p then t else e := by
|
||||
simp [cond_eq_ite]
|
||||
|
||||
@[simp] theorem cond_eq_ite_iff (a : Bool) (p : Prop) [h : Decidable p] (x y u v : α) :
|
||||
(cond a x y = ite p u v) ↔ ite a x y = ite p u v := by
|
||||
simp [Bool.cond_eq_ite]
|
||||
|
||||
@[simp] theorem ite_eq_cond_iff (p : Prop) [h : Decidable p] (a : Bool) (x y u v : α) :
|
||||
(ite p x y = cond a u v) ↔ ite p x y = ite a u v := by
|
||||
simp [Bool.cond_eq_ite]
|
||||
|
||||
@[simp] theorem cond_eq_true_distrib : ∀(c t f : Bool),
|
||||
(cond c t f = true) = ite (c = true) (t = true) (f = true) := by
|
||||
decide
|
||||
|
||||
@[simp] theorem cond_eq_false_distrib : ∀(c t f : Bool),
|
||||
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
|
||||
|
||||
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
|
||||
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
|
||||
|
||||
@[simp] theorem cond_true_left : ∀(c f : Bool), cond c true f = ( c || f) := by decide
|
||||
@[simp] theorem cond_false_left : ∀(c f : Bool), cond c false f = (!c && f) := by decide
|
||||
@[simp] theorem cond_true_right : ∀(c t : Bool), cond c t true = (!c || t) := by decide
|
||||
@[simp] theorem cond_false_right : ∀(c t : Bool), cond c t false = ( c && t) := by decide
|
||||
|
||||
@[simp] theorem cond_true_same : ∀(c b : Bool), cond c c b = (c || b) := by decide
|
||||
@[simp] theorem cond_false_same : ∀(c b : Bool), cond c b c = (c && b) := by decide
|
||||
|
||||
/-# decidability -/
|
||||
|
||||
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
|
||||
|
||||
@[simp] theorem decide_and (p q : Prop) [dpq : Decidable (p ∧ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
decide (p ∧ q) = (p && q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem decide_or (p q : Prop) [dpq : Decidable (p ∨ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
decide (p ∨ q) = (p || q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[simp] theorem decide_iff_dist (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
decide (p ↔ q) = (decide p == decide q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
end Bool
|
||||
|
||||
export Bool (cond_eq_if)
|
||||
|
||||
/-! ### decide -/
|
||||
|
||||
@[simp] theorem false_eq_decide_iff {p : Prop} [h : Decidable p] : false = decide p ↔ ¬p := by
|
||||
cases h with | _ q => simp [q]
|
||||
|
||||
@[simp] theorem true_eq_decide_iff {p : Prop} [h : Decidable p] : true = decide p ↔ p := by
|
||||
cases h with | _ q => simp [q]
|
||||
72
src/Init/Data/Cast.lean
Normal file
72
src/Init/Data/Cast.lean
Normal file
@@ -0,0 +1,72 @@
|
||||
/-
|
||||
Copyright (c) 2014 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro, Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.Coe
|
||||
|
||||
/-!
|
||||
# `NatCast`
|
||||
|
||||
We introduce the typeclass `NatCast R` for a type `R` with a "canonical
|
||||
homomorphism" `Nat → R`. The typeclass carries the data of the function,
|
||||
but no required axioms.
|
||||
|
||||
This typeclass was introduced to support a uniform `simp` normal form
|
||||
for such morphisms.
|
||||
|
||||
Without such a typeclass, we would have specific coercions such as
|
||||
`Int.ofNat`, but also later the generic coercion from `Nat` into any
|
||||
Mathlib semiring (including `Int`), and we would need to use `simp` to
|
||||
move between them. However `simp` lemmas expressed using a non-normal
|
||||
form on the LHS would then not fire.
|
||||
|
||||
Typically different instances of this class for the same target type `R`
|
||||
are definitionally equal, and so differences in the instance do not
|
||||
block `simp` or `rw`.
|
||||
|
||||
This logic also applies to `Int` and so we also introduce `IntCast` alongside
|
||||
`Int.
|
||||
|
||||
## Note about coercions into arbitrary types:
|
||||
|
||||
Coercions such as `Nat.cast` that go from a concrete structure such as
|
||||
`Nat` to an arbitrary type `R` should be set up as follows:
|
||||
```lean
|
||||
instance : CoeTail Nat R where coe := ...
|
||||
instance : CoeHTCT Nat R where coe := ...
|
||||
```
|
||||
|
||||
It needs to be `CoeTail` instead of `Coe` because otherwise type-class
|
||||
inference would loop when constructing the transitive coercion `Nat →
|
||||
Nat → Nat → ...`. Sometimes we also need to declare the `CoeHTCT`
|
||||
instance if we need to shadow another coercion.
|
||||
-/
|
||||
|
||||
/-- Type class for the canonical homomorphism `Nat → R`. -/
|
||||
class NatCast (R : Type u) where
|
||||
/-- The canonical map `Nat → R`. -/
|
||||
protected natCast : Nat → R
|
||||
|
||||
instance : NatCast Nat where natCast n := n
|
||||
|
||||
/--
|
||||
Canonical homomorphism from `Nat` to a type `R`.
|
||||
|
||||
It contains just the function, with no axioms.
|
||||
In practice, the target type will likely have a (semi)ring structure,
|
||||
and this homomorphism should be a ring homomorphism.
|
||||
|
||||
The prototypical example is `Int.ofNat`.
|
||||
|
||||
This class and `IntCast` exist to allow different libraries with their own types that can be notated as natural numbers to have consistent `simp` normal forms without needing to create coercion simplification sets that are aware of all combinations. Libraries should make it easy to work with `NatCast` where possible. For instance, in Mathlib there will be such a homomorphism (and thus a `NatCast R` instance) whenever `R` is an additive monoid with a `1`.
|
||||
-/
|
||||
@[coe, reducible, match_pattern] protected def Nat.cast {R : Type u} [NatCast R] : Nat → R :=
|
||||
NatCast.natCast
|
||||
|
||||
-- see the notes about coercions into arbitrary types in the module doc-string
|
||||
instance [NatCast R] : CoeTail Nat R where coe := Nat.cast
|
||||
|
||||
-- see the notes about coercions into arbitrary types in the module doc-string
|
||||
instance [NatCast R] : CoeHTCT Nat R where coe := Nat.cast
|
||||
@@ -6,3 +6,6 @@ Author: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Fin.Log2
|
||||
import Init.Data.Fin.Iterate
|
||||
import Init.Data.Fin.Fold
|
||||
import Init.Data.Fin.Lemmas
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/-
|
||||
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Leonardo de Moura
|
||||
Author: Leonardo de Moura, Robert Y. Lewis, Keeley Hoek, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Data.Nat.Bitwise
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
import Init.Coe
|
||||
|
||||
open Nat
|
||||
@@ -106,6 +106,8 @@ instance instOfNat : OfNat (Fin (no_index (n+1))) i where
|
||||
instance : Inhabited (Fin (no_index (n+1))) where
|
||||
default := 0
|
||||
|
||||
@[simp] theorem zero_eta : (⟨0, Nat.zero_lt_succ _⟩ : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
theorem val_ne_of_ne {i j : Fin n} (h : i ≠ j) : val i ≠ val j :=
|
||||
fun h' => absurd (eq_of_val_eq h') h
|
||||
|
||||
@@ -115,6 +117,58 @@ theorem modn_lt : ∀ {m : Nat} (i : Fin n), m > 0 → (modn i m).val < m
|
||||
theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
|
||||
Nat.lt_of_lt_of_le i.isLt h
|
||||
|
||||
protected theorem pos (i : Fin n) : 0 < n :=
|
||||
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
|
||||
|
||||
/-- The greatest value of `Fin (n+1)`. -/
|
||||
@[inline] def last (n : Nat) : Fin (n + 1) := ⟨n, n.lt_succ_self⟩
|
||||
|
||||
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
|
||||
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := ⟨i.1, h⟩
|
||||
|
||||
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
|
||||
@[inline] def castLE (h : n ≤ m) (i : Fin n) : Fin m := ⟨i, Nat.lt_of_lt_of_le i.2 h⟩
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
|
||||
@[inline] def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
|
||||
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
|
||||
@[inline] def castAdd (m) : Fin n → Fin (n + m) :=
|
||||
castLE <| Nat.le_add_right n m
|
||||
|
||||
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
|
||||
@[inline] def castSucc : Fin n → Fin (n + 1) := castAdd 1
|
||||
|
||||
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
|
||||
def addNat (i : Fin n) (m) : Fin (n + m) := ⟨i + m, Nat.add_lt_add_right i.2 _⟩
|
||||
|
||||
/-- `natAdd n i` adds `n` to `i` "on the left". -/
|
||||
def natAdd (n) (i : Fin m) : Fin (n + m) := ⟨n + i, Nat.add_lt_add_left i.2 _⟩
|
||||
|
||||
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
|
||||
@[inline] def rev (i : Fin n) : Fin n := ⟨n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)⟩
|
||||
|
||||
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
|
||||
@[inline] def subNat (m) (i : Fin (n + m)) (h : m ≤ i) : Fin n :=
|
||||
⟨i - m, Nat.sub_lt_right_of_lt_add h i.2⟩
|
||||
|
||||
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
|
||||
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i ≠ 0) : Fin n :=
|
||||
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h
|
||||
|
||||
theorem val_inj {a b : Fin n} : a.1 = b.1 ↔ a = b := ⟨Fin.eq_of_val_eq, Fin.val_eq_of_eq⟩
|
||||
|
||||
theorem val_congr {n : Nat} {a b : Fin n} (h : a = b) : (a : Nat) = (b : Nat) :=
|
||||
Fin.val_inj.mpr h
|
||||
|
||||
theorem val_le_of_le {n : Nat} {a b : Fin n} (h : a ≤ b) : (a : Nat) ≤ (b : Nat) := h
|
||||
|
||||
theorem val_le_of_ge {n : Nat} {a b : Fin n} (h : a ≥ b) : (b : Nat) ≤ (a : Nat) := h
|
||||
|
||||
theorem val_add_one_le_of_lt {n : Nat} {a b : Fin n} (h : a < b) : (a : Nat) + 1 ≤ (b : Nat) := h
|
||||
|
||||
theorem val_add_one_le_of_gt {n : Nat} {a b : Fin n} (h : a > b) : (b : Nat) + 1 ≤ (a : Nat) := h
|
||||
|
||||
end Fin
|
||||
|
||||
instance [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
|
||||
|
||||
21
src/Init/Data/Fin/Fold.lean
Normal file
21
src/Init/Data/Fin/Fold.lean
Normal file
@@ -0,0 +1,21 @@
|
||||
/-
|
||||
Copyright (c) 2023 François G. Dorais. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: François G. Dorais
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Linear
|
||||
|
||||
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop ⟨n, Nat.le_refl n⟩ init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
loop : {i // i ≤ n} → α → α
|
||||
| ⟨0, _⟩, x => x
|
||||
| ⟨i+1, h⟩, x => loop ⟨i, Nat.le_of_lt h⟩ (f ⟨i, h⟩ x)
|
||||
95
src/Init/Data/Fin/Iterate.lean
Normal file
95
src/Init/Data/Fin/Iterate.lean
Normal file
@@ -0,0 +1,95 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.PropLemmas
|
||||
import Init.Data.Fin.Basic
|
||||
|
||||
namespace Fin
|
||||
|
||||
/--
|
||||
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
|
||||
from `P i`.
|
||||
|
||||
See `hIterate` below for more details.
|
||||
-/
|
||||
def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.val+1))
|
||||
(i : Nat) (ubnd : i ≤ n) (a : P i) : P n :=
|
||||
if g : i < n then
|
||||
hIterateFrom P f (i+1) g (f ⟨i, g⟩ a)
|
||||
else
|
||||
have p : i = n := (or_iff_left g).mp (Nat.eq_or_lt_of_le ubnd)
|
||||
_root_.cast (congrArg P p) a
|
||||
termination_by n - i
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogenous 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`.
|
||||
|
||||
Concretely, `hIterate start stop f init` is equal to
|
||||
```lean
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
```
|
||||
|
||||
Because it is heterogenous 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
|
||||
`hIterate_elim` or other more specialized theorems.
|
||||
-/
|
||||
def hIterate (P : Nat → Sort _) {n : Nat} (init : P 0) (f : ∀(i : Fin n), P i.val → P (i.val+1)) :
|
||||
P n :=
|
||||
hIterateFrom P f 0 (Nat.zero_le n) init
|
||||
|
||||
private theorem hIterateFrom_elim {P : Nat → Sort _}(Q : ∀(i : Nat), P i → Prop)
|
||||
{n : Nat}
|
||||
(f : ∀(i : Fin n), P i.val → P (i.val+1))
|
||||
{i : Nat} (ubnd : i ≤ n)
|
||||
(s : P i)
|
||||
(init : Q i s)
|
||||
(step : ∀(k : Fin n) (s : P k.val), Q k.val s → Q (k.val+1) (f k s)) :
|
||||
Q n (hIterateFrom P f i ubnd s) := by
|
||||
let ⟨j, p⟩ := Nat.le.dest ubnd
|
||||
induction j generalizing i ubnd init with
|
||||
| zero =>
|
||||
unfold hIterateFrom
|
||||
have g : ¬ (i < n) := by simp at p; simp [p]
|
||||
have r : Q n (_root_.cast (congrArg P p) s) :=
|
||||
@Eq.rec Nat i (fun k eq => Q k (_root_.cast (congrArg P eq) s)) init n p
|
||||
simp only [g, r, dite_false]
|
||||
| succ j inv =>
|
||||
unfold hIterateFrom
|
||||
have d : Nat.succ i + j = n := by simp [Nat.succ_add]; exact p
|
||||
have g : i < n := Nat.le.intro d
|
||||
simp only [g]
|
||||
exact inv _ _ (step ⟨i,g⟩ s init) d
|
||||
|
||||
/-
|
||||
`hIterate_elim` provides a mechanism for showing that the result of
|
||||
`hIterate` satisifies 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)
|
||||
{n : Nat} (f : ∀(i : Fin n), P i.val → P (i.val+1)) (s : P 0) (init : Q 0 s)
|
||||
(step : ∀(k : Fin n) (s : P k.val), Q k.val s → Q (k.val+1) (f k s)) :
|
||||
Q n (hIterate P s f) := by
|
||||
exact hIterateFrom_elim _ _ _ _ init step
|
||||
|
||||
/-
|
||||
`hIterate_eq`provides a mechanism for replacing `hIterate P s f` with a
|
||||
function `state` showing that matches the steps performed by `hIterate`.
|
||||
|
||||
This allows rewriting incremental code using `hIterate` with a
|
||||
non-incremental state function.
|
||||
-/
|
||||
theorem hIterate_eq {P : Nat → Sort _} (state : ∀(i : Nat), P i)
|
||||
{n : Nat} (f : ∀(i : Fin n), P i.val → P (i.val+1)) (s : P 0)
|
||||
(init : s = state 0)
|
||||
(step : ∀(i : Fin n), f i (state i) = state (i+1)) :
|
||||
hIterate P s f = state n := by
|
||||
apply hIterate_elim (fun i s => s = state i) f s init
|
||||
intro i s s_eq
|
||||
simp only [s_eq, step]
|
||||
834
src/Init/Data/Fin/Lemmas.lean
Normal file
834
src/Init/Data/Fin/Lemmas.lean
Normal file
@@ -0,0 +1,834 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Ext
|
||||
import Init.ByCases
|
||||
import Init.Conv
|
||||
import Init.Omega
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
|
||||
theorem size_pos (i : Fin n) : 0 < n := Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
|
||||
|
||||
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a % m) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
|
||||
rfl
|
||||
|
||||
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a * b) % n) (Nat.mod_lt _ a.size_pos) := rfl
|
||||
|
||||
theorem sub_def (a b : Fin n) : a - b = Fin.mk ((a + (n - b)) % n) (Nat.mod_lt _ a.size_pos) := rfl
|
||||
|
||||
theorem size_pos' : ∀ [Nonempty (Fin n)], 0 < n | ⟨i⟩ => i.size_pos
|
||||
|
||||
@[simp] theorem is_lt (a : Fin n) : (a : Nat) < n := a.2
|
||||
|
||||
theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
|
||||
⟨fun h => ⟨⟨0, h⟩⟩, fun ⟨i⟩ => i.pos⟩
|
||||
|
||||
/-! ### coercions and constructions -/
|
||||
|
||||
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (⟨a, h⟩ : Fin n) = a := rfl
|
||||
|
||||
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
|
||||
|
||||
theorem ext_iff {a b : Fin n} : a = b ↔ a.1 = b.1 := val_inj.symm
|
||||
|
||||
theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
|
||||
|
||||
theorem exists_iff {p : Fin n → Prop} : (∃ i, p i) ↔ ∃ i h, p ⟨i, h⟩ :=
|
||||
⟨fun ⟨⟨i, hi⟩, hpi⟩ => ⟨i, hi, hpi⟩, fun ⟨i, hi, hpi⟩ => ⟨⟨i, hi⟩, hpi⟩⟩
|
||||
|
||||
theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩ :=
|
||||
⟨fun h i hi => h ⟨i, hi⟩, fun h ⟨i, hi⟩ => h i hi⟩
|
||||
|
||||
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
|
||||
(⟨a, ha⟩ : Fin n) = ⟨b, hb⟩ ↔ a = b := ext_iff
|
||||
|
||||
theorem val_mk {m n : Nat} (h : m < n) : (⟨m, h⟩ : Fin n).val = m := rfl
|
||||
|
||||
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
|
||||
a = ⟨k, hk⟩ ↔ (a : Nat) = k := ext_iff
|
||||
|
||||
theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
|
||||
|
||||
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
|
||||
(Fin.ofNat' a is_pos).val = a % n := rfl
|
||||
|
||||
@[deprecated ofNat'_zero_val] theorem ofNat'_zero_val : (Fin.ofNat' 0 h).val = 0 := Nat.zero_mod _
|
||||
|
||||
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem div_val (a b : Fin n) : (a / b).val = a.val / b.val :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
|
||||
rfl
|
||||
|
||||
theorem ite_val {n : Nat} {c : Prop} [Decidable c] {x : c → Fin n} (y : ¬c → Fin n) :
|
||||
(if h : c then x h else y h).val = if h : c then (x h).val else (y h).val := by
|
||||
by_cases c <;> simp [*]
|
||||
|
||||
theorem dite_val {n : Nat} {c : Prop} [Decidable c] {x y : Fin n} :
|
||||
(if c then x else y).val = if c then x.val else y.val := by
|
||||
by_cases c <;> simp [*]
|
||||
|
||||
/-! ### order -/
|
||||
|
||||
theorem le_def {a b : Fin n} : a ≤ b ↔ a.1 ≤ b.1 := .rfl
|
||||
|
||||
theorem lt_def {a b : Fin n} : a < b ↔ a.1 < b.1 := .rfl
|
||||
|
||||
theorem lt_iff_val_lt_val {a b : Fin n} : a < b ↔ a.val < b.val := Iff.rfl
|
||||
|
||||
@[simp] protected theorem not_le {a b : Fin n} : ¬ a ≤ b ↔ b < a := Nat.not_le
|
||||
|
||||
@[simp] protected theorem not_lt {a b : Fin n} : ¬ a < b ↔ b ≤ a := Nat.not_lt
|
||||
|
||||
protected theorem ne_of_lt {a b : Fin n} (h : a < b) : a ≠ b := Fin.ne_of_val_ne (Nat.ne_of_lt h)
|
||||
|
||||
protected theorem ne_of_gt {a b : Fin n} (h : a < b) : b ≠ a := Fin.ne_of_val_ne (Nat.ne_of_gt h)
|
||||
|
||||
protected theorem le_of_lt {a b : Fin n} (h : a < b) : a ≤ b := Nat.le_of_lt h
|
||||
|
||||
theorem is_le (i : Fin (n + 1)) : i ≤ n := Nat.le_of_lt_succ i.is_lt
|
||||
|
||||
@[simp] theorem is_le' {a : Fin n} : a ≤ n := Nat.le_of_lt a.is_lt
|
||||
|
||||
theorem mk_lt_of_lt_val {b : Fin n} {a : Nat} (h : a < b) :
|
||||
(⟨a, Nat.lt_trans h b.is_lt⟩ : Fin n) < b := h
|
||||
|
||||
theorem mk_le_of_le_val {b : Fin n} {a : Nat} (h : a ≤ b) :
|
||||
(⟨a, Nat.lt_of_le_of_lt h b.is_lt⟩ : Fin n) ≤ b := h
|
||||
|
||||
@[simp] theorem mk_le_mk {x y : Nat} {hx hy} : (⟨x, hx⟩ : Fin n) ≤ ⟨y, hy⟩ ↔ x ≤ y := .rfl
|
||||
|
||||
@[simp] theorem mk_lt_mk {x y : Nat} {hx hy} : (⟨x, hx⟩ : Fin n) < ⟨y, hy⟩ ↔ x < y := .rfl
|
||||
|
||||
@[simp] theorem val_zero (n : Nat) : (0 : Fin (n + 1)).1 = 0 := rfl
|
||||
|
||||
@[simp] theorem mk_zero : (⟨0, Nat.succ_pos n⟩ : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
@[simp] theorem zero_le (a : Fin (n + 1)) : 0 ≤ a := Nat.zero_le a.val
|
||||
|
||||
theorem zero_lt_one : (0 : Fin (n + 2)) < 1 := Nat.zero_lt_one
|
||||
|
||||
@[simp] theorem not_lt_zero (a : Fin (n + 1)) : ¬a < 0 := nofun
|
||||
|
||||
theorem pos_iff_ne_zero {a : Fin (n + 1)} : 0 < a ↔ a ≠ 0 := by
|
||||
rw [lt_def, val_zero, Nat.pos_iff_ne_zero, ← val_ne_iff]; rfl
|
||||
|
||||
theorem eq_zero_or_eq_succ {n : Nat} : ∀ i : Fin (n + 1), i = 0 ∨ ∃ j : Fin n, i = j.succ
|
||||
| 0 => .inl rfl
|
||||
| ⟨j + 1, h⟩ => .inr ⟨⟨j, Nat.lt_of_succ_lt_succ h⟩, rfl⟩
|
||||
|
||||
theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j : Fin n, i = j.succ :=
|
||||
(eq_zero_or_eq_succ i).resolve_left hi
|
||||
|
||||
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
|
||||
|
||||
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
|
||||
rw [val_rev, val_rev, ← Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem rev_le_rev {i j : Fin n} : rev i ≤ rev j ↔ j ≤ i := by
|
||||
simp only [le_def, val_rev, Nat.sub_le_sub_iff_left (Nat.succ_le.2 j.is_lt)]
|
||||
exact Nat.succ_le_succ_iff
|
||||
|
||||
@[simp] theorem rev_inj {i j : Fin n} : rev i = rev j ↔ i = j :=
|
||||
⟨fun h => by simpa using congrArg rev h, congrArg _⟩
|
||||
|
||||
theorem rev_eq {n a : Nat} (i : Fin (n + 1)) (h : n = a + i) :
|
||||
rev i = ⟨a, Nat.lt_succ_of_le (h ▸ Nat.le_add_right ..)⟩ := by
|
||||
ext; dsimp
|
||||
conv => lhs; congr; rw [h]
|
||||
rw [Nat.add_assoc, Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem rev_lt_rev {i j : Fin n} : rev i < rev j ↔ j < i := by
|
||||
rw [← Fin.not_le, ← Fin.not_le, rev_le_rev]
|
||||
|
||||
@[simp] theorem val_last (n : Nat) : last n = n := rfl
|
||||
|
||||
theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
|
||||
|
||||
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
|
||||
|
||||
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
|
||||
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
|
||||
|
||||
theorem val_lt_last {i : Fin (n + 1)} : i ≠ last n → (i : Nat) < n :=
|
||||
Decidable.not_imp_comm.1 eq_last_of_not_lt
|
||||
|
||||
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
|
||||
|
||||
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
|
||||
rw [← rev_rev (last _), rev_last]
|
||||
|
||||
/-! ### addition, numerals, and coercion from Nat -/
|
||||
|
||||
@[simp] theorem val_one (n : Nat) : (1 : Fin (n + 2)).val = 1 := rfl
|
||||
|
||||
@[simp] theorem mk_one : (⟨1, Nat.succ_lt_succ (Nat.succ_pos n)⟩ : Fin (n + 2)) = (1 : Fin _) := rfl
|
||||
|
||||
theorem subsingleton_iff_le_one : Subsingleton (Fin n) ↔ n ≤ 1 := by
|
||||
(match n with | 0 | 1 | n+2 => ?_) <;> try simp
|
||||
· exact ⟨nofun⟩
|
||||
· exact ⟨fun ⟨0, _⟩ ⟨0, _⟩ => rfl⟩
|
||||
· exact iff_of_false (fun h => Fin.ne_of_lt zero_lt_one (h.elim ..)) (of_decide_eq_false rfl)
|
||||
|
||||
instance subsingleton_zero : Subsingleton (Fin 0) := subsingleton_iff_le_one.2 (by decide)
|
||||
|
||||
instance subsingleton_one : Subsingleton (Fin 1) := subsingleton_iff_le_one.2 (by decide)
|
||||
|
||||
theorem fin_one_eq_zero (a : Fin 1) : a = 0 := Subsingleton.elim a 0
|
||||
|
||||
theorem add_def (a b : Fin n) : a + b = Fin.mk ((a + b) % n) (Nat.mod_lt _ a.size_pos) := rfl
|
||||
|
||||
theorem val_add (a b : Fin n) : (a + b).val = (a.val + b.val) % n := rfl
|
||||
|
||||
theorem val_add_one_of_lt {n : Nat} {i : Fin n.succ} (h : i < last _) : (i + 1).1 = i + 1 := by
|
||||
match n with
|
||||
| 0 => cases h
|
||||
| n+1 => rw [val_add, val_one, Nat.mod_eq_of_lt (by exact Nat.succ_lt_succ h)]
|
||||
|
||||
@[simp] theorem last_add_one : ∀ n, last n + 1 = 0
|
||||
| 0 => rfl
|
||||
| n + 1 => by ext; rw [val_add, val_zero, val_last, val_one, Nat.mod_self]
|
||||
|
||||
theorem val_add_one {n : Nat} (i : Fin (n + 1)) :
|
||||
((i + 1 : Fin (n + 1)) : Nat) = if i = last _ then (0 : Nat) else i + 1 := by
|
||||
match Nat.eq_or_lt_of_le (le_last i) with
|
||||
| .inl h => cases Fin.eq_of_val_eq h; simp
|
||||
| .inr h => simpa [Fin.ne_of_lt h] using val_add_one_of_lt h
|
||||
|
||||
@[simp] theorem val_two {n : Nat} : (2 : Fin (n + 3)).val = 2 := rfl
|
||||
|
||||
theorem add_one_pos (i : Fin (n + 1)) (h : i < Fin.last n) : (0 : Fin (n + 1)) < i + 1 := by
|
||||
match n with
|
||||
| 0 => cases h
|
||||
| n+1 =>
|
||||
rw [Fin.lt_def, val_last, ← Nat.add_lt_add_iff_right] at h
|
||||
rw [Fin.lt_def, val_add, val_zero, val_one, Nat.mod_eq_of_lt h]
|
||||
exact Nat.zero_lt_succ _
|
||||
|
||||
theorem one_pos : (0 : Fin (n + 2)) < 1 := Nat.succ_pos 0
|
||||
|
||||
theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
|
||||
|
||||
/-! ### succ and casts into larger Fin types -/
|
||||
|
||||
@[simp] theorem val_succ (j : Fin n) : (j.succ : Nat) = j + 1 := rfl
|
||||
|
||||
@[simp] theorem succ_pos (a : Fin n) : (0 : Fin (n + 1)) < a.succ := by
|
||||
simp [Fin.lt_def, Nat.succ_pos]
|
||||
|
||||
@[simp] theorem succ_le_succ_iff {a b : Fin n} : a.succ ≤ b.succ ↔ a ≤ b := Nat.succ_le_succ_iff
|
||||
|
||||
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ ↔ a < b := Nat.succ_lt_succ_iff
|
||||
|
||||
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ ↔ a = b := by
|
||||
refine ⟨fun h => ext ?_, congrArg _⟩
|
||||
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h ▸ Nat.le_refl _)
|
||||
|
||||
theorem succ_ne_zero {n} : ∀ k : Fin n, Fin.succ k ≠ 0
|
||||
| ⟨k, _⟩, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
|
||||
|
||||
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
|
||||
|
||||
/-- Version of `succ_one_eq_two` to be used by `dsimp` -/
|
||||
@[simp] theorem succ_one_eq_two : Fin.succ (1 : Fin (n + 2)) = 2 := rfl
|
||||
|
||||
@[simp] theorem succ_mk (n i : Nat) (h : i < n) :
|
||||
Fin.succ ⟨i, h⟩ = ⟨i + 1, Nat.succ_lt_succ h⟩ := rfl
|
||||
|
||||
theorem mk_succ_pos (i : Nat) (h : i < n) :
|
||||
(0 : Fin (n + 1)) < ⟨i.succ, Nat.add_lt_add_right h 1⟩ := by
|
||||
rw [lt_def, val_zero]; exact Nat.succ_pos i
|
||||
|
||||
theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
|
||||
let n+1 := n
|
||||
rw [← succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
|
||||
|
||||
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k ↔ k = last _ := by
|
||||
simp only [lt_def, val_add, val_last, ext_iff]
|
||||
let ⟨k, hk⟩ := k
|
||||
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
|
||||
| .inl h => cases h; simp [Nat.succ_pos]
|
||||
| .inr hk' => simp [Nat.ne_of_lt hk', Nat.mod_eq_of_lt (Nat.succ_lt_succ hk'), Nat.le_succ]
|
||||
|
||||
@[simp] theorem add_one_le_iff {n : Nat} : ∀ {k : Fin (n + 1)}, k + 1 ≤ k ↔ k = last _ := by
|
||||
match n with
|
||||
| 0 =>
|
||||
intro (k : Fin 1)
|
||||
exact iff_of_true (Subsingleton.elim (α := Fin 1) (k+1) _ ▸ Nat.le_refl _) (fin_one_eq_zero ..)
|
||||
| n + 1 =>
|
||||
intro (k : Fin (n+2))
|
||||
rw [← add_one_lt_iff, lt_def, le_def, Nat.lt_iff_le_and_ne, and_iff_left]
|
||||
rw [val_add_one]
|
||||
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
|
||||
|
||||
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n ≤ k ↔ k = last n := by
|
||||
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
|
||||
|
||||
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 ↔ k < last n := by
|
||||
rw [← Decidable.not_iff_not]; simp
|
||||
|
||||
@[simp] theorem le_zero_iff {n : Nat} {k : Fin (n + 1)} : k ≤ 0 ↔ k = 0 :=
|
||||
⟨fun h => Fin.eq_of_val_eq <| Nat.eq_zero_of_le_zero h, (· ▸ Nat.le_refl _)⟩
|
||||
|
||||
theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
|
||||
Fin.ne_of_gt (one_lt_succ_succ a)
|
||||
|
||||
@[simp] theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castLT_mk (i n m : Nat) (hn : i < n) (hm : i < m) : castLT ⟨i, hn⟩ hm = ⟨i, hm⟩ :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem coe_castLE (h : n ≤ m) (i : Fin n) : (castLE h i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n ≤ m) :
|
||||
castLE h ⟨i, hn⟩ = ⟨i, Nat.lt_of_lt_of_le hn h⟩ := rfl
|
||||
|
||||
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ ≤ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
|
||||
|
||||
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 ≤ n + 1) (i : Fin m) :
|
||||
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
|
||||
|
||||
@[simp] theorem castLE_castLE {k m n} (km : k ≤ m) (mn : m ≤ n) (i : Fin k) :
|
||||
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
|
||||
Fin.ext (by simp only [coe_castLE])
|
||||
|
||||
@[simp] theorem castLE_comp_castLE {k m n} (km : k ≤ m) (mn : m ≤ n) :
|
||||
Fin.castLE mn ∘ Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
|
||||
funext (castLE_castLE km mn)
|
||||
|
||||
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
|
||||
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
|
||||
|
||||
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h ⟨i, hn⟩ = ⟨i, h ▸ hn⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
|
||||
cast h' (cast h i) = cast (Eq.trans h h') i := rfl
|
||||
|
||||
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m ≤ n} : castLE h' = Fin.cast h := rfl
|
||||
|
||||
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n → Fin (n + 0)) = cast rfl := rfl
|
||||
|
||||
theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m := by simp
|
||||
|
||||
@[simp] theorem castAdd_mk (m : Nat) (i : Nat) (h : i < n) :
|
||||
castAdd m ⟨i, h⟩ = ⟨i, Nat.lt_add_right m h⟩ := rfl
|
||||
|
||||
@[simp] theorem castAdd_castLT (m : Nat) (i : Fin (n + m)) (hi : i.val < n) :
|
||||
castAdd m (castLT i hi) = i := rfl
|
||||
|
||||
@[simp] theorem castLT_castAdd (m : Nat) (i : Fin n) :
|
||||
castLT (castAdd m i) (castAdd_lt m i) = i := rfl
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
|
||||
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
|
||||
|
||||
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
|
||||
|
||||
@[simp] theorem cast_castAdd_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
|
||||
cast h (castAdd m' i) = castAdd m i := rfl
|
||||
|
||||
theorem castAdd_castAdd {m n p : Nat} (i : Fin m) :
|
||||
castAdd p (castAdd n i) = cast (Nat.add_assoc ..).symm (castAdd (n + p) i) := rfl
|
||||
|
||||
/-- The cast of the successor is the successor of the cast. See `Fin.succ_cast_eq` for rewriting in
|
||||
the reverse direction. -/
|
||||
@[simp] theorem cast_succ_eq {n' : Nat} (i : Fin n) (h : n.succ = n'.succ) :
|
||||
cast h i.succ = (cast (Nat.succ.inj h) i).succ := rfl
|
||||
|
||||
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
|
||||
(cast h i).succ = cast (by rw [h]) i.succ := rfl
|
||||
|
||||
@[simp] theorem coe_castSucc (i : Fin n) : (Fin.castSucc i : Nat) = i := rfl
|
||||
|
||||
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc ⟨i, h⟩ = ⟨i, Nat.lt.step h⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_castSucc {n' : Nat} {h : n + 1 = n' + 1} {i : Fin n} :
|
||||
cast h (castSucc i) = castSucc (cast (Nat.succ.inj h) i) := rfl
|
||||
|
||||
theorem castSucc_lt_succ (i : Fin n) : Fin.castSucc i < i.succ :=
|
||||
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
|
||||
|
||||
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i ≤ Fin.castSucc j ↔ i < j.succ := by
|
||||
simpa [lt_def, le_def] using Nat.succ_le_succ_iff.symm
|
||||
|
||||
theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
|
||||
Fin.castSucc i < j ↔ i.succ ≤ j := .rfl
|
||||
|
||||
@[simp] theorem succ_last (n : Nat) : (last n).succ = last n.succ := rfl
|
||||
|
||||
@[simp] theorem succ_eq_last_succ {n : Nat} (i : Fin n.succ) :
|
||||
i.succ = last (n + 1) ↔ i = last n := by rw [← succ_last, succ_inj]
|
||||
|
||||
@[simp] theorem castSucc_castLT (i : Fin (n + 1)) (h : (i : Nat) < n) :
|
||||
castSucc (castLT i h) = i := rfl
|
||||
|
||||
@[simp] theorem castLT_castSucc {n : Nat} (a : Fin n) (h : (a : Nat) < n) :
|
||||
castLT (castSucc a) h = a := rfl
|
||||
|
||||
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
|
||||
Fin.castSucc a < Fin.castSucc b ↔ a < b := .rfl
|
||||
|
||||
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b ↔ a = b := by simp [ext_iff]
|
||||
|
||||
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
|
||||
|
||||
@[simp] theorem castSucc_zero : castSucc (0 : Fin (n + 1)) = 0 := rfl
|
||||
|
||||
@[simp] theorem castSucc_one {n : Nat} : castSucc (1 : Fin (n + 2)) = 1 := rfl
|
||||
|
||||
/-- `castSucc i` is positive when `i` is positive -/
|
||||
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
|
||||
simpa [lt_def] using h
|
||||
|
||||
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 ↔ a = 0 := by simp [ext_iff]
|
||||
|
||||
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a ≠ 0 ↔ a ≠ 0 :=
|
||||
not_congr <| castSucc_eq_zero_iff a
|
||||
|
||||
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
|
||||
castSucc (Fin.succ j) = Fin.succ (castSucc j) := by simp [Fin.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
|
||||
cases n
|
||||
· exact a.elim0
|
||||
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
|
||||
|
||||
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
|
||||
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
|
||||
|
||||
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : (∃ j, castSucc j = i) ↔ i ≠ last n :=
|
||||
⟨fun ⟨j, hj⟩ => hj ▸ Fin.ne_of_lt j.castSucc_lt_last,
|
||||
fun hi => ⟨i.castLT <| Fin.val_lt_last hi, rfl⟩⟩
|
||||
|
||||
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = castSucc i.succ := rfl
|
||||
|
||||
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
|
||||
|
||||
@[simp] theorem addNat_one {i : Fin n} : addNat i 1 = i.succ := rfl
|
||||
|
||||
theorem le_coe_addNat (m : Nat) (i : Fin n) : m ≤ addNat i m :=
|
||||
Nat.le_add_left _ _
|
||||
|
||||
@[simp] theorem addNat_mk (n i : Nat) (hi : i < m) :
|
||||
addNat ⟨i, hi⟩ n = ⟨i + n, Nat.add_lt_add_right hi n⟩ := rfl
|
||||
|
||||
@[simp] theorem cast_addNat_zero {n n' : Nat} (i : Fin n) (h : n + 0 = n') :
|
||||
cast h (addNat i 0) = cast ((Nat.add_zero _).symm.trans h) i := rfl
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_addNat_left`. -/
|
||||
theorem addNat_cast {n n' m : Nat} (i : Fin n') (h : n' = n) :
|
||||
addNat (cast h i) m = cast (congrArg (. + m) h) (addNat i m) := rfl
|
||||
|
||||
theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
|
||||
cast h (addNat i m) = addNat (cast (Nat.add_right_cancel h) i) m := rfl
|
||||
|
||||
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
|
||||
cast h (addNat i m') = addNat i m :=
|
||||
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
|
||||
|
||||
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
|
||||
|
||||
@[simp] theorem natAdd_mk (n i : Nat) (hi : i < m) :
|
||||
natAdd n ⟨i, hi⟩ = ⟨n + i, Nat.add_lt_add_left hi n⟩ := rfl
|
||||
|
||||
theorem le_coe_natAdd (m : Nat) (i : Fin n) : m ≤ natAdd m i := Nat.le_add_right ..
|
||||
|
||||
theorem natAdd_zero {n : Nat} : natAdd 0 = cast (Nat.zero_add n).symm := by ext; simp
|
||||
|
||||
/-- For rewriting in the reverse direction, see `Fin.cast_natAdd_right`. -/
|
||||
theorem natAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
|
||||
natAdd m (cast h i) = cast (congrArg _ h) (natAdd m i) := rfl
|
||||
|
||||
theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
|
||||
cast h (natAdd m i) = natAdd m (cast (Nat.add_left_cancel h) i) := rfl
|
||||
|
||||
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
|
||||
cast h (natAdd m' i) = natAdd m i :=
|
||||
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
|
||||
|
||||
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
|
||||
|
||||
theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
|
||||
natAdd m (castAdd p i) = cast (Nat.add_assoc ..) (castAdd p (natAdd m i)) := rfl
|
||||
|
||||
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
|
||||
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
|
||||
ext <| (Nat.add_assoc ..).symm
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
|
||||
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
|
||||
ext <| Nat.zero_add _
|
||||
|
||||
@[simp]
|
||||
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
|
||||
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
|
||||
|
||||
@[simp]
|
||||
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
|
||||
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
|
||||
|
||||
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
|
||||
|
||||
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
|
||||
rfl
|
||||
|
||||
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
|
||||
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
|
||||
|
||||
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
|
||||
rw [← rev_rev (castAdd ..), rev_castAdd, rev_rev]
|
||||
|
||||
theorem rev_castSucc (k : Fin n) : rev (castSucc k) = succ (rev k) := k.rev_castAdd 1
|
||||
|
||||
theorem rev_succ (k : Fin n) : rev (succ k) = castSucc (rev k) := k.rev_addNat 1
|
||||
|
||||
/-! ### pred -/
|
||||
|
||||
@[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
|
||||
|
||||
@[simp]
|
||||
theorem pred_succ (i : Fin n) {h : i.succ ≠ 0} : i.succ.pred h = i := by
|
||||
cases i
|
||||
rfl
|
||||
|
||||
theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin n) :
|
||||
i.pred hi = j ↔ i = j.succ :=
|
||||
⟨fun h => by simp only [← h, Fin.succ_pred], fun h => by simp only [h, Fin.pred_succ]⟩
|
||||
|
||||
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
|
||||
Fin.pred ⟨i + 1, Nat.add_lt_add_right h 1⟩ (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
|
||||
⟨i, h⟩ := by
|
||||
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
|
||||
Fin.pred ⟨i + 1, h₁⟩ h₂ = ⟨i, Nat.lt_of_succ_lt_succ h₁⟩ := pred_mk_succ i _
|
||||
|
||||
-- This is not a simp theorem by default, because `pred_mk_succ` is nicer when it applies.
|
||||
theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w =
|
||||
⟨i - 1, Nat.sub_lt_right_of_lt_add (Nat.pos_iff_ne_zero.2 (Fin.val_ne_of_ne w)) h⟩ :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem pred_le_pred_iff {n : Nat} {a b : Fin n.succ} {ha : a ≠ 0} {hb : b ≠ 0} :
|
||||
a.pred ha ≤ b.pred hb ↔ a ≤ b := by rw [← succ_le_succ_iff, succ_pred, succ_pred]
|
||||
|
||||
@[simp] theorem pred_lt_pred_iff {n : Nat} {a b : Fin n.succ} {ha : a ≠ 0} {hb : b ≠ 0} :
|
||||
a.pred ha < b.pred hb ↔ a < b := by rw [← succ_lt_succ_iff, succ_pred, succ_pred]
|
||||
|
||||
@[simp] theorem pred_inj :
|
||||
∀ {a b : Fin (n + 1)} {ha : a ≠ 0} {hb : b ≠ 0}, a.pred ha = b.pred hb ↔ a = b
|
||||
| ⟨0, _⟩, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
|
||||
| ⟨i + 1, _⟩, ⟨0, _⟩, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
|
||||
| ⟨i + 1, hi⟩, ⟨j + 1, hj⟩, ha, hb => by simp [ext_iff]
|
||||
|
||||
@[simp] theorem pred_one {n : Nat} :
|
||||
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
|
||||
|
||||
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
|
||||
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
|
||||
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
|
||||
exact Nat.add_lt_add_right h 1
|
||||
|
||||
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m ≤ i) : (i.subNat m h : Nat) = i - m := rfl
|
||||
|
||||
@[simp] theorem subNat_mk {i : Nat} (h₁ : i < n + m) (h₂ : m ≤ i) :
|
||||
subNat m ⟨i, h₁⟩ h₂ = ⟨i - m, Nat.sub_lt_right_of_lt_add h₂ h₁⟩ := rfl
|
||||
|
||||
@[simp] theorem pred_castSucc_succ (i : Fin n) :
|
||||
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
|
||||
|
||||
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m ≤ i) : addNat (subNat m i h) m = i :=
|
||||
ext <| Nat.sub_add_cancel h
|
||||
|
||||
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m ≤ addNat i m := le_coe_addNat m i) :
|
||||
subNat m (addNat i m) h = i := ext <| Nat.add_sub_cancel i m
|
||||
|
||||
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n ≤ i) :
|
||||
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [← cast_addNat]; rfl
|
||||
|
||||
/-! ### recursion and induction principles -/
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
|
||||
`(n+1)`-tuple, and `succ n i` defines `(i+1)`-st element of `(n+1)`-tuple based on `n`, `i`, and
|
||||
`i`-th element of `n`-tuple. -/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRec {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive n.succ (0 : Fin (n + 1)))
|
||||
(succ : ∀ n i, motive n i → motive n.succ i.succ) : ∀ {n : Nat} (i : Fin n), motive n i
|
||||
| 0, i => i.elim0
|
||||
| Nat.succ n, ⟨0, _⟩ => by rw [mk_zero]; exact zero n
|
||||
| Nat.succ _, ⟨Nat.succ i, h⟩ => succ _ _ (succRec zero succ ⟨i, Nat.lt_of_succ_lt_succ h⟩)
|
||||
|
||||
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
|
||||
This function has two arguments:
|
||||
`zero n` defines the `0`-th element `motive (n+1) 0` of an `(n+1)`-tuple, and
|
||||
`succ n i` defines the `(i+1)`-st element of an `(n+1)`-tuple based on `n`, `i`,
|
||||
and the `i`-th element of an `n`-tuple.
|
||||
|
||||
A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : ∀ n, Fin n → Sort _}
|
||||
(zero : ∀ n, motive (n + 1) 0) (succ : ∀ n i, motive n i → motive (Nat.succ n) i.succ) :
|
||||
motive n i := i.succRec zero succ
|
||||
|
||||
@[simp] theorem succRecOn_zero {motive : ∀ n, Fin n → Sort _} {zero succ} (n) :
|
||||
@Fin.succRecOn (n + 1) 0 motive zero succ = zero n := by
|
||||
cases n <;> rfl
|
||||
|
||||
@[simp] theorem succRecOn_succ {motive : ∀ n, Fin n → Sort _} {zero succ} {n} (i : Fin n) :
|
||||
@Fin.succRecOn (n + 1) i.succ motive zero succ = succ n i (Fin.succRecOn i zero succ) := by
|
||||
cases i; rfl
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def induction {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) :
|
||||
∀ i : Fin (n + 1), motive i
|
||||
| ⟨0, hi⟩ => by rwa [Fin.mk_zero]
|
||||
| ⟨i+1, hi⟩ => succ ⟨i, Nat.lt_of_succ_lt_succ hi⟩ (induction zero succ ⟨i, Nat.lt_of_succ_lt hi⟩)
|
||||
|
||||
@[simp] theorem induction_zero {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(hs : ∀ i : Fin n, motive (castSucc i) → motive i.succ) :
|
||||
(induction zero hs : ∀ i : Fin (n + 1), motive i) 0 = zero := rfl
|
||||
|
||||
@[simp] theorem induction_succ {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) (i : Fin n) :
|
||||
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
|
||||
|
||||
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
|
||||
This function has two arguments: `zero` handles the base case on `motive 0`,
|
||||
and `succ` defines the inductive step using `motive i.castSucc`.
|
||||
|
||||
A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
|
||||
-/
|
||||
-- FIXME: Performance review
|
||||
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) → Sort _} (zero : motive 0)
|
||||
(succ : ∀ i : Fin n, motive (castSucc i) → motive i.succ) : motive i := induction zero succ i
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
|
||||
`i = j.succ`, `j : Fin n`. -/
|
||||
@[elab_as_elim] def cases {motive : Fin (n + 1) → Sort _}
|
||||
(zero : motive 0) (succ : ∀ i : Fin n, motive i.succ) :
|
||||
∀ i : Fin (n + 1), motive i := induction zero fun i _ => succ i
|
||||
|
||||
@[simp] theorem cases_zero {n} {motive : Fin (n + 1) → Sort _} {zero succ} :
|
||||
@Fin.cases n motive zero succ 0 = zero := rfl
|
||||
|
||||
@[simp] theorem cases_succ {n} {motive : Fin (n + 1) → Sort _} {zero succ} (i : Fin n) :
|
||||
@Fin.cases n motive zero succ i.succ = succ i := rfl
|
||||
|
||||
@[simp] theorem cases_succ' {n} {motive : Fin (n + 1) → Sort _} {zero succ}
|
||||
{i : Nat} (h : i + 1 < n + 1) :
|
||||
@Fin.cases n motive zero succ ⟨i.succ, h⟩ = succ ⟨i, Nat.lt_of_succ_lt_succ h⟩ := rfl
|
||||
|
||||
theorem forall_fin_succ {P : Fin (n + 1) → Prop} : (∀ i, P i) ↔ P 0 ∧ ∀ i : Fin n, P i.succ :=
|
||||
⟨fun H => ⟨H 0, fun _ => H _⟩, fun ⟨H0, H1⟩ i => Fin.cases H0 H1 i⟩
|
||||
|
||||
theorem exists_fin_succ {P : Fin (n + 1) → Prop} : (∃ i, P i) ↔ P 0 ∨ ∃ i : Fin n, P i.succ :=
|
||||
⟨fun ⟨i, h⟩ => Fin.cases Or.inl (fun i hi => Or.inr ⟨i, hi⟩) i h, fun h =>
|
||||
(h.elim fun h => ⟨0, h⟩) fun ⟨i, hi⟩ => ⟨i.succ, hi⟩⟩
|
||||
|
||||
theorem forall_fin_one {p : Fin 1 → Prop} : (∀ i, p i) ↔ p 0 :=
|
||||
⟨fun h => h _, fun h i => Subsingleton.elim i 0 ▸ h⟩
|
||||
|
||||
theorem exists_fin_one {p : Fin 1 → Prop} : (∃ i, p i) ↔ p 0 :=
|
||||
⟨fun ⟨i, h⟩ => Subsingleton.elim i 0 ▸ h, fun h => ⟨_, h⟩⟩
|
||||
|
||||
theorem forall_fin_two {p : Fin 2 → Prop} : (∀ i, p i) ↔ p 0 ∧ p 1 :=
|
||||
forall_fin_succ.trans <| and_congr_right fun _ => forall_fin_one
|
||||
|
||||
theorem exists_fin_two {p : Fin 2 → Prop} : (∃ i, p i) ↔ p 0 ∨ p 1 :=
|
||||
exists_fin_succ.trans <| or_congr_right exists_fin_one
|
||||
|
||||
theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a = b := by
|
||||
simp only [forall_fin_two]; decide
|
||||
|
||||
/--
|
||||
Define `motive i` by reverse induction on `i : Fin (n + 1)` via induction on the underlying `Nat`
|
||||
value. This function has two arguments: `last` handles the base case on `motive (Fin.last n)`,
|
||||
and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
|
||||
-/
|
||||
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive i.succ → motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
if hi : i = Fin.last n then _root_.cast (congrArg motive hi.symm) last
|
||||
else
|
||||
let j : Fin n := ⟨i, Nat.lt_of_le_of_ne (Nat.le_of_lt_succ i.2) fun h => hi (Fin.ext h)⟩
|
||||
cast _ (reverseInduction last cast j.succ)
|
||||
termination_by n + 1 - i
|
||||
decreasing_by decreasing_with
|
||||
-- FIXME: we put the proof down here to avoid getting a dummy `have` in the definition
|
||||
exact Nat.add_sub_add_right .. ▸ Nat.sub_lt_sub_left i.2 (Nat.lt_succ_self i)
|
||||
|
||||
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) → Sort _} {zero succ} :
|
||||
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
|
||||
rw [reverseInduction]; simp
|
||||
|
||||
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) → Sort _} {zero succ}
|
||||
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =
|
||||
succ i (reverseInduction zero succ i.succ) := by
|
||||
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
|
||||
|
||||
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
|
||||
`i = j.castSucc`, `j : Fin n`. -/
|
||||
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) → Sort _} (last : motive (Fin.last n))
|
||||
(cast : ∀ i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
|
||||
reverseInduction last (fun i _ => cast i) i
|
||||
|
||||
@[simp] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) → Sort _} {last cast} :
|
||||
(Fin.lastCases last cast (Fin.last n) : motive (Fin.last n)) = last :=
|
||||
reverseInduction_last ..
|
||||
|
||||
@[simp] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) → Sort _} {last cast}
|
||||
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
|
||||
reverseInduction_castSucc ..
|
||||
|
||||
/-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
|
||||
`j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
|
||||
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) → Sort u}
|
||||
(left : ∀ i, motive (castAdd n i)) (right : ∀ i, motive (natAdd m i))
|
||||
(i : Fin (m + n)) : motive i :=
|
||||
if hi : (i : Nat) < m then (castAdd_castLT n i hi) ▸ (left (castLT i hi))
|
||||
else (natAdd_subNat_cast (Nat.le_of_not_lt hi)) ▸ (right _)
|
||||
|
||||
@[simp] theorem addCases_left {m n : Nat} {motive : Fin (m + n) → Sort _} {left right} (i : Fin m) :
|
||||
addCases (motive := motive) left right (Fin.castAdd n i) = left i := by
|
||||
rw [addCases, dif_pos (castAdd_lt _ _)]; rfl
|
||||
|
||||
@[simp]
|
||||
theorem addCases_right {m n : Nat} {motive : Fin (m + n) → Sort _} {left right} (i : Fin n) :
|
||||
addCases (motive := motive) left right (natAdd m i) = right i := by
|
||||
have : ¬(natAdd m i : Nat) < m := Nat.not_lt.2 (le_coe_natAdd ..)
|
||||
rw [addCases, dif_neg this]; exact eq_of_heq <| (eqRec_heq _ _).trans (by congr 1; simp)
|
||||
|
||||
/-! ### add -/
|
||||
|
||||
@[simp] theorem ofNat'_add (x : Nat) (lt : 0 < n) (y : Fin n) :
|
||||
Fin.ofNat' x lt + y = Fin.ofNat' (x + y.val) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.add_def]
|
||||
|
||||
@[simp] theorem add_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
|
||||
x + Fin.ofNat' y lt = Fin.ofNat' (x.val + y) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.add_def]
|
||||
|
||||
/-! ### sub -/
|
||||
|
||||
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = (a + (n - b)) % n := by
|
||||
cases a; cases b; rfl
|
||||
|
||||
@[simp] theorem ofNat'_sub (x : Nat) (lt : 0 < n) (y : Fin n) :
|
||||
Fin.ofNat' x lt - y = Fin.ofNat' (x + (n - y.val)) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.sub_def]
|
||||
|
||||
@[simp] theorem sub_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
|
||||
x - Fin.ofNat' y lt = Fin.ofNat' (x.val + (n - y % n)) lt := by
|
||||
apply Fin.eq_of_val_eq
|
||||
simp [Fin.ofNat', Fin.sub_def]
|
||||
|
||||
private theorem _root_.Nat.mod_eq_sub_of_lt_two_mul {x n} (h₁ : n ≤ x) (h₂ : x < 2 * n) :
|
||||
x % n = x - n := by
|
||||
rw [Nat.mod_eq, if_pos (by omega), Nat.mod_eq_of_lt (by omega)]
|
||||
|
||||
theorem coe_sub_iff_le {a b : Fin n} : (↑(a - b) : Nat) = a - b ↔ b ≤ a := by
|
||||
rw [sub_def, le_def]
|
||||
dsimp only
|
||||
if h : n ≤ a + (n - b) then
|
||||
rw [Nat.mod_eq_sub_of_lt_two_mul h]
|
||||
all_goals omega
|
||||
else
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
all_goals omega
|
||||
|
||||
theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b := by
|
||||
rw [sub_def, lt_def]
|
||||
dsimp only
|
||||
if h : n ≤ a + (n - b) then
|
||||
rw [Nat.mod_eq_sub_of_lt_two_mul h]
|
||||
all_goals omega
|
||||
else
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
all_goals omega
|
||||
|
||||
/-! ### mul -/
|
||||
|
||||
theorem val_mul {n : Nat} : ∀ a b : Fin n, (a * b).val = a.val * b.val % n
|
||||
| ⟨_, _⟩, ⟨_, _⟩ => rfl
|
||||
|
||||
theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
|
||||
| ⟨_, _⟩, ⟨_, _⟩ => rfl
|
||||
|
||||
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
|
||||
match n with
|
||||
| 0 => exact Subsingleton.elim (α := Fin 1) ..
|
||||
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
|
||||
|
||||
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
|
||||
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
|
||||
|
||||
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
|
||||
apply eq_of_val_eq
|
||||
simp only [val_mul]
|
||||
rw [← Nat.mod_eq_of_lt a.isLt, ← Nat.mod_eq_of_lt b.isLt, ← Nat.mod_eq_of_lt c.isLt]
|
||||
simp only [← Nat.mul_mod, Nat.mul_assoc]
|
||||
|
||||
protected theorem one_mul (k : Fin (n + 1)) : (1 : Fin (n + 1)) * k = k := by
|
||||
rw [Fin.mul_comm, Fin.mul_one]
|
||||
|
||||
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
|
||||
|
||||
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
|
||||
simp [ext_iff, mul_def]
|
||||
|
||||
end Fin
|
||||
|
||||
namespace USize
|
||||
|
||||
@[simp] theorem lt_def {a b : USize} : a < b ↔ a.toNat < b.toNat := .rfl
|
||||
|
||||
@[simp] theorem le_def {a b : USize} : a ≤ b ↔ a.toNat ≤ b.toNat := .rfl
|
||||
|
||||
@[simp] theorem zero_toNat : (0 : USize).toNat = 0 := Nat.zero_mod _
|
||||
|
||||
@[simp] theorem mod_toNat (a b : USize) : (a % b).toNat = a.toNat % b.toNat :=
|
||||
Fin.mod_val ..
|
||||
|
||||
@[simp] theorem div_toNat (a b : USize) : (a / b).toNat = a.toNat / b.toNat :=
|
||||
Fin.div_val ..
|
||||
|
||||
@[simp] theorem modn_toNat (a : USize) (b : Nat) : (a.modn b).toNat = a.toNat % b :=
|
||||
Fin.modn_val ..
|
||||
|
||||
theorem mod_lt (a b : USize) (h : 0 < b) : a % b < b := USize.modn_lt _ (by simp at h; exact h)
|
||||
|
||||
theorem toNat.inj : ∀ {a b : USize}, a.toNat = b.toNat → a = b
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
end USize
|
||||
@@ -5,3 +5,9 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Int.Bitwise
|
||||
import Init.Data.Int.DivMod
|
||||
import Init.Data.Int.DivModLemmas
|
||||
import Init.Data.Int.Gcd
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.Data.Int.Order
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Jeremy Avigad, Leonardo de Moura
|
||||
The integers, with addition, multiplication, and subtraction.
|
||||
-/
|
||||
prelude
|
||||
import Init.Coe
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Data.List.Basic
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
@@ -47,14 +47,35 @@ inductive Int : Type where
|
||||
attribute [extern "lean_nat_to_int"] Int.ofNat
|
||||
attribute [extern "lean_int_neg_succ_of_nat"] Int.negSucc
|
||||
|
||||
instance : Coe Nat Int := ⟨Int.ofNat⟩
|
||||
instance : NatCast Int where natCast n := Int.ofNat n
|
||||
|
||||
instance instOfNat : OfNat Int n where
|
||||
ofNat := Int.ofNat n
|
||||
|
||||
namespace Int
|
||||
|
||||
/--
|
||||
`-[n+1]` is suggestive notation for `negSucc n`, which is the second constructor of
|
||||
`Int` for making strictly negative numbers by mapping `n : Nat` to `-(n + 1)`.
|
||||
-/
|
||||
scoped notation "-[" n "+1]" => negSucc n
|
||||
|
||||
instance : Inhabited Int := ⟨ofNat 0⟩
|
||||
|
||||
@[simp] theorem default_eq_zero : default = (0 : Int) := rfl
|
||||
|
||||
protected theorem zero_ne_one : (0 : Int) ≠ 1 := nofun
|
||||
|
||||
/-! ## Coercions -/
|
||||
|
||||
@[simp] theorem ofNat_eq_coe : Int.ofNat n = Nat.cast n := rfl
|
||||
|
||||
@[simp] theorem ofNat_zero : ((0 : Nat) : Int) = 0 := rfl
|
||||
|
||||
@[simp] theorem ofNat_one : ((1 : Nat) : Int) = 1 := rfl
|
||||
|
||||
theorem ofNat_two : ((2 : Nat) : Int) = 2 := rfl
|
||||
|
||||
/-- Negation of a natural number. -/
|
||||
def negOfNat : Nat → Int
|
||||
| 0 => 0
|
||||
@@ -100,10 +121,10 @@ set_option bootstrap.genMatcherCode false in
|
||||
@[extern "lean_int_add"]
|
||||
protected def add (m n : @& Int) : Int :=
|
||||
match m, n with
|
||||
| ofNat m, ofNat n => ofNat (m + n)
|
||||
| ofNat m, negSucc n => subNatNat m (succ n)
|
||||
| negSucc m, ofNat n => subNatNat n (succ m)
|
||||
| negSucc m, negSucc n => negSucc (succ (m + n))
|
||||
| ofNat m, ofNat n => ofNat (m + n)
|
||||
| ofNat m, -[n +1] => subNatNat m (succ n)
|
||||
| -[m +1], ofNat n => subNatNat n (succ m)
|
||||
| -[m +1], -[n +1] => negSucc (succ (m + n))
|
||||
|
||||
instance : Add Int where
|
||||
add := Int.add
|
||||
@@ -121,10 +142,10 @@ set_option bootstrap.genMatcherCode false in
|
||||
@[extern "lean_int_mul"]
|
||||
protected def mul (m n : @& Int) : Int :=
|
||||
match m, n with
|
||||
| ofNat m, ofNat n => ofNat (m * n)
|
||||
| ofNat m, negSucc n => negOfNat (m * succ n)
|
||||
| negSucc m, ofNat n => negOfNat (succ m * n)
|
||||
| negSucc m, negSucc n => ofNat (succ m * succ n)
|
||||
| ofNat m, ofNat n => ofNat (m * n)
|
||||
| ofNat m, -[n +1] => negOfNat (m * succ n)
|
||||
| -[m +1], ofNat n => negOfNat (succ m * n)
|
||||
| -[m +1], -[n +1] => ofNat (succ m * succ n)
|
||||
|
||||
instance : Mul Int where
|
||||
mul := Int.mul
|
||||
@@ -139,8 +160,7 @@ instance : Mul Int where
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
@[extern "lean_int_sub"]
|
||||
protected def sub (m n : @& Int) : Int :=
|
||||
m + (- n)
|
||||
protected def sub (m n : @& Int) : Int := m + (- n)
|
||||
|
||||
instance : Sub Int where
|
||||
sub := Int.sub
|
||||
@@ -178,11 +198,11 @@ protected def decEq (a b : @& Int) : Decidable (a = b) :=
|
||||
| ofNat a, ofNat b => match decEq a b with
|
||||
| isTrue h => isTrue <| h ▸ rfl
|
||||
| isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
|
||||
| negSucc a, negSucc b => match decEq a b with
|
||||
| ofNat _, -[_ +1] => isFalse <| fun h => Int.noConfusion h
|
||||
| -[_ +1], ofNat _ => isFalse <| fun h => Int.noConfusion h
|
||||
| -[a +1], -[b +1] => match decEq a b with
|
||||
| isTrue h => isTrue <| h ▸ rfl
|
||||
| isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
|
||||
| ofNat _, negSucc _ => isFalse <| fun h => Int.noConfusion h
|
||||
| negSucc _, ofNat _ => isFalse <| fun h => Int.noConfusion h
|
||||
|
||||
instance : DecidableEq Int := Int.decEq
|
||||
|
||||
@@ -199,8 +219,8 @@ set_option bootstrap.genMatcherCode false in
|
||||
@[extern "lean_int_dec_nonneg"]
|
||||
private def decNonneg (m : @& Int) : Decidable (NonNeg m) :=
|
||||
match m with
|
||||
| ofNat m => isTrue <| NonNeg.mk m
|
||||
| negSucc _ => isFalse <| fun h => nomatch h
|
||||
| ofNat m => isTrue <| NonNeg.mk m
|
||||
| -[_ +1] => isFalse <| fun h => nomatch h
|
||||
|
||||
/-- Decides whether `a ≤ b`.
|
||||
|
||||
@@ -241,85 +261,21 @@ set_option bootstrap.genMatcherCode false in
|
||||
@[extern "lean_nat_abs"]
|
||||
def natAbs (m : @& Int) : Nat :=
|
||||
match m with
|
||||
| ofNat m => m
|
||||
| negSucc m => m.succ
|
||||
| ofNat m => m
|
||||
| -[m +1] => m.succ
|
||||
|
||||
/-- Integer division. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention,
|
||||
meaning that it rounds toward zero. Also note that division by zero
|
||||
is defined to equal zero.
|
||||
/-! ## sign -/
|
||||
|
||||
The relation between integer division and modulo is found in [the
|
||||
`Int.mod_add_div` theorem in std][theo mod_add_div] which states
|
||||
that `a % b + b * (a / b) = a`, unconditionally.
|
||||
/--
|
||||
Returns the "sign" of the integer as another integer: `1` for positive numbers,
|
||||
`-1` for negative numbers, and `0` for `0`.
|
||||
-/
|
||||
def sign : Int → Int
|
||||
| Int.ofNat (succ _) => 1
|
||||
| Int.ofNat 0 => 0
|
||||
| -[_+1] => -1
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo mod_add_div]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int) / (0 : Int) -- 0
|
||||
#eval (0 : Int) / (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) / (6 : Int) -- 2
|
||||
#eval (12 : Int) / (-6 : Int) -- -2
|
||||
#eval (-12 : Int) / (6 : Int) -- -2
|
||||
#eval (-12 : Int) / (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int) / (7 : Int) -- 1
|
||||
#eval (12 : Int) / (-7 : Int) -- -1
|
||||
#eval (-12 : Int) / (7 : Int) -- -1
|
||||
#eval (-12 : Int) / (-7 : Int) -- 1
|
||||
```
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
@[extern "lean_int_div"]
|
||||
def div : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
| ofNat m, negSucc n => -ofNat (m / succ n)
|
||||
| negSucc m, ofNat n => -ofNat (succ m / n)
|
||||
| negSucc m, negSucc n => ofNat (succ m / succ n)
|
||||
|
||||
instance : Div Int where
|
||||
div := Int.div
|
||||
|
||||
/-- Integer modulo. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
|
||||
to pair with `Int.div`, meaning that `a % b + b * (a / b) = a`
|
||||
unconditionally (see [`Int.mod_add_div`][theo mod_add_div]). In
|
||||
particular, `a % 0 = a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo mod_add_div]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int) % (0 : Int) -- 7
|
||||
#eval (0 : Int) % (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (6 : Int) -- 0
|
||||
#eval (12 : Int) % (-6 : Int) -- 0
|
||||
#eval (-12 : Int) % (6 : Int) -- 0
|
||||
#eval (-12 : Int) % (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (7 : Int) -- 5
|
||||
#eval (12 : Int) % (-7 : Int) -- 5
|
||||
#eval (-12 : Int) % (7 : Int) -- 2
|
||||
#eval (-12 : Int) % (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
@[extern "lean_int_mod"]
|
||||
def mod : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m % n)
|
||||
| ofNat m, negSucc n => ofNat (m % succ n)
|
||||
| negSucc m, ofNat n => -ofNat (succ m % n)
|
||||
| negSucc m, negSucc n => -ofNat (succ m % succ n)
|
||||
|
||||
instance : Mod Int where
|
||||
mod := Int.mod
|
||||
/-! ## Conversion -/
|
||||
|
||||
/-- Turns an integer into a natural number, negative numbers become
|
||||
`0`.
|
||||
@@ -334,6 +290,25 @@ def toNat : Int → Nat
|
||||
| ofNat n => n
|
||||
| negSucc _ => 0
|
||||
|
||||
/--
|
||||
* If `n : Nat`, then `int.toNat' n = some n`
|
||||
* If `n : Int` is negative, then `int.toNat' n = none`.
|
||||
-/
|
||||
def toNat' : Int → Option Nat
|
||||
| (n : Nat) => some n
|
||||
| -[_+1] => none
|
||||
|
||||
/-! ## divisibility -/
|
||||
|
||||
/--
|
||||
Divisibility of integers. `a ∣ b` (typed as `\|`) says that
|
||||
there is some `c` such that `b = a * c`.
|
||||
-/
|
||||
instance : Dvd Int where
|
||||
dvd a b := Exists (fun c => b = a * c)
|
||||
|
||||
/-! ## Powers -/
|
||||
|
||||
/-- Power of an integer to some natural number.
|
||||
|
||||
```
|
||||
@@ -359,3 +334,27 @@ instance : Min Int := minOfLe
|
||||
instance : Max Int := maxOfLe
|
||||
|
||||
end Int
|
||||
|
||||
/--
|
||||
The canonical homomorphism `Int → R`.
|
||||
In most use cases `R` will have a ring structure and this will be a ring homomorphism.
|
||||
-/
|
||||
class IntCast (R : Type u) where
|
||||
/-- The canonical map `Int → R`. -/
|
||||
protected intCast : Int → R
|
||||
|
||||
instance : IntCast Int where intCast n := n
|
||||
|
||||
/--
|
||||
Apply the canonical homomorphism from `Int` to a type `R` from an `IntCast R` instance.
|
||||
|
||||
In Mathlib there will be such a homomorphism whenever `R` is an additive group with a `1`.
|
||||
-/
|
||||
@[coe, reducible, match_pattern] protected def Int.cast {R : Type u} [IntCast R] : Int → R :=
|
||||
IntCast.intCast
|
||||
|
||||
-- see the notes about coercions into arbitrary types in the module doc-string
|
||||
instance [IntCast R] : CoeTail Int R where coe := Int.cast
|
||||
|
||||
-- see the notes about coercions into arbitrary types in the module doc-string
|
||||
instance [IntCast R] : CoeHTCT Int R where coe := Int.cast
|
||||
|
||||
50
src/Init/Data/Int/Bitwise.lean
Normal file
50
src/Init/Data/Int/Bitwise.lean
Normal file
@@ -0,0 +1,50 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## bit operations -/
|
||||
|
||||
/--
|
||||
Bitwise not
|
||||
|
||||
Interprets the integer as an infinite sequence of bits in two's complement
|
||||
and complements each bit.
|
||||
```
|
||||
~~~(0:Int) = -1
|
||||
~~~(1:Int) = -2
|
||||
~~~(-1:Int) = 0
|
||||
```
|
||||
-/
|
||||
protected def not : Int -> Int
|
||||
| Int.ofNat n => Int.negSucc n
|
||||
| Int.negSucc n => Int.ofNat n
|
||||
|
||||
instance : Complement Int := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Bitwise shift right.
|
||||
|
||||
Conceptually, this treats the integer as an infinite sequence of bits in two's
|
||||
complement and shifts the value to the right.
|
||||
|
||||
```lean
|
||||
( 0b0111:Int) >>> 1 = 0b0011
|
||||
( 0b1000:Int) >>> 1 = 0b0100
|
||||
(-0b1000:Int) >>> 1 = -0b0100
|
||||
(-0b0111:Int) >>> 1 = -0b0100
|
||||
```
|
||||
-/
|
||||
protected def shiftRight : Int → Nat → Int
|
||||
| Int.ofNat n, s => Int.ofNat (n >>> s)
|
||||
| Int.negSucc n, s => Int.negSucc (n >>> s)
|
||||
|
||||
instance : HShiftRight Int Nat Int := ⟨.shiftRight⟩
|
||||
|
||||
end Int
|
||||
201
src/Init/Data/Int/DivMod.lean
Normal file
201
src/Init/Data/Int/DivMod.lean
Normal file
@@ -0,0 +1,201 @@
|
||||
/-
|
||||
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Jeremy Avigad, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
|
||||
open Nat
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## Quotient and remainder
|
||||
|
||||
There are three main conventions for integer division,
|
||||
referred here as the E, F, T rounding conventions.
|
||||
All three pairs satisfy the identity `x % y + (x / y) * y = x` unconditionally,
|
||||
and satisfy `x / 0 = 0` and `x % 0 = x`.
|
||||
-/
|
||||
|
||||
/-! ### T-rounding division -/
|
||||
|
||||
/--
|
||||
`div` uses the [*"T-rounding"*][t-rounding]
|
||||
(**T**runcation-rounding) convention, meaning that it rounds toward
|
||||
zero. Also note that division by zero is defined to equal zero.
|
||||
|
||||
The relation between integer division and modulo is found in
|
||||
`Int.mod_add_div` which states that
|
||||
`a % b + b * (a / b) = a`, unconditionally.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862 [theo
|
||||
mod_add_div]:
|
||||
https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int) / (0 : Int) -- 0
|
||||
#eval (0 : Int) / (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) / (6 : Int) -- 2
|
||||
#eval (12 : Int) / (-6 : Int) -- -2
|
||||
#eval (-12 : Int) / (6 : Int) -- -2
|
||||
#eval (-12 : Int) / (-6 : Int) -- 2
|
||||
|
||||
#eval (12 : Int) / (7 : Int) -- 1
|
||||
#eval (12 : Int) / (-7 : Int) -- -1
|
||||
#eval (-12 : Int) / (7 : Int) -- -1
|
||||
#eval (-12 : Int) / (-7 : Int) -- 1
|
||||
```
|
||||
|
||||
Implemented by efficient native code.
|
||||
-/
|
||||
@[extern "lean_int_div"]
|
||||
def div : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
| ofNat m, -[n +1] => -ofNat (m / succ n)
|
||||
| -[m +1], ofNat n => -ofNat (succ m / n)
|
||||
| -[m +1], -[n +1] => ofNat (succ m / succ n)
|
||||
|
||||
/-- Integer modulo. This function uses the
|
||||
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
|
||||
to pair with `Int.div`, meaning that `a % b + b * (a / b) = a`
|
||||
unconditionally (see [`Int.mod_add_div`][theo mod_add_div]). In
|
||||
particular, `a % 0 = a`.
|
||||
|
||||
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
|
||||
[theo mod_add_div]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
#eval (7 : Int) % (0 : Int) -- 7
|
||||
#eval (0 : Int) % (7 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (6 : Int) -- 0
|
||||
#eval (12 : Int) % (-6 : Int) -- 0
|
||||
#eval (-12 : Int) % (6 : Int) -- 0
|
||||
#eval (-12 : Int) % (-6 : Int) -- 0
|
||||
|
||||
#eval (12 : Int) % (7 : Int) -- 5
|
||||
#eval (12 : Int) % (-7 : Int) -- 5
|
||||
#eval (-12 : Int) % (7 : Int) -- 2
|
||||
#eval (-12 : Int) % (-7 : Int) -- 2
|
||||
```
|
||||
|
||||
Implemented by efficient native code. -/
|
||||
@[extern "lean_int_mod"]
|
||||
def mod : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m % n)
|
||||
| ofNat m, -[n +1] => ofNat (m % succ n)
|
||||
| -[m +1], ofNat n => -ofNat (succ m % n)
|
||||
| -[m +1], -[n +1] => -ofNat (succ m % succ n)
|
||||
|
||||
/-! ### F-rounding division
|
||||
This pair satisfies `fdiv x y = floor (x / y)`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of division uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
-/
|
||||
def fdiv : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
| ofNat (succ m), -[n+1] => -[m / succ n +1]
|
||||
| -[_+1], 0 => 0
|
||||
| -[m+1], ofNat (succ n) => -[m / succ n +1]
|
||||
| -[m+1], -[n+1] => ofNat (succ m / succ n)
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the F-rounding convention
|
||||
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
|
||||
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
|
||||
-/
|
||||
def fmod : Int → Int → Int
|
||||
| 0, _ => 0
|
||||
| ofNat m, ofNat n => ofNat (m % n)
|
||||
| ofNat (succ m), -[n+1] => subNatNat (m % succ n) n
|
||||
| -[m+1], ofNat n => subNatNat n (succ (m % n))
|
||||
| -[m+1], -[n+1] => -ofNat (succ m % succ n)
|
||||
|
||||
/-! ### E-rounding division
|
||||
This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
|
||||
-/
|
||||
|
||||
/--
|
||||
Integer division. This version of `Int.div` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
-/
|
||||
@[extern "lean_int_ediv"]
|
||||
def ediv : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
| ofNat m, -[n+1] => -ofNat (m / succ n)
|
||||
| -[_+1], 0 => 0
|
||||
| -[m+1], ofNat (succ n) => -[m / succ n +1]
|
||||
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
|
||||
|
||||
/--
|
||||
Integer modulus. This version of `Int.mod` uses the E-rounding convention
|
||||
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
|
||||
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
|
||||
-/
|
||||
@[extern "lean_int_emod"]
|
||||
def emod : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, n => ofNat (m % natAbs n)
|
||||
| -[m+1], n => subNatNat (natAbs n) (succ (m % natAbs n))
|
||||
|
||||
/--
|
||||
The Div and Mod syntax uses ediv and emod for compatibility with SMTLIb and mathematical
|
||||
reasoning tends to be easier.
|
||||
-/
|
||||
instance : Div Int where
|
||||
div := Int.ediv
|
||||
instance : Mod Int where
|
||||
mod := Int.emod
|
||||
|
||||
/-!
|
||||
# `bmod` ("balanced" mod)
|
||||
|
||||
Balanced mod (and balanced div) are a division and modulus pair such
|
||||
that `b * (Int.bdiv a b) + Int.bmod a b = a` and `b/2 ≤ Int.bmod a b <
|
||||
b/2` for all `a : Int` and `b > 0`.
|
||||
|
||||
This is used in Omega as well as signed bitvectors.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced modulus. This version of Integer modulus uses the
|
||||
balanced rounding convention, which guarantees that
|
||||
`m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
|
||||
to `x` modulo `m`.
|
||||
|
||||
If `m = 0`, then `bmod x m = x`.
|
||||
-/
|
||||
def bmod (x : Int) (m : Nat) : Int :=
|
||||
let r := x % m
|
||||
if r < (m + 1) / 2 then
|
||||
r
|
||||
else
|
||||
r - m
|
||||
|
||||
/--
|
||||
Balanced division. This returns the unique integer so that
|
||||
`b * (Int.bdiv a b) + Int.bmod a b = a`.
|
||||
-/
|
||||
def bdiv (x : Int) (m : Nat) : Int :=
|
||||
if m = 0 then
|
||||
0
|
||||
else
|
||||
let q := x / m
|
||||
let r := x % m
|
||||
if r < (m + 1) / 2 then
|
||||
q
|
||||
else
|
||||
q + 1
|
||||
|
||||
end Int
|
||||
402
src/Init/Data/Int/DivModLemmas.lean
Normal file
402
src/Init/Data/Int/DivModLemmas.lean
Normal file
@@ -0,0 +1,402 @@
|
||||
/-
|
||||
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Jeremy Avigad, Mario Carneiro
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Int.DivMod
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.Nat.Dvd
|
||||
import Init.RCases
|
||||
import Init.TacticsExtra
|
||||
|
||||
/-!
|
||||
# Lemmas about integer division needed to bootstrap `omega`.
|
||||
-/
|
||||
|
||||
|
||||
open Nat (succ)
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ### `/` -/
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : (↑(m / n) : Int) = ↑m / ↑n := rfl
|
||||
|
||||
@[simp] theorem zero_ediv : ∀ b : Int, 0 / b = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => show -ofNat _ = _ by simp
|
||||
|
||||
@[simp] protected theorem ediv_zero : ∀ a : Int, a / 0 = 0
|
||||
| ofNat _ => show ofNat _ = _ by simp
|
||||
| -[_+1] => rfl
|
||||
|
||||
|
||||
@[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
|
||||
|
||||
protected theorem div_def (a b : Int) : a / b = Int.ediv a b := rfl
|
||||
|
||||
theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c = a / c + b :=
|
||||
suffices ∀ {{a b c : Int}}, 0 < c → (a + b * c).ediv c = a.ediv c + b from
|
||||
match Int.lt_trichotomy c 0 with
|
||||
| Or.inl hlt => by
|
||||
rw [← Int.neg_inj, ← Int.ediv_neg, Int.neg_add, ← Int.ediv_neg, ← Int.neg_mul_neg]
|
||||
exact this (Int.neg_pos_of_neg hlt)
|
||||
| Or.inr (Or.inl HEq) => absurd HEq H
|
||||
| Or.inr (Or.inr hgt) => this hgt
|
||||
suffices ∀ {k n : Nat} {a : Int}, (a + n * k.succ).ediv k.succ = a.ediv k.succ + n from
|
||||
fun a b c H => match c, eq_succ_of_zero_lt H, b with
|
||||
| _, ⟨_, rfl⟩, ofNat _ => this
|
||||
| _, ⟨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
|
||||
| -[m+1] => by
|
||||
show ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
|
||||
if h : m < n * k.succ then
|
||||
rw [← Int.ofNat_sub h, ← Int.ofNat_sub ((Nat.div_lt_iff_lt_mul k.succ_pos).2 h)]
|
||||
apply congrArg ofNat
|
||||
rw [Nat.mul_comm, Nat.mul_sub_div]; rwa [Nat.mul_comm]
|
||||
else
|
||||
have h := Nat.not_lt.1 h
|
||||
have H {a b : Nat} (h : a ≤ b) : (a : Int) + -((b : Int) + 1) = -[b - a +1] := by
|
||||
rw [negSucc_eq, Int.ofNat_sub h]
|
||||
simp only [Int.sub_eq_add_neg, Int.neg_add, Int.neg_neg, Int.add_left_comm, Int.add_assoc]
|
||||
show ediv (↑(n * succ k) + -((m : Int) + 1)) (succ k) = n + -(↑(m / succ k) + 1 : Int)
|
||||
rw [H h, H ((Nat.le_div_iff_mul_le k.succ_pos).2 h)]
|
||||
apply congrArg negSucc
|
||||
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
|
||||
|
||||
theorem add_ediv_of_dvd_right {a b c : Int} (H : c ∣ b) : (a + b) / c = a / c + b / c :=
|
||||
if h : c = 0 then by simp [h] else by
|
||||
let ⟨k, hk⟩ := H
|
||||
rw [hk, Int.mul_comm c k, Int.add_mul_ediv_right _ _ h,
|
||||
← Int.zero_add (k * c), Int.add_mul_ediv_right _ _ h, Int.zero_ediv, Int.zero_add]
|
||||
|
||||
theorem add_ediv_of_dvd_left {a b c : Int} (H : c ∣ a) : (a + b) / c = a / c + b / c := by
|
||||
rw [Int.add_comm, Int.add_ediv_of_dvd_right H, Int.add_comm]
|
||||
|
||||
@[simp] theorem mul_ediv_cancel (a : Int) {b : Int} (H : b ≠ 0) : (a * b) / b = a := by
|
||||
have := Int.add_mul_ediv_right 0 a H
|
||||
rwa [Int.zero_add, Int.zero_ediv, Int.zero_add] at this
|
||||
|
||||
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a ≠ 0) : (a * b) / a = b :=
|
||||
Int.mul_comm .. ▸ Int.mul_ediv_cancel _ H
|
||||
|
||||
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0 := by
|
||||
rw [Int.div_def]
|
||||
match b, h with
|
||||
| Int.ofNat (b+1), _ =>
|
||||
rcases a with ⟨a⟩ <;> simp [Int.ediv]
|
||||
exact decide_eq_decide.mp rfl
|
||||
|
||||
/-! ### mod -/
|
||||
|
||||
theorem mod_def' (m n : Int) : m % n = emod m n := rfl
|
||||
|
||||
theorem ofNat_mod (m n : Nat) : (↑(m % n) : Int) = mod m n := rfl
|
||||
|
||||
theorem ofNat_mod_ofNat (m n : Nat) : (m % n : Int) = ↑(m % n) := rfl
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := rfl
|
||||
|
||||
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := by simp [mod_def', emod]
|
||||
|
||||
@[simp] theorem emod_zero : ∀ a : Int, a % 0 = a
|
||||
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
|
||||
| -[_+1] => congrArg negSucc <| Nat.mod_zero _
|
||||
|
||||
theorem emod_add_ediv : ∀ a b : Int, a % b + b * (a / b) = a
|
||||
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
|
||||
| ofNat m, -[n+1] => by
|
||||
show (m % succ n + -↑(succ n) * -↑(m / succ n) : Int) = m
|
||||
rw [Int.neg_mul_neg]; exact congrArg ofNat <| Nat.mod_add_div ..
|
||||
| -[_+1], 0 => by rw [emod_zero]; rfl
|
||||
| -[m+1], succ n => aux m n.succ
|
||||
| -[m+1], -[n+1] => aux m n.succ
|
||||
where
|
||||
aux (m n : Nat) : n - (m % n + 1) - (n * (m / n) + n) = -[m+1] := by
|
||||
rw [← ofNat_emod, ← ofNat_ediv, ← Int.sub_sub, negSucc_eq, Int.sub_sub n,
|
||||
← Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
|
||||
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
|
||||
|
||||
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a :=
|
||||
(Int.add_comm ..).trans (emod_add_ediv ..)
|
||||
|
||||
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
|
||||
rw [← Int.add_sub_cancel (a % b), emod_add_ediv]
|
||||
|
||||
theorem emod_nonneg : ∀ (a : Int) {b : Int}, b ≠ 0 → 0 ≤ a % b
|
||||
| ofNat _, _, _ => ofNat_zero_le _
|
||||
| -[_+1], _, H => Int.sub_nonneg_of_le <| ofNat_le.2 <| Nat.mod_lt _ (natAbs_pos.2 H)
|
||||
|
||||
theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
|
||||
match a, b, eq_succ_of_zero_lt H with
|
||||
| ofNat _, _, ⟨_, rfl⟩ => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
|
||||
| -[_+1], _, ⟨_, rfl⟩ => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
|
||||
|
||||
theorem mul_ediv_self_le {x k : Int} (h : k ≠ 0) : k * (x / k) ≤ x :=
|
||||
calc k * (x / k)
|
||||
_ ≤ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
|
||||
_ = x := ediv_add_emod _ _
|
||||
|
||||
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
|
||||
calc x
|
||||
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
|
||||
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
|
||||
|
||||
theorem emod_add_ediv' (m k : Int) : m % k + m / k * k = m := by
|
||||
rw [Int.mul_comm]; apply emod_add_ediv
|
||||
|
||||
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
|
||||
if cz : c = 0 then by
|
||||
rw [cz, Int.mul_zero, Int.add_zero]
|
||||
else by
|
||||
rw [Int.emod_def, Int.emod_def, Int.add_mul_ediv_right _ _ cz, Int.add_comm _ b,
|
||||
Int.mul_add, Int.mul_comm, ← Int.sub_sub, Int.add_sub_cancel]
|
||||
|
||||
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
|
||||
rw [Int.mul_comm, Int.add_mul_emod_self]
|
||||
|
||||
@[simp] theorem add_emod_self {a b : Int} : (a + b) % b = a % b := by
|
||||
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
|
||||
|
||||
@[simp] theorem add_emod_self_left {a b : Int} : (a + b) % a = b % a := by
|
||||
rw [Int.add_comm, Int.add_emod_self]
|
||||
|
||||
theorem neg_emod {a b : Int} : -a % b = (b - a) % b := by
|
||||
rw [← add_emod_self_left]; rfl
|
||||
|
||||
@[simp] theorem emod_add_emod (m n k : Int) : (m % n + k) % n = (m + k) % n := by
|
||||
have := (add_mul_emod_self_left (m % n + k) n (m / n)).symm
|
||||
rwa [Int.add_right_comm, emod_add_ediv] at this
|
||||
|
||||
@[simp] theorem add_emod_emod (m n k : Int) : (m + n % k) % k = (m + n) % k := by
|
||||
rw [Int.add_comm, emod_add_emod, Int.add_comm]
|
||||
|
||||
theorem add_emod (a b n : Int) : (a + b) % n = (a % n + b % n) % n := by
|
||||
rw [add_emod_emod, emod_add_emod]
|
||||
|
||||
theorem add_emod_eq_add_emod_right {m n k : Int} (i : Int)
|
||||
(H : m % n = k % n) : (m + i) % n = (k + i) % n := by
|
||||
rw [← emod_add_emod, ← emod_add_emod k, H]
|
||||
|
||||
theorem emod_add_cancel_right {m n k : Int} (i) : (m + i) % n = (k + i) % n ↔ m % n = k % n :=
|
||||
⟨fun H => by
|
||||
have := add_emod_eq_add_emod_right (-i) H
|
||||
rwa [Int.add_neg_cancel_right, Int.add_neg_cancel_right] at this,
|
||||
add_emod_eq_add_emod_right _⟩
|
||||
|
||||
@[simp] theorem mul_emod_left (a b : Int) : (a * b) % b = 0 := by
|
||||
rw [← Int.zero_add (a * b), Int.add_mul_emod_self, Int.zero_emod]
|
||||
|
||||
@[simp] theorem mul_emod_right (a b : Int) : (a * b) % a = 0 := by
|
||||
rw [Int.mul_comm, mul_emod_left]
|
||||
|
||||
theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
|
||||
conv => lhs; rw [
|
||||
← emod_add_ediv a n, ← emod_add_ediv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
|
||||
Int.mul_assoc, Int.mul_assoc, ← Int.mul_add n _ _, add_mul_emod_self_left,
|
||||
← Int.mul_assoc, add_mul_emod_self]
|
||||
|
||||
@[local simp] theorem emod_self {a : Int} : a % a = 0 := by
|
||||
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
|
||||
(h : m ∣ k) : (n % k) % m = n % m := by
|
||||
conv => rhs; rw [← emod_add_ediv n k]
|
||||
match k, h with
|
||||
| _, ⟨t, rfl⟩ => rw [Int.mul_assoc, add_mul_emod_self_left]
|
||||
|
||||
@[simp] theorem emod_emod (a b : Int) : (a % b) % b = a % b := by
|
||||
conv => rhs; rw [← emod_add_ediv a b, add_mul_emod_self_left]
|
||||
|
||||
theorem sub_emod (a b n : Int) : (a - b) % n = (a % n - b % n) % n := by
|
||||
apply (emod_add_cancel_right b).mp
|
||||
rw [Int.sub_add_cancel, ← Int.add_emod_emod, Int.sub_add_cancel, emod_emod]
|
||||
|
||||
/-! ### properties of `/` and `%` -/
|
||||
|
||||
theorem mul_ediv_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : b * (a / b) = a := by
|
||||
have := emod_add_ediv a b; rwa [H, Int.zero_add] at this
|
||||
|
||||
theorem ediv_mul_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : a / b * b = a := by
|
||||
rw [Int.mul_comm, mul_ediv_cancel_of_emod_eq_zero H]
|
||||
|
||||
/-! ### dvd -/
|
||||
|
||||
protected theorem dvd_zero (n : Int) : n ∣ 0 := ⟨0, (Int.mul_zero _).symm⟩
|
||||
|
||||
protected theorem dvd_refl (n : Int) : n ∣ n := ⟨1, (Int.mul_one _).symm⟩
|
||||
|
||||
protected theorem one_dvd (n : Int) : 1 ∣ n := ⟨n, (Int.one_mul n).symm⟩
|
||||
|
||||
protected theorem dvd_trans : ∀ {a b c : Int}, a ∣ b → b ∣ c → a ∣ c
|
||||
| _, _, _, ⟨d, rfl⟩, ⟨e, rfl⟩ => ⟨d * e, by rw [Int.mul_assoc]⟩
|
||||
|
||||
@[simp] protected theorem zero_dvd {n : Int} : 0 ∣ n ↔ n = 0 :=
|
||||
⟨fun ⟨k, e⟩ => by rw [e, Int.zero_mul], fun h => h.symm ▸ Int.dvd_refl _⟩
|
||||
|
||||
protected theorem neg_dvd {a b : Int} : -a ∣ b ↔ a ∣ b := by
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
protected theorem dvd_neg {a b : Int} : a ∣ -b ↔ a ∣ b := by
|
||||
constructor <;> exact fun ⟨k, e⟩ =>
|
||||
⟨-k, by simp [← e, Int.neg_mul, Int.mul_neg, Int.neg_neg]⟩
|
||||
|
||||
protected theorem dvd_mul_right (a b : Int) : a ∣ a * b := ⟨_, rfl⟩
|
||||
|
||||
protected theorem dvd_mul_left (a b : Int) : b ∣ a * b := ⟨_, Int.mul_comm ..⟩
|
||||
|
||||
protected theorem dvd_add : ∀ {a b c : Int}, a ∣ b → a ∣ c → a ∣ b + c
|
||||
| _, _, _, ⟨d, rfl⟩, ⟨e, rfl⟩ => ⟨d + e, by rw [Int.mul_add]⟩
|
||||
|
||||
protected theorem dvd_sub : ∀ {a b c : Int}, a ∣ b → a ∣ c → a ∣ b - c
|
||||
| _, _, _, ⟨d, rfl⟩, ⟨e, rfl⟩ => ⟨d - e, by rw [Int.mul_sub]⟩
|
||||
|
||||
|
||||
@[norm_cast] theorem ofNat_dvd {m n : Nat} : (↑m : Int) ∣ ↑n ↔ m ∣ n := by
|
||||
refine ⟨fun ⟨a, ae⟩ => ?_, fun ⟨k, e⟩ => ⟨k, by rw [e, Int.ofNat_mul]⟩⟩
|
||||
match Int.le_total a 0 with
|
||||
| .inl h =>
|
||||
have := ae.symm ▸ Int.mul_nonpos_of_nonneg_of_nonpos (ofNat_zero_le _) h
|
||||
rw [Nat.le_antisymm (ofNat_le.1 this) (Nat.zero_le _)]
|
||||
apply Nat.dvd_zero
|
||||
| .inr h => match a, eq_ofNat_of_zero_le h with
|
||||
| _, ⟨k, rfl⟩ => exact ⟨k, Int.ofNat.inj ae⟩
|
||||
|
||||
@[simp] theorem natAbs_dvd_natAbs {a b : Int} : natAbs a ∣ natAbs b ↔ a ∣ b := by
|
||||
refine ⟨fun ⟨k, hk⟩ => ?_, fun ⟨k, hk⟩ => ⟨natAbs k, hk.symm ▸ natAbs_mul a k⟩⟩
|
||||
rw [← natAbs_ofNat k, ← natAbs_mul, natAbs_eq_natAbs_iff] at hk
|
||||
cases hk <;> subst b
|
||||
· apply Int.dvd_mul_right
|
||||
· rw [← Int.mul_neg]; apply Int.dvd_mul_right
|
||||
|
||||
theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) ∣ z ↔ n ∣ z.natAbs := by
|
||||
rw [← natAbs_dvd_natAbs, natAbs_ofNat]
|
||||
|
||||
theorem dvd_of_emod_eq_zero {a b : Int} (H : b % a = 0) : a ∣ b :=
|
||||
⟨b / a, (mul_ediv_cancel_of_emod_eq_zero H).symm⟩
|
||||
|
||||
theorem dvd_emod_sub_self {x : Int} {m : Nat} : (m : Int) ∣ x % m - x := by
|
||||
apply dvd_of_emod_eq_zero
|
||||
simp [sub_emod]
|
||||
|
||||
theorem emod_eq_zero_of_dvd : ∀ {a b : Int}, a ∣ b → b % a = 0
|
||||
| _, _, ⟨_, rfl⟩ => mul_emod_right ..
|
||||
|
||||
theorem dvd_iff_emod_eq_zero (a b : Int) : a ∣ b ↔ b % a = 0 :=
|
||||
⟨emod_eq_zero_of_dvd, dvd_of_emod_eq_zero⟩
|
||||
|
||||
theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a ∣ b) : a = 0 ∨ 0 < b % a := by
|
||||
rw [dvd_iff_emod_eq_zero] at h
|
||||
if w : a = 0 then simp_all
|
||||
else exact Or.inr (Int.lt_iff_le_and_ne.mpr ⟨emod_nonneg b w, Ne.symm h⟩)
|
||||
|
||||
instance decidableDvd : DecidableRel (α := Int) (· ∣ ·) := fun _ _ =>
|
||||
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
|
||||
|
||||
protected theorem ediv_mul_cancel {a b : Int} (H : b ∣ a) : a / b * b = a :=
|
||||
ediv_mul_cancel_of_emod_eq_zero (emod_eq_zero_of_dvd H)
|
||||
|
||||
protected theorem mul_ediv_cancel' {a b : Int} (H : a ∣ b) : a * (b / a) = b := by
|
||||
rw [Int.mul_comm, Int.ediv_mul_cancel H]
|
||||
|
||||
protected theorem mul_ediv_assoc (a : Int) : ∀ {b c : Int}, c ∣ b → (a * b) / c = a * (b / c)
|
||||
| _, c, ⟨d, rfl⟩ =>
|
||||
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
|
||||
rw [Int.mul_left_comm, Int.mul_ediv_cancel_left _ cz, Int.mul_ediv_cancel_left _ cz]
|
||||
|
||||
protected theorem mul_ediv_assoc' (b : Int) {a c : Int}
|
||||
(h : c ∣ a) : (a * b) / c = a / c * b := by
|
||||
rw [Int.mul_comm, Int.mul_ediv_assoc _ h, Int.mul_comm]
|
||||
|
||||
theorem neg_ediv_of_dvd : ∀ {a b : Int}, b ∣ a → (-a) / b = -(a / b)
|
||||
| _, b, ⟨c, rfl⟩ => by if bz : b = 0 then simp [bz] else
|
||||
rw [Int.neg_mul_eq_mul_neg, Int.mul_ediv_cancel_left _ bz, Int.mul_ediv_cancel_left _ bz]
|
||||
|
||||
theorem sub_ediv_of_dvd (a : Int) {b c : Int}
|
||||
(hcb : c ∣ b) : (a - b) / c = a / c - b / c := by
|
||||
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
|
||||
congr; exact Int.neg_ediv_of_dvd hcb
|
||||
|
||||
@[simp] theorem ediv_one : ∀ a : Int, a / 1 = a
|
||||
| (_:Nat) => congrArg Nat.cast (Nat.div_one _)
|
||||
| -[_+1] => congrArg negSucc (Nat.div_one _)
|
||||
|
||||
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
|
||||
simp [emod_def, Int.one_mul, Int.sub_self]
|
||||
|
||||
@[simp] protected theorem ediv_self {a : Int} (H : a ≠ 0) : a / a = 1 := by
|
||||
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
|
||||
|
||||
@[simp]
|
||||
theorem Int.emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
|
||||
if h : y = 0 then
|
||||
simp [h]
|
||||
else
|
||||
simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
|
||||
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
|
||||
|
||||
/-! bmod -/
|
||||
|
||||
@[simp] theorem bmod_emod : bmod x m % m = x % m := by
|
||||
dsimp [bmod]
|
||||
split <;> simp [Int.sub_emod]
|
||||
|
||||
@[simp]
|
||||
theorem emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n := by
|
||||
simp [bmod, Int.emod_emod]
|
||||
|
||||
theorem bmod_def (x : Int) (m : Nat) : bmod x m =
|
||||
if (x % m) < (m + 1) / 2 then
|
||||
x % m
|
||||
else
|
||||
(x % m) - m :=
|
||||
rfl
|
||||
|
||||
theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x % m := by
|
||||
simp [bmod_def, p]
|
||||
|
||||
theorem bmod_neg (x : Int) (m : Nat) (p : x % m ≥ (m + 1) / 2) : bmod x m = (x % m) - m := by
|
||||
simp [bmod_def, Int.not_lt.mpr p]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_one_is_zero (x : Int) : Int.bmod x 1 = 0 := by
|
||||
simp [Int.bmod]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_add_cancel (x : Int) (n : Nat) : Int.bmod (x + n) n = Int.bmod x n := by
|
||||
simp [bmod_def]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_add_mul_cancel (x : Int) (n : Nat) (k : Int) : Int.bmod (x + n * k) n = Int.bmod x n := by
|
||||
simp [bmod_def]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_sub_cancel (x : Int) (n : Nat) : Int.bmod (x - n) n = Int.bmod x n := by
|
||||
simp [bmod_def]
|
||||
|
||||
@[simp]
|
||||
theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmod (x + y) n := by
|
||||
simp [Int.emod_def, Int.sub_eq_add_neg]
|
||||
rw [←Int.mul_neg, Int.add_right_comm, Int.bmod_add_mul_cancel]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
|
||||
rw [bmod_def x n]
|
||||
split
|
||||
case inl p =>
|
||||
simp
|
||||
case inr p =>
|
||||
rw [Int.sub_eq_add_neg, Int.add_right_comm, ←Int.sub_eq_add_neg]
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
|
||||
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]
|
||||
17
src/Init/Data/Int/Gcd.lean
Normal file
17
src/Init/Data/Int/Gcd.lean
Normal file
@@ -0,0 +1,17 @@
|
||||
/-
|
||||
Copyright (c) 2022 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Data.Nat.Gcd
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## gcd -/
|
||||
|
||||
/-- Computes the greatest common divisor of two integers, as a `Nat`. -/
|
||||
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
|
||||
|
||||
end Int
|
||||
554
src/Init/Data/Int/Lemmas.lean
Normal file
554
src/Init/Data/Int/Lemmas.lean
Normal file
@@ -0,0 +1,554 @@
|
||||
/-
|
||||
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Jeremy Avigad, Deniz Aydin, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Basic
|
||||
import Init.Conv
|
||||
import Init.PropLemmas
|
||||
|
||||
namespace Int
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Definitions of basic functions -/
|
||||
|
||||
theorem subNatNat_of_sub_eq_zero {m n : Nat} (h : n - m = 0) : subNatNat m n = ↑(m - n) := by
|
||||
rw [subNatNat, h, ofNat_eq_coe]
|
||||
|
||||
theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat m n = -[k+1] := by
|
||||
rw [subNatNat, h]
|
||||
|
||||
@[simp] protected theorem neg_zero : -(0:Int) = 0 := rfl
|
||||
|
||||
@[norm_cast] theorem ofNat_add (n m : Nat) : (↑(n + m) : Int) = n + m := rfl
|
||||
@[norm_cast] theorem ofNat_mul (n m : Nat) : (↑(n * m) : Int) = n * m := rfl
|
||||
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
|
||||
|
||||
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
|
||||
@[local simp] theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
|
||||
@[local simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
|
||||
|
||||
theorem negSucc_coe (n : Nat) : -[n+1] = -↑(n + 1) := rfl
|
||||
|
||||
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
|
||||
|
||||
/-! ## These are only for internal use -/
|
||||
|
||||
@[simp] theorem add_def {a b : Int} : Int.add a b = a + b := rfl
|
||||
|
||||
@[local simp] theorem ofNat_add_ofNat (m n : Nat) : (↑m + ↑n : Int) = ↑(m + n) := rfl
|
||||
@[local simp] theorem ofNat_add_negSucc (m n : Nat) : ↑m + -[n+1] = subNatNat m (succ n) := rfl
|
||||
@[local simp] theorem negSucc_add_ofNat (m n : Nat) : -[m+1] + ↑n = subNatNat n (succ m) := rfl
|
||||
@[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
|
||||
|
||||
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
|
||||
|
||||
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (↑m * ↑n : Int) = ↑(m * n) := rfl
|
||||
@[local simp] theorem ofNat_mul_negSucc' (m n : Nat) : ↑m * -[n+1] = negOfNat (m * succ n) := rfl
|
||||
@[local simp] theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * ↑n = negOfNat (succ m * n) := rfl
|
||||
@[local simp] theorem negSucc_mul_negSucc' (m n : Nat) :
|
||||
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
|
||||
|
||||
/- ## some basic functions and properties -/
|
||||
|
||||
@[norm_cast] theorem ofNat_inj : ((m : Nat) : Int) = (n : Nat) ↔ m = n := ⟨ofNat.inj, congrArg _⟩
|
||||
|
||||
theorem ofNat_eq_zero : ((n : Nat) : Int) = 0 ↔ n = 0 := ofNat_inj
|
||||
|
||||
theorem ofNat_ne_zero : ((n : Nat) : Int) ≠ 0 ↔ n ≠ 0 := not_congr ofNat_eq_zero
|
||||
|
||||
theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H => by simp [H]⟩
|
||||
|
||||
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
|
||||
|
||||
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] ≠ 0 := nofun
|
||||
|
||||
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 ≠ -[n+1] := nofun
|
||||
|
||||
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
|
||||
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
|
||||
|
||||
/- ## neg -/
|
||||
|
||||
@[simp] protected theorem neg_neg : ∀ a : Int, -(-a) = a
|
||||
| 0 => rfl
|
||||
| succ _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
protected theorem neg_inj {a b : Int} : -a = -b ↔ a = b :=
|
||||
⟨fun h => by rw [← Int.neg_neg a, ← Int.neg_neg b, h], congrArg _⟩
|
||||
|
||||
@[simp] protected theorem neg_eq_zero : -a = 0 ↔ a = 0 := Int.neg_inj (b := 0)
|
||||
|
||||
protected theorem neg_ne_zero : -a ≠ 0 ↔ a ≠ 0 := not_congr Int.neg_eq_zero
|
||||
|
||||
protected theorem sub_eq_add_neg {a b : Int} : a - b = a + -b := rfl
|
||||
|
||||
theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
|
||||
|
||||
/- ## basic properties of subNatNat -/
|
||||
|
||||
-- @[elabAsElim] -- TODO(Mario): unexpected eliminator resulting type
|
||||
theorem subNatNat_elim (m n : Nat) (motive : Nat → Nat → Int → Prop)
|
||||
(hp : ∀ i n, motive (n + i) n i)
|
||||
(hn : ∀ i m, motive m (m + i + 1) -[i+1]) :
|
||||
motive m n (subNatNat m n) := by
|
||||
unfold subNatNat
|
||||
match h : n - m with
|
||||
| 0 =>
|
||||
have ⟨k, h⟩ := Nat.le.dest (Nat.le_of_sub_eq_zero h)
|
||||
rw [h.symm, Nat.add_sub_cancel_left]; apply hp
|
||||
| succ k =>
|
||||
rw [Nat.sub_eq_iff_eq_add (Nat.le_of_lt (Nat.lt_of_sub_eq_succ h))] at h
|
||||
rw [h, Nat.add_comm]; apply hn
|
||||
|
||||
theorem subNatNat_add_left : subNatNat (m + n) m = n := by
|
||||
unfold subNatNat
|
||||
rw [Nat.sub_eq_zero_of_le (Nat.le_add_right ..), Nat.add_sub_cancel_left, ofNat_eq_coe]
|
||||
|
||||
theorem subNatNat_add_right : subNatNat m (m + n + 1) = negSucc n := by
|
||||
simp [subNatNat, Nat.add_assoc, Nat.add_sub_cancel_left]
|
||||
|
||||
theorem subNatNat_add_add (m n k : Nat) : subNatNat (m + k) (n + k) = subNatNat m n := by
|
||||
apply subNatNat_elim m n (fun m n i => subNatNat (m + k) (n + k) = i)
|
||||
focus
|
||||
intro i j
|
||||
rw [Nat.add_assoc, Nat.add_comm i k, ← Nat.add_assoc]
|
||||
exact subNatNat_add_left
|
||||
focus
|
||||
intro i j
|
||||
rw [Nat.add_assoc j i 1, Nat.add_comm j (i+1), Nat.add_assoc, Nat.add_comm (i+1) (j+k)]
|
||||
exact subNatNat_add_right
|
||||
|
||||
theorem subNatNat_of_le {m n : Nat} (h : n ≤ m) : subNatNat m n = ↑(m - n) :=
|
||||
subNatNat_of_sub_eq_zero (Nat.sub_eq_zero_of_le h)
|
||||
|
||||
theorem subNatNat_of_lt {m n : Nat} (h : m < n) : subNatNat m n = -[pred (n - m) +1] :=
|
||||
subNatNat_of_sub_eq_succ <| (Nat.succ_pred_eq_of_pos (Nat.sub_pos_of_lt h)).symm
|
||||
|
||||
/- # Additive group properties -/
|
||||
|
||||
/- addition -/
|
||||
|
||||
protected theorem add_comm : ∀ a b : Int, a + b = b + a
|
||||
| ofNat n, ofNat m => by simp [Nat.add_comm]
|
||||
| ofNat _, -[_+1] => rfl
|
||||
| -[_+1], ofNat _ => rfl
|
||||
| -[_+1], -[_+1] => by simp [Nat.add_comm]
|
||||
|
||||
@[simp] protected theorem add_zero : ∀ a : Int, a + 0 = a
|
||||
| ofNat _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. ▸ a.add_zero
|
||||
|
||||
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
|
||||
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
|
||||
|
||||
theorem subNatNat_sub (h : n ≤ m) (k : Nat) : subNatNat (m - n) k = subNatNat m (k + n) := by
|
||||
rwa [← subNatNat_add_add _ _ n, Nat.sub_add_cancel]
|
||||
|
||||
theorem subNatNat_add (m n k : Nat) : subNatNat (m + n) k = m + subNatNat n k := by
|
||||
cases n.lt_or_ge k with
|
||||
| inl h' =>
|
||||
simp [subNatNat_of_lt h', succ_pred_eq_of_pos (Nat.sub_pos_of_lt h')]
|
||||
conv => lhs; rw [← Nat.sub_add_cancel (Nat.le_of_lt h')]
|
||||
apply subNatNat_add_add
|
||||
| inr h' => simp [subNatNat_of_le h',
|
||||
subNatNat_of_le (Nat.le_trans h' (le_add_left ..)), Nat.add_sub_assoc h']
|
||||
|
||||
theorem subNatNat_add_negSucc (m n k : Nat) :
|
||||
subNatNat m n + -[k+1] = subNatNat m (n + succ k) := by
|
||||
have h := Nat.lt_or_ge m n
|
||||
cases h with
|
||||
| inr h' =>
|
||||
rw [subNatNat_of_le h']
|
||||
simp
|
||||
rw [subNatNat_sub h', Nat.add_comm]
|
||||
| inl h' =>
|
||||
have h₂ : m < n + succ k := Nat.lt_of_lt_of_le h' (le_add_right _ _)
|
||||
have h₃ : m ≤ n + k := le_of_succ_le_succ h₂
|
||||
rw [subNatNat_of_lt h', subNatNat_of_lt h₂]
|
||||
simp [Nat.add_comm]
|
||||
rw [← add_succ, succ_pred_eq_of_pos (Nat.sub_pos_of_lt h'), add_succ, succ_sub h₃,
|
||||
Nat.pred_succ]
|
||||
rw [Nat.add_comm n, Nat.add_sub_assoc (Nat.le_of_lt h')]
|
||||
|
||||
protected theorem add_assoc : ∀ a b c : Int, a + b + c = a + (b + c)
|
||||
| (m:Nat), (n:Nat), c => 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 ..
|
||||
| -[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
|
||||
rw [Int.add_comm, Int.add_comm m, Int.add_comm m, ← aux2, Int.add_comm -[k+1]]
|
||||
| -[m+1], -[n+1], -[k+1] => by
|
||||
simp [add_succ, Nat.add_comm, Nat.add_left_comm, neg_ofNat_succ]
|
||||
where
|
||||
aux1 (m n : Nat) : ∀ c : Int, m + n + c = m + (n + c)
|
||||
| (k:Nat) => by simp [Nat.add_assoc]
|
||||
| -[k+1] => by simp [subNatNat_add]
|
||||
aux2 (m n k : Nat) : -[m+1] + -[n+1] + k = -[m+1] + (-[n+1] + k) := by
|
||||
simp [add_succ]
|
||||
rw [Int.add_comm, subNatNat_add_negSucc]
|
||||
simp [add_succ, succ_add, Nat.add_comm]
|
||||
|
||||
protected theorem add_left_comm (a b c : Int) : a + (b + c) = b + (a + c) := by
|
||||
rw [← Int.add_assoc, Int.add_comm a, Int.add_assoc]
|
||||
|
||||
protected theorem add_right_comm (a b c : Int) : a + b + c = a + c + b := by
|
||||
rw [Int.add_assoc, Int.add_comm b, ← Int.add_assoc]
|
||||
|
||||
/- ## negation -/
|
||||
|
||||
theorem subNatNat_self : ∀ n, subNatNat n n = 0
|
||||
| 0 => rfl
|
||||
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
|
||||
|
||||
attribute [local simp] subNatNat_self
|
||||
|
||||
@[local simp] protected theorem add_left_neg : ∀ a : Int, -a + a = 0
|
||||
| 0 => rfl
|
||||
| succ m => by simp
|
||||
| -[m+1] => by simp
|
||||
|
||||
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
|
||||
rw [Int.add_comm, Int.add_left_neg]
|
||||
|
||||
@[simp] protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
|
||||
rw [← Int.add_zero (-a), ← h, ← Int.add_assoc, Int.add_left_neg, Int.zero_add]
|
||||
|
||||
protected theorem eq_neg_of_eq_neg {a b : Int} (h : a = -b) : b = -a := by
|
||||
rw [h, Int.neg_neg]
|
||||
|
||||
protected theorem eq_neg_comm {a b : Int} : a = -b ↔ b = -a :=
|
||||
⟨Int.eq_neg_of_eq_neg, Int.eq_neg_of_eq_neg⟩
|
||||
|
||||
protected theorem neg_eq_comm {a b : Int} : -a = b ↔ -b = a := by
|
||||
rw [eq_comm, Int.eq_neg_comm, eq_comm]
|
||||
|
||||
protected theorem neg_add_cancel_left (a b : Int) : -a + (a + b) = b := by
|
||||
rw [← Int.add_assoc, Int.add_left_neg, Int.zero_add]
|
||||
|
||||
protected theorem add_neg_cancel_left (a b : Int) : a + (-a + b) = b := by
|
||||
rw [← Int.add_assoc, Int.add_right_neg, Int.zero_add]
|
||||
|
||||
protected theorem add_neg_cancel_right (a b : Int) : a + b + -b = a := by
|
||||
rw [Int.add_assoc, Int.add_right_neg, Int.add_zero]
|
||||
|
||||
protected theorem neg_add_cancel_right (a b : Int) : a + -b + b = a := by
|
||||
rw [Int.add_assoc, Int.add_left_neg, Int.add_zero]
|
||||
|
||||
protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := by
|
||||
have h₁ : -a + (a + b) = -a + (a + c) := by rw [h]
|
||||
simp [← Int.add_assoc, Int.add_left_neg, Int.zero_add] at h₁; exact h₁
|
||||
|
||||
@[local simp] protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
|
||||
apply Int.add_left_cancel (a := a + b)
|
||||
rw [Int.add_right_neg, Int.add_comm a, ← Int.add_assoc, Int.add_assoc b,
|
||||
Int.add_right_neg, Int.add_zero, Int.add_right_neg]
|
||||
|
||||
/- ## subtraction -/
|
||||
|
||||
@[simp] theorem negSucc_sub_one (n : Nat) : -[n+1] - 1 = -[n + 1 +1] := rfl
|
||||
|
||||
@[simp] protected theorem sub_self (a : Int) : a - a = 0 := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_right_neg]
|
||||
|
||||
@[simp] protected theorem sub_zero (a : Int) : a - 0 = a := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] protected theorem zero_sub (a : Int) : 0 - a = -a := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
protected theorem sub_eq_zero_of_eq {a b : Int} (h : a = b) : a - b = 0 := by
|
||||
rw [h, Int.sub_self]
|
||||
|
||||
protected theorem eq_of_sub_eq_zero {a b : Int} (h : a - b = 0) : a = b := by
|
||||
have : 0 + b = b := by rw [Int.zero_add]
|
||||
have : a - b + b = b := by rwa [h]
|
||||
rwa [Int.sub_eq_add_neg, Int.neg_add_cancel_right] at this
|
||||
|
||||
protected theorem sub_eq_zero {a b : Int} : a - b = 0 ↔ a = b :=
|
||||
⟨Int.eq_of_sub_eq_zero, Int.sub_eq_zero_of_eq⟩
|
||||
|
||||
protected theorem sub_sub (a b c : Int) : a - b - c = a - (b + c) := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
|
||||
protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_comm]
|
||||
|
||||
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
|
||||
simp [Int.sub_eq_add_neg, ← Int.add_assoc]
|
||||
|
||||
protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] protected theorem sub_add_cancel (a b : Int) : a - b + b = a :=
|
||||
Int.neg_add_cancel_right a b
|
||||
|
||||
@[simp] protected theorem add_sub_cancel (a b : Int) : a + b - b = a :=
|
||||
Int.add_neg_cancel_right a b
|
||||
|
||||
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
|
||||
rw [Int.sub_eq_add_neg, Int.add_assoc, ← Int.sub_eq_add_neg]
|
||||
|
||||
@[norm_cast] theorem ofNat_sub (h : m ≤ n) : ((n - m : Nat) : Int) = n - m := by
|
||||
match m with
|
||||
| 0 => rfl
|
||||
| succ m =>
|
||||
show ofNat (n - succ m) = subNatNat n (succ m)
|
||||
rw [subNatNat, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
theorem negSucc_coe' (n : Nat) : -[n+1] = -↑n - 1 := by
|
||||
rw [Int.sub_eq_add_neg, ← Int.neg_add]; rfl
|
||||
|
||||
protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = ↑m - ↑n := by
|
||||
apply subNatNat_elim m n fun m n i => i = m - n
|
||||
· intros i n
|
||||
rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
|
||||
Int.add_right_neg, Int.add_zero]
|
||||
· intros i n
|
||||
simp only [negSucc_coe, ofNat_add, Int.sub_eq_add_neg, Int.neg_add, ← Int.add_assoc]
|
||||
rw [← @Int.sub_eq_add_neg n, ← ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
|
||||
apply Nat.le_refl
|
||||
|
||||
theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
|
||||
rw [← Int.subNatNat_eq_coe]
|
||||
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_)
|
||||
· exact (Nat.add_sub_cancel_left ..).symm
|
||||
· dsimp; rw [Nat.add_assoc, Nat.sub_eq_zero_of_le (Nat.le_add_right ..)]; rfl
|
||||
|
||||
/- ## add/sub injectivity -/
|
||||
|
||||
@[simp]
|
||||
protected theorem add_right_inj (i j k : Int) : (i + k = j + k) ↔ i = j := by
|
||||
apply Iff.intro
|
||||
· intro p
|
||||
rw [←Int.add_sub_cancel i k, ←Int.add_sub_cancel j k, p]
|
||||
· exact congrArg (· + k)
|
||||
|
||||
@[simp]
|
||||
protected theorem add_left_inj (i j k : Int) : (k + i = k + j) ↔ i = j := by
|
||||
simp [Int.add_comm k]
|
||||
|
||||
@[simp]
|
||||
protected theorem sub_left_inj (i j k : Int) : (k - i = k - j) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg, Int.neg_inj]
|
||||
|
||||
@[simp]
|
||||
protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) ↔ i = j := by
|
||||
simp [Int.sub_eq_add_neg]
|
||||
|
||||
/- ## Ring properties -/
|
||||
|
||||
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -↑(m * succ n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -↑(succ m * n) := rfl
|
||||
|
||||
@[simp] theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
|
||||
|
||||
protected theorem mul_comm (a b : Int) : a * b = b * a := by
|
||||
cases a <;> cases b <;> simp [Nat.mul_comm]
|
||||
|
||||
theorem ofNat_mul_negOfNat (m n : Nat) : (m : Nat) * negOfNat n = negOfNat (m * n) := by
|
||||
cases n <;> rfl
|
||||
|
||||
theorem negOfNat_mul_ofNat (m n : Nat) : negOfNat m * (n : Nat) = negOfNat (m * n) := by
|
||||
rw [Int.mul_comm]; simp [ofNat_mul_negOfNat, Nat.mul_comm]
|
||||
|
||||
theorem negSucc_mul_negOfNat (m n : Nat) : -[m+1] * negOfNat n = ofNat (succ m * n) := by
|
||||
cases n <;> rfl
|
||||
|
||||
theorem negOfNat_mul_negSucc (m n : Nat) : negOfNat n * -[m+1] = ofNat (n * succ m) := by
|
||||
rw [Int.mul_comm, negSucc_mul_negOfNat, Nat.mul_comm]
|
||||
|
||||
attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
|
||||
negSucc_mul_negOfNat negOfNat_mul_negSucc
|
||||
|
||||
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
|
||||
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
|
||||
|
||||
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
|
||||
rw [← Int.mul_assoc, ← Int.mul_assoc, Int.mul_comm a]
|
||||
|
||||
protected theorem mul_right_comm (a b c : Int) : a * b * c = a * c * b := by
|
||||
rw [Int.mul_assoc, Int.mul_assoc, Int.mul_comm b]
|
||||
|
||||
@[simp] protected theorem mul_zero (a : Int) : a * 0 = 0 := by cases a <;> rfl
|
||||
|
||||
@[simp] protected theorem zero_mul (a : Int) : 0 * a = 0 := Int.mul_comm .. ▸ a.mul_zero
|
||||
|
||||
theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases n <;> rfl
|
||||
|
||||
theorem ofNat_mul_subNatNat (m n k : Nat) :
|
||||
m * subNatNat n k = subNatNat (m * n) (m * k) := by
|
||||
cases m with
|
||||
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul]
|
||||
| succ m => cases n.lt_or_ge k with
|
||||
| inl h =>
|
||||
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
|
||||
simp [subNatNat_of_lt h, subNatNat_of_lt h']
|
||||
rw [succ_pred_eq_of_pos (Nat.sub_pos_of_lt h), ← neg_ofNat_succ, Nat.mul_sub_left_distrib,
|
||||
← succ_pred_eq_of_pos (Nat.sub_pos_of_lt h')]; rfl
|
||||
| inr h =>
|
||||
have h' : succ m * k ≤ succ m * n := Nat.mul_le_mul_left _ h
|
||||
simp [subNatNat_of_le h, subNatNat_of_le h', Nat.mul_sub_left_distrib]
|
||||
|
||||
theorem negOfNat_add (m n : Nat) : negOfNat m + negOfNat n = negOfNat (m + n) := by
|
||||
cases m <;> cases n <;> simp [Nat.succ_add] <;> rfl
|
||||
|
||||
theorem negSucc_mul_subNatNat (m n k : Nat) :
|
||||
-[m+1] * subNatNat n k = subNatNat (succ m * k) (succ m * n) := by
|
||||
cases n.lt_or_ge k with
|
||||
| inl h =>
|
||||
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
|
||||
rw [subNatNat_of_lt h, subNatNat_of_le (Nat.le_of_lt h')]
|
||||
simp [succ_pred_eq_of_pos (Nat.sub_pos_of_lt h), Nat.mul_sub_left_distrib]
|
||||
| inr h => cases Nat.lt_or_ge k n with
|
||||
| inl h' =>
|
||||
have h₁ : succ m * n > succ m * k := Nat.mul_lt_mul_of_pos_left h' (Nat.succ_pos m)
|
||||
rw [subNatNat_of_le h, subNatNat_of_lt h₁, negSucc_mul_ofNat,
|
||||
Nat.mul_sub_left_distrib, ← succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁)]; rfl
|
||||
| inr h' => rw [Nat.le_antisymm h h', subNatNat_self, subNatNat_self, Int.mul_zero]
|
||||
|
||||
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat
|
||||
|
||||
protected theorem mul_add : ∀ a b c : Int, a * (b + c) = a * b + a * c
|
||||
| (m:Nat), (n:Nat), (k:Nat) => by simp [Nat.left_distrib]
|
||||
| (m:Nat), (n:Nat), -[k+1] => by
|
||||
simp [negOfNat_eq_subNatNat_zero]; rw [← subNatNat_add]; rfl
|
||||
| (m:Nat), -[n+1], (k:Nat) => by
|
||||
simp [negOfNat_eq_subNatNat_zero]; rw [Int.add_comm, ← subNatNat_add]; rfl
|
||||
| (m:Nat), -[n+1], -[k+1] => by simp; rw [← Nat.left_distrib, succ_add]; rfl
|
||||
| -[m+1], (n:Nat), (k:Nat) => by simp [Nat.mul_comm]; rw [← Nat.right_distrib, Nat.mul_comm]
|
||||
| -[m+1], (n:Nat), -[k+1] => by
|
||||
simp [negOfNat_eq_subNatNat_zero]; rw [Int.add_comm, ← subNatNat_add]; rfl
|
||||
| -[m+1], -[n+1], (k:Nat) => by simp [negOfNat_eq_subNatNat_zero]; rw [← subNatNat_add]; rfl
|
||||
| -[m+1], -[n+1], -[k+1] => by simp; rw [← Nat.left_distrib, succ_add]; rfl
|
||||
|
||||
protected theorem add_mul (a b c : Int) : (a + b) * c = a * c + b * c := by
|
||||
simp [Int.mul_comm, Int.mul_add]
|
||||
|
||||
protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
|
||||
Int.neg_eq_of_add_eq_zero <| by rw [← Int.add_mul, Int.add_right_neg, Int.zero_mul]
|
||||
|
||||
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
|
||||
Int.neg_eq_of_add_eq_zero <| by rw [← Int.mul_add, Int.add_right_neg, Int.mul_zero]
|
||||
|
||||
@[local simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
|
||||
(Int.neg_mul_eq_neg_mul a b).symm
|
||||
|
||||
@[local simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
|
||||
(Int.neg_mul_eq_mul_neg a b).symm
|
||||
|
||||
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
|
||||
|
||||
protected theorem neg_mul_comm (a b : Int) : -a * b = a * -b := by simp
|
||||
|
||||
protected theorem mul_sub (a b c : Int) : a * (b - c) = a * b - a * c := by
|
||||
simp [Int.sub_eq_add_neg, Int.mul_add]
|
||||
|
||||
protected theorem sub_mul (a b c : Int) : (a - b) * c = a * c - b * c := by
|
||||
simp [Int.sub_eq_add_neg, Int.add_mul]
|
||||
|
||||
@[simp] protected theorem one_mul : ∀ a : Int, 1 * a = a
|
||||
| ofNat n => show ofNat (1 * n) = ofNat n by rw [Nat.one_mul]
|
||||
| -[n+1] => show -[1 * n +1] = -[n+1] by rw [Nat.one_mul]
|
||||
|
||||
@[simp] protected theorem mul_one (a : Int) : a * 1 = a := by rw [Int.mul_comm, Int.one_mul]
|
||||
|
||||
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
|
||||
|
||||
protected theorem neg_eq_neg_one_mul : ∀ a : Int, -a = -1 * a
|
||||
| 0 => rfl
|
||||
| succ n => show _ = -[1 * n +1] by rw [Nat.one_mul]; rfl
|
||||
| -[n+1] => show _ = ofNat _ by rw [Nat.one_mul]; rfl
|
||||
|
||||
protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 ∨ b = 0 := by
|
||||
refine ⟨fun h => ?_, fun h => h.elim (by simp [·, Int.zero_mul]) (by simp [·, Int.mul_zero])⟩
|
||||
exact match a, b, h with
|
||||
| .ofNat 0, _, _ => by simp
|
||||
| _, .ofNat 0, _ => by simp
|
||||
| .ofNat (a+1), .negSucc b, h => by cases h
|
||||
|
||||
protected theorem mul_ne_zero {a b : Int} (a0 : a ≠ 0) (b0 : b ≠ 0) : a * b ≠ 0 :=
|
||||
Or.rec a0 b0 ∘ Int.mul_eq_zero.mp
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_right {a b c : Int} (ha : a ≠ 0) (h : b * a = c * a) : b = c :=
|
||||
have : (b - c) * a = 0 := by rwa [Int.sub_mul, Int.sub_eq_zero]
|
||||
Int.sub_eq_zero.1 <| (Int.mul_eq_zero.mp this).resolve_right ha
|
||||
|
||||
protected theorem eq_of_mul_eq_mul_left {a b c : Int} (ha : a ≠ 0) (h : a * b = a * c) : b = c :=
|
||||
have : a * b - a * c = 0 := Int.sub_eq_zero_of_eq h
|
||||
have : a * (b - c) = 0 := by rw [Int.mul_sub, this]
|
||||
have : b - c = 0 := (Int.mul_eq_zero.1 this).resolve_left ha
|
||||
Int.eq_of_sub_eq_zero this
|
||||
|
||||
theorem mul_eq_mul_left_iff {a b c : Int} (h : c ≠ 0) : c * a = c * b ↔ a = b :=
|
||||
⟨Int.eq_of_mul_eq_mul_left h, fun w => congrArg (fun x => c * x) w⟩
|
||||
|
||||
theorem mul_eq_mul_right_iff {a b c : Int} (h : c ≠ 0) : a * c = b * c ↔ a = b :=
|
||||
⟨Int.eq_of_mul_eq_mul_right h, fun w => congrArg (fun x => x * c) w⟩
|
||||
|
||||
theorem eq_one_of_mul_eq_self_left {a b : Int} (Hpos : a ≠ 0) (H : b * a = a) : b = 1 :=
|
||||
Int.eq_of_mul_eq_mul_right Hpos <| by rw [Int.one_mul, H]
|
||||
|
||||
theorem eq_one_of_mul_eq_self_right {a b : Int} (Hpos : b ≠ 0) (H : b * a = b) : a = 1 :=
|
||||
Int.eq_of_mul_eq_mul_left Hpos <| by rw [Int.mul_one, H]
|
||||
|
||||
/-! # pow -/
|
||||
|
||||
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
|
||||
|
||||
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
|
||||
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
|
||||
rw [Int.mul_comm, Int.pow_succ]
|
||||
|
||||
theorem pow_le_pow_of_le_left {n m : Nat} (h : n ≤ m) : ∀ (i : Nat), n^i ≤ m^i
|
||||
| 0 => Nat.le_refl _
|
||||
| succ i => Nat.mul_le_mul (pow_le_pow_of_le_left h i) h
|
||||
|
||||
theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤ j → n^i ≤ n^j
|
||||
| 0, h =>
|
||||
have : i = 0 := eq_zero_of_le_zero h
|
||||
this.symm ▸ Nat.le_refl _
|
||||
| succ j, h =>
|
||||
match le_or_eq_of_le_succ h with
|
||||
| Or.inl h => show n^i ≤ n^j * n from
|
||||
have : n^i * 1 ≤ n^j * n := Nat.mul_le_mul (pow_le_pow_of_le_right hx h) hx
|
||||
Nat.mul_one (n^i) ▸ this
|
||||
| Or.inr h =>
|
||||
h.symm ▸ Nat.le_refl _
|
||||
|
||||
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 _)
|
||||
|
||||
/-! NatCast lemmas -/
|
||||
|
||||
/-!
|
||||
The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul` in Mathlib
|
||||
but it is convenient to have these earlier, for users who only need `Nat` and `Int`.
|
||||
-/
|
||||
|
||||
theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
|
||||
|
||||
theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
|
||||
|
||||
@[simp] theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
|
||||
-- Note this only works because of local simp attributes in this file,
|
||||
-- so it still makes sense to tag the lemmas with `@[simp]`.
|
||||
simp
|
||||
|
||||
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
|
||||
simp
|
||||
|
||||
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]
|
||||
|
||||
end Int
|
||||
500
src/Init/Data/Int/Order.lean
Normal file
500
src/Init/Data/Int/Order.lean
Normal file
@@ -0,0 +1,500 @@
|
||||
/-
|
||||
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Jeremy Avigad, Deniz Aydin, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Lemmas
|
||||
import Init.ByCases
|
||||
|
||||
/-!
|
||||
# Results about the order properties of the integers, and the integers as an ordered ring.
|
||||
-/
|
||||
|
||||
open Nat
|
||||
|
||||
namespace Int
|
||||
|
||||
/-! ## Order properties of the integers -/
|
||||
|
||||
theorem nonneg_def {a : Int} : NonNeg a ↔ ∃ n : Nat, a = n :=
|
||||
⟨fun ⟨n⟩ => ⟨n, rfl⟩, fun h => match a, h with | _, ⟨n, rfl⟩ => ⟨n⟩⟩
|
||||
|
||||
theorem NonNeg.elim {a : Int} : NonNeg a → ∃ n : Nat, a = n := nonneg_def.1
|
||||
|
||||
theorem nonneg_or_nonneg_neg : ∀ (a : Int), NonNeg a ∨ NonNeg (-a)
|
||||
| (_:Nat) => .inl ⟨_⟩
|
||||
| -[_+1] => .inr ⟨_⟩
|
||||
|
||||
theorem le_def (a b : Int) : a ≤ b ↔ NonNeg (b - a) := .rfl
|
||||
|
||||
theorem lt_iff_add_one_le (a b : Int) : a < b ↔ a + 1 ≤ b := .rfl
|
||||
|
||||
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a ≤ b := by
|
||||
simp [le_def, h]; constructor
|
||||
|
||||
attribute [local simp] Int.add_left_neg Int.add_right_neg Int.neg_add
|
||||
|
||||
theorem le.intro {a b : Int} (n : Nat) (h : a + n = b) : a ≤ b :=
|
||||
le.intro_sub n <| by rw [← h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
|
||||
|
||||
theorem le.dest_sub {a b : Int} (h : a ≤ b) : ∃ n : Nat, b - a = n := nonneg_def.1 h
|
||||
|
||||
theorem le.dest {a b : Int} (h : a ≤ b) : ∃ n : Nat, a + n = b :=
|
||||
let ⟨n, h₁⟩ := le.dest_sub h
|
||||
⟨n, by rw [← h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]⟩
|
||||
|
||||
protected theorem le_total (a b : Int) : a ≤ b ∨ b ≤ a :=
|
||||
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
|
||||
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (↑m : Int) ≤ ↑n ↔ m ≤ n :=
|
||||
⟨fun h =>
|
||||
let ⟨k, hk⟩ := le.dest h
|
||||
Nat.le.intro <| Int.ofNat.inj <| (Int.ofNat_add m k).trans hk,
|
||||
fun h =>
|
||||
let ⟨k, (hk : m + k = n)⟩ := Nat.le.dest h
|
||||
le.intro k (by rw [← hk]; rfl)⟩
|
||||
|
||||
theorem ofNat_zero_le (n : Nat) : 0 ≤ (↑n : Int) := ofNat_le.2 n.zero_le
|
||||
|
||||
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 ≤ a) : ∃ n : Nat, a = n := by
|
||||
have t := le.dest_sub h; rwa [Int.sub_zero] at t
|
||||
|
||||
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : ∃ n : Nat, a = n.succ :=
|
||||
let ⟨n, (h : ↑(1 + n) = a)⟩ := le.dest h
|
||||
⟨n, by rw [Nat.add_comm] at h; exact h.symm⟩
|
||||
|
||||
theorem lt_add_succ (a : Int) (n : Nat) : a < a + Nat.succ n :=
|
||||
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]; rfl
|
||||
|
||||
theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
|
||||
h ▸ lt_add_succ a n
|
||||
|
||||
theorem lt.dest {a b : Int} (h : a < b) : ∃ n : Nat, a + Nat.succ n = b :=
|
||||
let ⟨n, h⟩ := le.dest h; ⟨n, by rwa [Int.add_comm, Int.add_left_comm] at h⟩
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_lt {n m : Nat} : (↑n : Int) < ↑m ↔ n < m := by
|
||||
rw [lt_iff_add_one_le, ← ofNat_succ, ofNat_le]; rfl
|
||||
|
||||
@[simp, norm_cast] theorem ofNat_pos {n : Nat} : 0 < (↑n : Int) ↔ 0 < n := ofNat_lt
|
||||
|
||||
theorem ofNat_nonneg (n : Nat) : 0 ≤ (n : Int) := ⟨_⟩
|
||||
|
||||
theorem ofNat_succ_pos (n : Nat) : 0 < (succ n : Int) := ofNat_lt.2 <| Nat.succ_pos _
|
||||
|
||||
@[simp] protected theorem le_refl (a : Int) : a ≤ a :=
|
||||
le.intro _ (Int.add_zero a)
|
||||
|
||||
protected theorem le_trans {a b c : Int} (h₁ : a ≤ b) (h₂ : b ≤ c) : a ≤ c :=
|
||||
let ⟨n, hn⟩ := le.dest h₁; let ⟨m, hm⟩ := le.dest h₂
|
||||
le.intro (n + m) <| by rw [← hm, ← hn, Int.add_assoc, ofNat_add]
|
||||
|
||||
protected theorem le_antisymm {a b : Int} (h₁ : a ≤ b) (h₂ : b ≤ a) : a = b := by
|
||||
let ⟨n, hn⟩ := le.dest h₁; let ⟨m, hm⟩ := le.dest h₂
|
||||
have := hn; rw [← hm, Int.add_assoc, ← ofNat_add] at this
|
||||
have := Int.ofNat.inj <| Int.add_left_cancel <| this.trans (Int.add_zero _).symm
|
||||
rw [← hn, Nat.eq_zero_of_add_eq_zero_left this, ofNat_zero, Int.add_zero a]
|
||||
|
||||
protected theorem lt_irrefl (a : Int) : ¬a < a := fun H =>
|
||||
let ⟨n, hn⟩ := lt.dest H
|
||||
have : (a+Nat.succ n) = a+0 := by
|
||||
rw [hn, Int.add_zero]
|
||||
have : Nat.succ n = 0 := Int.ofNat.inj (Int.add_left_cancel this)
|
||||
show False from Nat.succ_ne_zero _ this
|
||||
|
||||
protected theorem ne_of_lt {a b : Int} (h : a < b) : a ≠ b := fun e => by
|
||||
cases e; exact Int.lt_irrefl _ h
|
||||
|
||||
protected theorem ne_of_gt {a b : Int} (h : b < a) : a ≠ b := (Int.ne_of_lt h).symm
|
||||
|
||||
protected theorem le_of_lt {a b : Int} (h : a < b) : a ≤ b :=
|
||||
let ⟨_, hn⟩ := lt.dest h; le.intro _ hn
|
||||
|
||||
protected theorem lt_iff_le_and_ne {a b : Int} : a < b ↔ a ≤ b ∧ a ≠ b := by
|
||||
refine ⟨fun h => ⟨Int.le_of_lt h, Int.ne_of_lt h⟩, fun ⟨aleb, aneb⟩ => ?_⟩
|
||||
let ⟨n, hn⟩ := le.dest aleb
|
||||
have : n ≠ 0 := aneb.imp fun eq => by rw [← hn, eq, ofNat_zero, Int.add_zero]
|
||||
apply lt.intro; rwa [← Nat.succ_pred_eq_of_pos (Nat.pos_of_ne_zero this)] at hn
|
||||
|
||||
theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
|
||||
|
||||
protected theorem zero_lt_one : (0 : Int) < 1 := ⟨_⟩
|
||||
|
||||
protected theorem lt_iff_le_not_le {a b : Int} : a < b ↔ a ≤ b ∧ ¬b ≤ a := by
|
||||
rw [Int.lt_iff_le_and_ne]
|
||||
constructor <;> refine fun ⟨h, h'⟩ => ⟨h, h'.imp fun h' => ?_⟩
|
||||
· exact Int.le_antisymm h h'
|
||||
· subst h'; apply Int.le_refl
|
||||
|
||||
protected theorem not_le {a b : Int} : ¬a ≤ b ↔ b < a :=
|
||||
⟨fun h => Int.lt_iff_le_not_le.2 ⟨(Int.le_total ..).resolve_right h, h⟩,
|
||||
fun h => (Int.lt_iff_le_not_le.1 h).2⟩
|
||||
|
||||
protected theorem not_lt {a b : Int} : ¬a < b ↔ b ≤ a :=
|
||||
by rw [← Int.not_le, Decidable.not_not]
|
||||
|
||||
protected theorem lt_trichotomy (a b : Int) : a < b ∨ a = b ∨ b < a :=
|
||||
if eq : a = b then .inr <| .inl eq else
|
||||
if le : a ≤ b then .inl <| Int.lt_iff_le_and_ne.2 ⟨le, eq⟩ else
|
||||
.inr <| .inr <| Int.not_le.1 le
|
||||
|
||||
protected theorem ne_iff_lt_or_gt {a b : Int} : a ≠ b ↔ a < b ∨ b < a := by
|
||||
constructor
|
||||
· intro h
|
||||
cases Int.lt_trichotomy a b
|
||||
case inl lt => exact Or.inl lt
|
||||
case inr h =>
|
||||
cases h
|
||||
case inl =>simp_all
|
||||
case inr gt => exact Or.inr gt
|
||||
· intro h
|
||||
cases h
|
||||
case inl lt => exact Int.ne_of_lt lt
|
||||
case inr gt => exact Int.ne_of_gt gt
|
||||
|
||||
protected theorem lt_or_gt_of_ne {a b : Int} : a ≠ b → a < b ∨ b < a:= Int.ne_iff_lt_or_gt.mp
|
||||
|
||||
protected theorem eq_iff_le_and_ge {x y : Int} : x = y ↔ x ≤ y ∧ y ≤ x := by
|
||||
constructor
|
||||
· simp_all
|
||||
· intro ⟨h₁, h₂⟩
|
||||
exact Int.le_antisymm h₁ h₂
|
||||
|
||||
protected theorem lt_of_le_of_lt {a b c : Int} (h₁ : a ≤ b) (h₂ : b < c) : a < c :=
|
||||
Int.not_le.1 fun h => Int.not_le.2 h₂ (Int.le_trans h h₁)
|
||||
|
||||
protected theorem lt_of_lt_of_le {a b c : Int} (h₁ : a < b) (h₂ : b ≤ c) : a < c :=
|
||||
Int.not_le.1 fun h => Int.not_le.2 h₁ (Int.le_trans h₂ h)
|
||||
|
||||
protected theorem lt_trans {a b c : Int} (h₁ : a < b) (h₂ : b < c) : a < c :=
|
||||
Int.lt_of_le_of_lt (Int.le_of_lt h₁) h₂
|
||||
|
||||
instance : Trans (α := Int) (· ≤ ·) (· ≤ ·) (· ≤ ·) := ⟨Int.le_trans⟩
|
||||
|
||||
instance : Trans (α := Int) (· < ·) (· ≤ ·) (· < ·) := ⟨Int.lt_of_lt_of_le⟩
|
||||
|
||||
instance : Trans (α := Int) (· ≤ ·) (· < ·) (· < ·) := ⟨Int.lt_of_le_of_lt⟩
|
||||
|
||||
instance : Trans (α := Int) (· < ·) (· < ·) (· < ·) := ⟨Int.lt_trans⟩
|
||||
|
||||
protected theorem min_def (n m : Int) : min n m = if n ≤ m then n else m := rfl
|
||||
|
||||
protected theorem max_def (n m : Int) : max n m = if n ≤ m then m else n := rfl
|
||||
|
||||
protected theorem min_comm (a b : Int) : min a b = min b a := by
|
||||
simp [Int.min_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₁ h₂
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
|
||||
protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def]; split <;> simp [*]
|
||||
|
||||
protected theorem min_le_left (a b : Int) : min a b ≤ a := Int.min_comm .. ▸ Int.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Int} (h : a ≤ b) : min a b = a := by simp [Int.min_def, h]
|
||||
|
||||
protected theorem min_eq_right {a b : Int} (h : b ≤ a) : min a b = b := by
|
||||
rw [Int.min_comm a b]; exact Int.min_eq_left h
|
||||
|
||||
protected theorem le_min {a b c : Int} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.min_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_comm (a b : Int) : max a b = max b a := by
|
||||
simp only [Int.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Int.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Int.le_total ..
|
||||
|
||||
protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]; split <;> simp [*]
|
||||
|
||||
protected theorem le_max_right (a b : Int) : b ≤ max a b := Int.max_comm .. ▸ Int.le_max_left ..
|
||||
|
||||
protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :=
|
||||
⟨fun h => ⟨Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h⟩,
|
||||
fun ⟨h₁, h₂⟩ => by rw [Int.max_def]; split <;> assumption⟩
|
||||
|
||||
protected theorem max_eq_right {a b : Int} (h : a ≤ b) : max a b = b := by
|
||||
simp [Int.max_def, h, Int.not_lt.2 h]
|
||||
|
||||
protected theorem max_eq_left {a b : Int} (h : b ≤ a) : max a b = a := by
|
||||
rw [← Int.max_comm b a]; exact Int.max_eq_right h
|
||||
|
||||
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 ≤ a) : a = natAbs a := by
|
||||
let ⟨n, e⟩ := eq_ofNat_of_zero_le h
|
||||
rw [e]; rfl
|
||||
|
||||
theorem le_natAbs {a : Int} : a ≤ natAbs a :=
|
||||
match Int.le_total 0 a with
|
||||
| .inl h => by rw [eq_natAbs_of_zero_le h]; apply Int.le_refl
|
||||
| .inr h => Int.le_trans h (ofNat_zero_le _)
|
||||
|
||||
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
|
||||
Int.not_le.1 fun h => let ⟨_, h⟩ := eq_ofNat_of_zero_le h; nomatch h
|
||||
|
||||
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 ≤ -[n+1] ↔ False := by
|
||||
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
|
||||
|
||||
protected theorem add_le_add_left {a b : Int} (h : a ≤ b) (c : Int) : c + a ≤ c + b :=
|
||||
let ⟨n, hn⟩ := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
|
||||
|
||||
protected theorem add_lt_add_left {a b : Int} (h : a < b) (c : Int) : c + a < c + b :=
|
||||
Int.lt_iff_le_and_ne.2 ⟨Int.add_le_add_left (Int.le_of_lt h) _, fun heq =>
|
||||
b.lt_irrefl <| by rwa [Int.add_left_cancel heq] at h⟩
|
||||
|
||||
protected theorem add_le_add_right {a b : Int} (h : a ≤ b) (c : Int) : a + c ≤ b + c :=
|
||||
Int.add_comm c a ▸ Int.add_comm c b ▸ Int.add_le_add_left h c
|
||||
|
||||
protected theorem add_lt_add_right {a b : Int} (h : a < b) (c : Int) : a + c < b + c :=
|
||||
Int.add_comm c a ▸ Int.add_comm c b ▸ Int.add_lt_add_left h c
|
||||
|
||||
protected theorem le_of_add_le_add_left {a b c : Int} (h : a + b ≤ a + c) : b ≤ c := by
|
||||
have : -a + (a + b) ≤ -a + (a + c) := Int.add_le_add_left h _
|
||||
simp [Int.neg_add_cancel_left] at this
|
||||
assumption
|
||||
|
||||
protected theorem le_of_add_le_add_right {a b c : Int} (h : a + b ≤ c + b) : a ≤ c :=
|
||||
Int.le_of_add_le_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
|
||||
|
||||
protected theorem add_le_add_iff_left (a : Int) : a + b ≤ a + c ↔ b ≤ c :=
|
||||
⟨Int.le_of_add_le_add_left, (Int.add_le_add_left · _)⟩
|
||||
|
||||
protected theorem add_le_add_iff_right (c : Int) : a + c ≤ b + c ↔ a ≤ b :=
|
||||
⟨Int.le_of_add_le_add_right, (Int.add_le_add_right · _)⟩
|
||||
|
||||
protected theorem add_le_add {a b c d : Int} (h₁ : a ≤ b) (h₂ : c ≤ d) : a + c ≤ b + d :=
|
||||
Int.le_trans (Int.add_le_add_right h₁ c) (Int.add_le_add_left h₂ b)
|
||||
|
||||
protected theorem le_add_of_nonneg_right {a b : Int} (h : 0 ≤ b) : a ≤ a + b := by
|
||||
have : a + b ≥ a + 0 := Int.add_le_add_left h a
|
||||
rwa [Int.add_zero] at this
|
||||
|
||||
protected theorem le_add_of_nonneg_left {a b : Int} (h : 0 ≤ b) : a ≤ b + a := by
|
||||
have : 0 + a ≤ b + a := Int.add_le_add_right h a
|
||||
rwa [Int.zero_add] at this
|
||||
|
||||
protected theorem neg_le_neg {a b : Int} (h : a ≤ b) : -b ≤ -a := by
|
||||
have : 0 ≤ -a + b := Int.add_left_neg a ▸ Int.add_le_add_left h (-a)
|
||||
have : 0 + -b ≤ -a + b + -b := Int.add_le_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
protected theorem le_of_neg_le_neg {a b : Int} (h : -b ≤ -a) : a ≤ b :=
|
||||
suffices - -a ≤ - -b by simp [Int.neg_neg] at this; assumption
|
||||
Int.neg_le_neg h
|
||||
|
||||
protected theorem neg_nonpos_of_nonneg {a : Int} (h : 0 ≤ a) : -a ≤ 0 := by
|
||||
have : -a ≤ -0 := Int.neg_le_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
|
||||
protected theorem neg_nonneg_of_nonpos {a : Int} (h : a ≤ 0) : 0 ≤ -a := by
|
||||
have : -0 ≤ -a := Int.neg_le_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
|
||||
protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
|
||||
have : 0 < -a + b := Int.add_left_neg a ▸ Int.add_lt_add_left h (-a)
|
||||
have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
|
||||
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
|
||||
|
||||
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
|
||||
have : -a < -0 := Int.neg_lt_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
|
||||
protected theorem neg_pos_of_neg {a : Int} (h : a < 0) : 0 < -a := by
|
||||
have : -0 < -a := Int.neg_lt_neg h
|
||||
rwa [Int.neg_zero] at this
|
||||
|
||||
protected theorem sub_nonneg_of_le {a b : Int} (h : b ≤ a) : 0 ≤ a - b := by
|
||||
have h := Int.add_le_add_right h (-b)
|
||||
rwa [Int.add_right_neg] at h
|
||||
|
||||
protected theorem le_of_sub_nonneg {a b : Int} (h : 0 ≤ a - b) : b ≤ a := by
|
||||
have h := Int.add_le_add_right h b
|
||||
rwa [Int.sub_add_cancel, Int.zero_add] at h
|
||||
|
||||
protected theorem sub_pos_of_lt {a b : Int} (h : b < a) : 0 < a - b := by
|
||||
have h := Int.add_lt_add_right h (-b)
|
||||
rwa [Int.add_right_neg] at h
|
||||
|
||||
protected theorem lt_of_sub_pos {a b : Int} (h : 0 < a - b) : b < a := by
|
||||
have h := Int.add_lt_add_right h b
|
||||
rwa [Int.sub_add_cancel, Int.zero_add] at h
|
||||
|
||||
protected theorem sub_left_le_of_le_add {a b c : Int} (h : a ≤ b + c) : a - b ≤ c := by
|
||||
have h := Int.add_le_add_right h (-b)
|
||||
rwa [Int.add_comm b c, Int.add_neg_cancel_right] at h
|
||||
|
||||
protected theorem sub_le_self (a : Int) {b : Int} (h : 0 ≤ b) : a - b ≤ a :=
|
||||
calc a + -b
|
||||
_ ≤ a + 0 := Int.add_le_add_left (Int.neg_nonpos_of_nonneg h) _
|
||||
_ = a := by rw [Int.add_zero]
|
||||
|
||||
protected theorem sub_lt_self (a : Int) {b : Int} (h : 0 < b) : a - b < a :=
|
||||
calc a + -b
|
||||
_ < a + 0 := Int.add_lt_add_left (Int.neg_neg_of_pos h) _
|
||||
_ = a := by rw [Int.add_zero]
|
||||
|
||||
theorem add_one_le_of_lt {a b : Int} (H : a < b) : a + 1 ≤ b := H
|
||||
|
||||
/- ### Order properties and multiplication -/
|
||||
|
||||
|
||||
protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := by
|
||||
let ⟨n, hn⟩ := eq_ofNat_of_zero_le ha
|
||||
let ⟨m, hm⟩ := eq_ofNat_of_zero_le hb
|
||||
rw [hn, hm, ← ofNat_mul]; apply ofNat_nonneg
|
||||
|
||||
protected theorem mul_pos {a b : Int} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := by
|
||||
let ⟨n, hn⟩ := eq_succ_of_zero_lt ha
|
||||
let ⟨m, hm⟩ := eq_succ_of_zero_lt hb
|
||||
rw [hn, hm, ← ofNat_mul]; apply ofNat_succ_pos
|
||||
|
||||
protected theorem mul_lt_mul_of_pos_left {a b c : Int}
|
||||
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by
|
||||
have : 0 < c * (b - a) := Int.mul_pos h₂ (Int.sub_pos_of_lt h₁)
|
||||
rw [Int.mul_sub] at this
|
||||
exact Int.lt_of_sub_pos this
|
||||
|
||||
protected theorem mul_lt_mul_of_pos_right {a b c : Int}
|
||||
(h₁ : a < b) (h₂ : 0 < c) : a * c < b * c := by
|
||||
have : 0 < b - a := Int.sub_pos_of_lt h₁
|
||||
have : 0 < (b - a) * c := Int.mul_pos this h₂
|
||||
rw [Int.sub_mul] at this
|
||||
exact Int.lt_of_sub_pos this
|
||||
|
||||
protected theorem mul_le_mul_of_nonneg_left {a b c : Int}
|
||||
(h₁ : a ≤ b) (h₂ : 0 ≤ c) : c * a ≤ c * b :=
|
||||
if hba : b ≤ a then by
|
||||
rw [Int.le_antisymm hba h₁]; apply Int.le_refl
|
||||
else if hc0 : c ≤ 0 then by
|
||||
simp [Int.le_antisymm hc0 h₂, Int.zero_mul]
|
||||
else by
|
||||
exact Int.le_of_lt <| Int.mul_lt_mul_of_pos_left
|
||||
(Int.lt_iff_le_not_le.2 ⟨h₁, hba⟩) (Int.lt_iff_le_not_le.2 ⟨h₂, hc0⟩)
|
||||
|
||||
protected theorem mul_le_mul_of_nonneg_right {a b c : Int}
|
||||
(h₁ : a ≤ b) (h₂ : 0 ≤ c) : a * c ≤ b * c := by
|
||||
rw [Int.mul_comm, Int.mul_comm b]; exact Int.mul_le_mul_of_nonneg_left h₁ h₂
|
||||
|
||||
protected theorem mul_le_mul {a b c d : Int}
|
||||
(hac : a ≤ c) (hbd : b ≤ d) (nn_b : 0 ≤ b) (nn_c : 0 ≤ c) : a * b ≤ c * d :=
|
||||
Int.le_trans (Int.mul_le_mul_of_nonneg_right hac nn_b) (Int.mul_le_mul_of_nonneg_left hbd nn_c)
|
||||
|
||||
protected theorem mul_nonpos_of_nonneg_of_nonpos {a b : Int}
|
||||
(ha : 0 ≤ a) (hb : b ≤ 0) : a * b ≤ 0 := by
|
||||
have h : a * b ≤ a * 0 := Int.mul_le_mul_of_nonneg_left hb ha
|
||||
rwa [Int.mul_zero] at h
|
||||
|
||||
protected theorem mul_nonpos_of_nonpos_of_nonneg {a b : Int}
|
||||
(ha : a ≤ 0) (hb : 0 ≤ b) : a * b ≤ 0 := by
|
||||
have h : a * b ≤ 0 * b := Int.mul_le_mul_of_nonneg_right ha hb
|
||||
rwa [Int.zero_mul] at h
|
||||
|
||||
protected theorem mul_le_mul_of_nonpos_right {a b c : Int}
|
||||
(h : b ≤ a) (hc : c ≤ 0) : a * c ≤ b * c :=
|
||||
have : -c ≥ 0 := Int.neg_nonneg_of_nonpos hc
|
||||
have : b * -c ≤ a * -c := Int.mul_le_mul_of_nonneg_right h this
|
||||
Int.le_of_neg_le_neg <| by rwa [← Int.neg_mul_eq_mul_neg, ← Int.neg_mul_eq_mul_neg] at this
|
||||
|
||||
protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
|
||||
(ha : a ≤ 0) (h : c ≤ b) : a * b ≤ a * c := by
|
||||
rw [Int.mul_comm a b, Int.mul_comm a c]
|
||||
apply Int.mul_le_mul_of_nonpos_right h ha
|
||||
|
||||
/- ## natAbs -/
|
||||
|
||||
@[simp] theorem natAbs_ofNat (n : Nat) : natAbs ↑n = n := rfl
|
||||
@[simp] theorem natAbs_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
|
||||
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
|
||||
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : Nat) := rfl
|
||||
|
||||
@[simp] theorem natAbs_eq_zero : natAbs a = 0 ↔ a = 0 :=
|
||||
⟨fun H => match a with
|
||||
| ofNat _ => congrArg ofNat H
|
||||
| -[_+1] => absurd H (succ_ne_zero _),
|
||||
fun e => e ▸ rfl⟩
|
||||
|
||||
theorem natAbs_pos : 0 < natAbs a ↔ a ≠ 0 := by rw [Nat.pos_iff_ne_zero, Ne, natAbs_eq_zero]
|
||||
|
||||
@[simp] theorem natAbs_neg : ∀ (a : Int), natAbs (-a) = natAbs a
|
||||
| 0 => rfl
|
||||
| succ _ => rfl
|
||||
| -[_+1] => rfl
|
||||
|
||||
theorem natAbs_eq : ∀ (a : Int), a = natAbs a ∨ a = -↑(natAbs a)
|
||||
| ofNat _ => Or.inl rfl
|
||||
| -[_+1] => Or.inr rfl
|
||||
|
||||
theorem natAbs_negOfNat (n : Nat) : natAbs (negOfNat n) = n := by
|
||||
cases n <;> rfl
|
||||
|
||||
theorem natAbs_mul (a b : Int) : natAbs (a * b) = natAbs a * natAbs b := by
|
||||
cases a <;> cases b <;>
|
||||
simp only [← Int.mul_def, Int.mul, natAbs_negOfNat] <;> simp only [natAbs]
|
||||
|
||||
theorem natAbs_eq_natAbs_iff {a b : Int} : a.natAbs = b.natAbs ↔ a = b ∨ a = -b := by
|
||||
constructor <;> intro h
|
||||
· cases Int.natAbs_eq a with
|
||||
| inl h₁ | inr h₁ =>
|
||||
cases Int.natAbs_eq b with
|
||||
| inl h₂ | inr h₂ => rw [h₁, h₂]; simp [h]
|
||||
· cases h with (subst a; try rfl)
|
||||
| inr h => rw [Int.natAbs_neg]
|
||||
|
||||
theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
|
||||
match a, eq_ofNat_of_zero_le H with
|
||||
| _, ⟨_, rfl⟩ => rfl
|
||||
|
||||
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a ≤ 0) : (natAbs a : Int) = -a := by
|
||||
rw [← natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
|
||||
|
||||
/-! ### toNat -/
|
||||
|
||||
theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
|
||||
| (n : Nat) => (Int.max_eq_left (ofNat_zero_le n)).symm
|
||||
| -[n+1] => (Int.max_eq_right (Int.le_of_lt (negSucc_lt_zero n))).symm
|
||||
|
||||
@[simp] theorem toNat_zero : (0 : Int).toNat = 0 := rfl
|
||||
|
||||
@[simp] theorem toNat_one : (1 : Int).toNat = 1 := rfl
|
||||
|
||||
@[simp] theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
|
||||
rw [toNat_eq_max, Int.max_eq_left h]
|
||||
|
||||
@[simp] theorem toNat_ofNat (n : Nat) : toNat ↑n = n := rfl
|
||||
|
||||
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
|
||||
|
||||
theorem self_le_toNat (a : Int) : a ≤ toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
|
||||
|
||||
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 ≤ z) : n ≤ z.toNat ↔ (n : Int) ≤ z := by
|
||||
rw [← Int.ofNat_le, Int.toNat_of_nonneg h]
|
||||
|
||||
@[simp] theorem toNat_lt {n : Nat} {z : Int} (h : 0 ≤ z) : z.toNat < n ↔ z < (n : Int) := by
|
||||
rw [← Int.not_le, ← Nat.not_le, Int.le_toNat h]
|
||||
|
||||
theorem toNat_add {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a + b).toNat = a.toNat + b.toNat :=
|
||||
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
|
||||
| _, _, ⟨_, rfl⟩, ⟨_, rfl⟩ => rfl
|
||||
|
||||
theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toNat + n :=
|
||||
match a, eq_ofNat_of_zero_le ha with | _, ⟨_, rfl⟩ => rfl
|
||||
|
||||
@[simp] theorem pred_toNat : ∀ i : Int, (i - 1).toNat = i.toNat - 1
|
||||
| 0 => rfl
|
||||
| (n+1:Nat) => by simp [ofNat_add]
|
||||
| -[n+1] => rfl
|
||||
|
||||
@[simp] theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
|
||||
| 0 => rfl
|
||||
| (_+1:Nat) => Int.sub_zero _
|
||||
| -[_+1] => Int.zero_sub _
|
||||
|
||||
@[simp] theorem toNat_add_toNat_neg_eq_natAbs : ∀ n : Int, n.toNat + (-n).toNat = n.natAbs
|
||||
| 0 => rfl
|
||||
| (_+1:Nat) => Nat.add_zero _
|
||||
| -[_+1] => Nat.zero_add _
|
||||
|
||||
@[simp] theorem toNat_neg_nat : ∀ n : Nat, (-(n : Int)).toNat = 0
|
||||
| 0 => rfl
|
||||
| _+1 => rfl
|
||||
@@ -7,3 +7,4 @@ prelude
|
||||
import Init.Data.List.Basic
|
||||
import Init.Data.List.BasicAux
|
||||
import Init.Data.List.Control
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
@@ -603,6 +603,27 @@ The longer list is truncated to match the shorter list.
|
||||
def zip : List α → List β → List (Prod α β) :=
|
||||
zipWith Prod.mk
|
||||
|
||||
/--
|
||||
`O(max |xs| |ys|)`.
|
||||
Version of `List.zipWith` that continues to the end of both lists,
|
||||
passing `none` to one argument once the shorter list has run out.
|
||||
-/
|
||||
def zipWithAll (f : Option α → Option β → γ) : List α → List β → List γ
|
||||
| [], bs => bs.map fun b => f none (some b)
|
||||
| a :: as, [] => (a :: as).map fun a => f (some a) none
|
||||
| a :: as, b :: bs => f a b :: zipWithAll f as bs
|
||||
|
||||
@[simp] theorem zipWithAll_nil_right :
|
||||
zipWithAll f as [] = as.map fun a => f (some a) none := by
|
||||
cases as <;> rfl
|
||||
|
||||
@[simp] theorem zipWithAll_nil_left :
|
||||
zipWithAll f [] bs = bs.map fun b => f none (some b) := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem zipWithAll_cons_cons :
|
||||
zipWithAll f (a :: as) (b :: bs) = f (some a) (some b) :: zipWithAll f as bs := rfl
|
||||
|
||||
/--
|
||||
`O(|l|)`. Separates a list of pairs into two lists containing the first components and second components.
|
||||
* `unzip [(x₁, y₁), (x₂, y₂), (x₃, y₃)] = ([x₁, x₂, x₃], [y₁, y₂, y₃])`
|
||||
@@ -706,9 +727,9 @@ inductive lt [LT α] : List α → List α → Prop where
|
||||
instance [LT α] : LT (List α) := ⟨List.lt⟩
|
||||
|
||||
instance hasDecidableLt [LT α] [h : DecidableRel (α:=α) (·<·)] : (l₁ l₂ : List α) → Decidable (l₁ < l₂)
|
||||
| [], [] => isFalse (fun h => nomatch h)
|
||||
| [], [] => isFalse nofun
|
||||
| [], _::_ => isTrue (List.lt.nil _ _)
|
||||
| _::_, [] => isFalse (fun h => nomatch h)
|
||||
| _::_, [] => isFalse nofun
|
||||
| a::as, b::bs =>
|
||||
match h a b with
|
||||
| isTrue h₁ => isTrue (List.lt.head _ _ h₁)
|
||||
@@ -868,6 +889,33 @@ def minimum? [Min α] : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some <| as.foldl min a
|
||||
|
||||
/-- Inserts an element into a list without duplication. -/
|
||||
@[inline] protected def insert [BEq α] (a : α) (l : List α) : List α :=
|
||||
if l.elem a then l else a :: l
|
||||
|
||||
instance decidableBEx (p : α → Prop) [DecidablePred p] :
|
||||
∀ l : List α, Decidable (Exists fun x => x ∈ l ∧ p x)
|
||||
| [] => isFalse nofun
|
||||
| x :: xs =>
|
||||
if h₁ : p x then isTrue ⟨x, .head .., h₁⟩ else
|
||||
match decidableBEx p xs with
|
||||
| isTrue h₂ => isTrue <| let ⟨y, hm, hp⟩ := h₂; ⟨y, .tail _ hm, hp⟩
|
||||
| isFalse h₂ => isFalse fun
|
||||
| ⟨y, .tail _ h, hp⟩ => h₂ ⟨y, h, hp⟩
|
||||
| ⟨_, .head .., hp⟩ => h₁ hp
|
||||
|
||||
instance decidableBAll (p : α → Prop) [DecidablePred p] :
|
||||
∀ l : List α, Decidable (∀ x, x ∈ l → p x)
|
||||
| [] => isTrue nofun
|
||||
| x :: xs =>
|
||||
if h₁ : p x then
|
||||
match decidableBAll p xs with
|
||||
| isTrue h₂ => isTrue fun
|
||||
| y, .tail _ h => h₂ y h
|
||||
| _, .head .. => h₁
|
||||
| isFalse h₂ => isFalse fun H => h₂ fun y hm => H y (.tail _ hm)
|
||||
else isFalse fun H => h₁ <| H x (.head ..)
|
||||
|
||||
instance [BEq α] [LawfulBEq α] : LawfulBEq (List α) where
|
||||
eq_of_beq {as bs} := by
|
||||
induction as generalizing bs with
|
||||
@@ -876,7 +924,7 @@ instance [BEq α] [LawfulBEq α] : LawfulBEq (List α) where
|
||||
cases bs with
|
||||
| nil => intro h; contradiction
|
||||
| cons b bs =>
|
||||
simp [show (a::as == b::bs) = (a == b && as == bs) from rfl]
|
||||
simp [show (a::as == b::bs) = (a == b && as == bs) from rfl, -and_imp]
|
||||
intro ⟨h₁, h₂⟩
|
||||
exact ⟨h₁, ih h₂⟩
|
||||
rfl {as} := by
|
||||
|
||||
@@ -5,6 +5,7 @@ Author: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.List.Basic
|
||||
import Init.Util
|
||||
|
||||
@@ -207,4 +208,42 @@ if the result of each `f a` is a pointer equal value `a`.
|
||||
def mapMono (as : List α) (f : α → α) : List α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
/--
|
||||
Monadic generalization of `List.partition`.
|
||||
|
||||
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic`.
|
||||
-/
|
||||
@[inline] def partitionM [Monad m] (p : α → m Bool) (l : List α) : m (List α × List α) :=
|
||||
go l #[] #[]
|
||||
where
|
||||
/-- Auxiliary for `partitionM`:
|
||||
`partitionM.go p l acc₁ acc₂` returns `(acc₁.toList ++ left, acc₂.toList ++ right)`
|
||||
if `partitionM p l` returns `(left, right)`. -/
|
||||
@[specialize] go : List α → Array α → Array α → m (List α × List α)
|
||||
| [], acc₁, acc₂ => pure (acc₁.toList, acc₂.toList)
|
||||
| x :: xs, acc₁, acc₂ => do
|
||||
if ← p x then
|
||||
go xs (acc₁.push x) acc₂
|
||||
else
|
||||
go xs acc₁ (acc₂.push x)
|
||||
|
||||
/--
|
||||
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
|
||||
whilst partitioning the result it into a pair of lists, `List β × List γ`,
|
||||
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
|
||||
```
|
||||
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
|
||||
```
|
||||
-/
|
||||
@[inline] def partitionMap (f : α → β ⊕ γ) (l : List α) : List β × List γ := go l #[] #[] where
|
||||
/-- Auxiliary for `partitionMap`:
|
||||
`partitionMap.go f l acc₁ acc₂ = (acc₁.toList ++ left, acc₂.toList ++ right)`
|
||||
if `partitionMap f l = (left, right)`. -/
|
||||
@[specialize] go : List α → Array β → Array γ → List β × List γ
|
||||
| [], acc₁, acc₂ => (acc₁.toList, acc₂.toList)
|
||||
| x :: xs, acc₁, acc₂ =>
|
||||
match f x with
|
||||
| .inl a => go xs (acc₁.push a) acc₂
|
||||
| .inr b => go xs acc₁ (acc₂.push b)
|
||||
|
||||
end List
|
||||
|
||||
708
src/Init/Data/List/Lemmas.lean
Normal file
708
src/Init/Data/List/Lemmas.lean
Normal file
@@ -0,0 +1,708 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.BasicAux
|
||||
import Init.Data.List.Control
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.PropLemmas
|
||||
import Init.Control.Lawful
|
||||
import Init.Hints
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-!
|
||||
# Bootstrapping theorems for lists
|
||||
|
||||
These are theorems used in the definitions of `Std.Data.List.Basic` and tactics.
|
||||
New theorems should be added to `Std.Data.List.Lemmas` if they are not needed by the bootstrap.
|
||||
-/
|
||||
|
||||
attribute [simp] concat_eq_append append_assoc
|
||||
|
||||
@[simp] theorem get?_nil : @get? α [] n = none := rfl
|
||||
@[simp] theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
|
||||
@[simp] theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
|
||||
@[simp] theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
|
||||
@[simp] theorem head?_nil : @head? α [] = none := rfl
|
||||
@[simp] theorem head?_cons : @head? α (a::l) = some a := rfl
|
||||
@[simp 1100] theorem headD_nil : @headD α [] d = d := rfl
|
||||
@[simp 1100] theorem headD_cons : @headD α (a::l) d = a := rfl
|
||||
@[simp] theorem head_cons : @head α (a::l) h = a := rfl
|
||||
@[simp] theorem tail?_nil : @tail? α [] = none := rfl
|
||||
@[simp] theorem tail?_cons : @tail? α (a::l) = some l := rfl
|
||||
@[simp] theorem tail!_cons : @tail! α (a::l) = l := rfl
|
||||
@[simp 1100] theorem tailD_nil : @tailD α [] l' = l' := rfl
|
||||
@[simp 1100] theorem tailD_cons : @tailD α (a::l) l' = l := rfl
|
||||
@[simp] theorem any_nil : [].any f = false := rfl
|
||||
@[simp] theorem any_cons : (a::l).any f = (f a || l.any f) := rfl
|
||||
@[simp] theorem all_nil : [].all f = true := rfl
|
||||
@[simp] theorem all_cons : (a::l).all f = (f a && l.all f) := rfl
|
||||
@[simp] theorem or_nil : [].or = false := rfl
|
||||
@[simp] theorem or_cons : (a::l).or = (a || l.or) := rfl
|
||||
@[simp] theorem and_nil : [].and = true := rfl
|
||||
@[simp] theorem and_cons : (a::l).and = (a && l.and) := rfl
|
||||
|
||||
/-! ### length -/
|
||||
|
||||
theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl
|
||||
|
||||
theorem ne_nil_of_length_eq_succ (_ : length l = succ n) : l ≠ [] := fun _ => nomatch l
|
||||
|
||||
theorem length_eq_zero : length l = 0 ↔ l = [] :=
|
||||
⟨eq_nil_of_length_eq_zero, fun h => h ▸ rfl⟩
|
||||
|
||||
/-! ### mem -/
|
||||
|
||||
@[simp] theorem not_mem_nil (a : α) : ¬ a ∈ [] := nofun
|
||||
|
||||
@[simp] theorem mem_cons : a ∈ (b :: l) ↔ a = b ∨ a ∈ l :=
|
||||
⟨fun h => by cases h <;> simp [Membership.mem, *],
|
||||
fun | Or.inl rfl => by constructor | Or.inr h => by constructor; assumption⟩
|
||||
|
||||
theorem mem_cons_self (a : α) (l : List α) : a ∈ a :: l := .head ..
|
||||
|
||||
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a ∈ l → a ∈ y :: l := .tail _
|
||||
|
||||
theorem eq_nil_iff_forall_not_mem {l : List α} : l = [] ↔ ∀ a, a ∉ l := by
|
||||
cases l <;> simp [-not_or]
|
||||
|
||||
/-! ### append -/
|
||||
|
||||
@[simp 1100] theorem singleton_append : [x] ++ l = x :: l := rfl
|
||||
|
||||
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
|
||||
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₂ :=
|
||||
(append_inj h hl).right
|
||||
|
||||
theorem append_inj_left (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : s₁ = s₂ :=
|
||||
(append_inj h hl).left
|
||||
|
||||
theorem append_inj' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ ∧ t₁ = t₂ :=
|
||||
append_inj h <| @Nat.add_right_cancel _ (length t₁) _ <| by
|
||||
let hap := congrArg length h; simp only [length_append, ← hl] at hap; exact hap
|
||||
|
||||
theorem append_inj_right' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : t₁ = t₂ :=
|
||||
(append_inj' h hl).right
|
||||
|
||||
theorem append_inj_left' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ :=
|
||||
(append_inj' h hl).left
|
||||
|
||||
theorem append_right_inj {t₁ t₂ : List α} (s) : s ++ t₁ = s ++ t₂ ↔ t₁ = t₂ :=
|
||||
⟨fun h => append_inj_right h rfl, congrArg _⟩
|
||||
|
||||
theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ :=
|
||||
⟨fun h => append_inj_left' h rfl, congrArg (· ++ _)⟩
|
||||
|
||||
@[simp] theorem append_eq_nil : p ++ q = [] ↔ p = [] ∧ q = [] := by
|
||||
cases p <;> simp
|
||||
|
||||
theorem get_append : ∀ {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length),
|
||||
(l₁ ++ l₂).get ⟨n, length_append .. ▸ Nat.lt_add_right _ h⟩ = l₁.get ⟨n, h⟩
|
||||
| a :: l, _, 0, h => rfl
|
||||
| a :: l, _, n+1, h => by simp only [get, cons_append]; apply get_append
|
||||
|
||||
/-! ### map -/
|
||||
|
||||
@[simp] theorem map_nil {f : α → β} : map f [] = [] := rfl
|
||||
|
||||
@[simp] theorem map_cons (f : α → β) a l : map f (a :: l) = f a :: map f l := rfl
|
||||
|
||||
@[simp] theorem map_append (f : α → β) : ∀ l₁ l₂, map f (l₁ ++ l₂) = map f l₁ ++ map f l₂ := by
|
||||
intro l₁; induction l₁ <;> intros <;> simp_all
|
||||
|
||||
@[simp] theorem map_id (l : List α) : map id l = l := by induction l <;> simp_all
|
||||
|
||||
@[simp] theorem map_id' (l : List α) : map (fun a => a) l = l := by induction l <;> simp_all
|
||||
|
||||
@[simp] theorem mem_map {f : α → β} : ∀ {l : List α}, b ∈ l.map f ↔ ∃ a, a ∈ l ∧ f a = b
|
||||
| [] => by simp
|
||||
| _ :: l => by simp [mem_map (l := l), eq_comm (a := b)]
|
||||
|
||||
theorem mem_map_of_mem (f : α → β) (h : a ∈ l) : f a ∈ map f l := mem_map.2 ⟨_, h, rfl⟩
|
||||
|
||||
@[simp] theorem map_map (g : β → γ) (f : α → β) (l : List α) :
|
||||
map g (map f l) = map (g ∘ f) l := by induction l <;> simp_all
|
||||
|
||||
/-! ### bind -/
|
||||
|
||||
@[simp] theorem nil_bind (f : α → List β) : List.bind [] f = [] := by simp [join, List.bind]
|
||||
|
||||
@[simp] theorem cons_bind x xs (f : α → List β) :
|
||||
List.bind (x :: xs) f = f x ++ List.bind xs f := by simp [join, List.bind]
|
||||
|
||||
@[simp] theorem append_bind xs ys (f : α → List β) :
|
||||
List.bind (xs ++ ys) f = List.bind xs f ++ List.bind ys f := by
|
||||
induction xs; {rfl}; simp_all [cons_bind, append_assoc]
|
||||
|
||||
@[simp] theorem bind_id (l : List (List α)) : List.bind l id = l.join := by simp [List.bind]
|
||||
|
||||
/-! ### join -/
|
||||
|
||||
@[simp] theorem join_nil : List.join ([] : List (List α)) = [] := rfl
|
||||
|
||||
@[simp] theorem join_cons : (l :: ls).join = l ++ ls.join := rfl
|
||||
|
||||
/-! ### bounded quantifiers over Lists -/
|
||||
|
||||
theorem forall_mem_cons {p : α → Prop} {a : α} {l : List α} :
|
||||
(∀ x, x ∈ a :: l → p x) ↔ p a ∧ ∀ x, x ∈ l → p x :=
|
||||
⟨fun H => ⟨H _ (.head ..), fun _ h => H _ (.tail _ h)⟩,
|
||||
fun ⟨H₁, H₂⟩ _ => fun | .head .. => H₁ | .tail _ h => H₂ _ h⟩
|
||||
|
||||
/-! ### reverse -/
|
||||
|
||||
@[simp] theorem reverseAux_nil : reverseAux [] r = r := rfl
|
||||
@[simp] theorem reverseAux_cons : reverseAux (a::l) r = reverseAux l (a::r) := rfl
|
||||
|
||||
theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
|
||||
reverseAux_eq_append ..
|
||||
|
||||
theorem reverse_map (f : α → β) (l : List α) : (l.map f).reverse = l.reverse.map f := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[simp] theorem reverse_eq_nil_iff {xs : List α} : xs.reverse = [] ↔ xs = [] := by
|
||||
match xs with
|
||||
| [] => simp
|
||||
| x :: xs => simp
|
||||
|
||||
/-! ### nth element -/
|
||||
|
||||
theorem get_of_mem : ∀ {a} {l : List α}, a ∈ l → ∃ n, get l n = a
|
||||
| _, _ :: _, .head .. => ⟨⟨0, Nat.succ_pos _⟩, rfl⟩
|
||||
| _, _ :: _, .tail _ m => let ⟨⟨n, h⟩, e⟩ := get_of_mem m; ⟨⟨n+1, Nat.succ_lt_succ h⟩, e⟩
|
||||
|
||||
theorem get_mem : ∀ (l : List α) n h, get l ⟨n, h⟩ ∈ l
|
||||
| _ :: _, 0, _ => .head ..
|
||||
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
|
||||
|
||||
theorem mem_iff_get {a} {l : List α} : a ∈ l ↔ ∃ n, get l n = a :=
|
||||
⟨get_of_mem, fun ⟨_, e⟩ => e ▸ get_mem ..⟩
|
||||
|
||||
theorem get?_len_le : ∀ {l : List α} {n}, length l ≤ n → l.get? n = none
|
||||
| [], _, _ => rfl
|
||||
| _ :: l, _+1, h => get?_len_le (l := l) <| Nat.le_of_succ_le_succ h
|
||||
|
||||
theorem get?_eq_get : ∀ {l : List α} {n} (h : n < l.length), l.get? n = some (get l ⟨n, h⟩)
|
||||
| _ :: _, 0, _ => rfl
|
||||
| _ :: l, _+1, _ => get?_eq_get (l := l) _
|
||||
|
||||
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 _⟩
|
||||
|
||||
@[simp] 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⟩
|
||||
|
||||
@[simp] theorem get?_map (f : α → β) : ∀ l n, (map f l).get? n = (l.get? n).map f
|
||||
| [], _ => rfl
|
||||
| _ :: _, 0 => rfl
|
||||
| _ :: l, n+1 => get?_map f l n
|
||||
|
||||
theorem get?_append {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
|
||||
(l₁ ++ l₂).get? n = l₁.get? n := by
|
||||
have hn' : n < (l₁ ++ l₂).length := Nat.lt_of_lt_of_le hn <|
|
||||
length_append .. ▸ Nat.le_add_right ..
|
||||
rw [get?_eq_get hn, get?_eq_get hn', get_append]
|
||||
|
||||
@[simp] theorem get?_concat_length : ∀ (l : List α) (a : α), (l ++ [a]).get? l.length = some a
|
||||
| [], a => rfl
|
||||
| b :: l, a => by rw [cons_append, length_cons]; simp only [get?, get?_concat_length]
|
||||
|
||||
theorem getLast_eq_get : ∀ (l : List α) (h : l ≠ []),
|
||||
getLast l h = l.get ⟨l.length - 1, by
|
||||
match l with
|
||||
| [] => contradiction
|
||||
| a :: l => exact Nat.le_refl _⟩
|
||||
| [a], h => rfl
|
||||
| a :: b :: l, h => by
|
||||
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_get]
|
||||
|
||||
@[simp] theorem getLast?_nil : @getLast? α [] = none := rfl
|
||||
|
||||
theorem getLast?_eq_getLast : ∀ l h, @getLast? α l = some (getLast l h)
|
||||
| [], h => nomatch h rfl
|
||||
| _::_, _ => rfl
|
||||
|
||||
theorem getLast?_eq_get? : ∀ (l : List α), getLast? l = l.get? (l.length - 1)
|
||||
| [] => rfl
|
||||
| a::l => by rw [getLast?_eq_getLast (a::l) nofun, getLast_eq_get, get?_eq_get]
|
||||
|
||||
@[simp] theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
|
||||
simp [getLast?_eq_get?, Nat.succ_sub_succ]
|
||||
|
||||
theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a
|
||||
| [], _, _ => rfl
|
||||
| _a::_, 0, _ => rfl
|
||||
| _::l, _+1, _ => getD_eq_get? (l := l) ..
|
||||
|
||||
theorem get?_append_right : ∀ {l₁ l₂ : List α} {n : Nat}, l₁.length ≤ n →
|
||||
(l₁ ++ l₂).get? n = l₂.get? (n - l₁.length)
|
||||
| [], _, n, _ => rfl
|
||||
| a :: l, _, n+1, h₁ => by rw [cons_append]; simp [get?_append_right (Nat.lt_succ.1 h₁)]
|
||||
|
||||
theorem get?_reverse' : ∀ {l : List α} (i j), i + j + 1 = length l →
|
||||
get? l.reverse i = get? l j
|
||||
| [], _, _, _ => rfl
|
||||
| a::l, i, 0, h => by simp at h; simp [h, get?_append_right]
|
||||
| a::l, i, j+1, h => by
|
||||
have := Nat.succ.inj h; simp at this ⊢
|
||||
rw [get?_append, get?_reverse' _ j this]
|
||||
rw [length_reverse, ← this]; apply Nat.lt_add_of_pos_right (Nat.succ_pos _)
|
||||
|
||||
theorem get?_reverse {l : List α} (i) (h : i < length l) :
|
||||
get? l.reverse i = get? l (l.length - 1 - i) :=
|
||||
get?_reverse' _ _ <| by
|
||||
rw [Nat.add_sub_of_le (Nat.le_sub_one_of_lt h),
|
||||
Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) h)]
|
||||
|
||||
/-! ### take and drop -/
|
||||
|
||||
@[simp] theorem take_append_drop : ∀ (n : Nat) (l : List α), take n l ++ drop n l = l
|
||||
| 0, _ => rfl
|
||||
| _+1, [] => rfl
|
||||
| n+1, x :: xs => congrArg (cons x) <| take_append_drop n xs
|
||||
|
||||
@[simp] theorem length_drop : ∀ (i : Nat) (l : List α), length (drop i l) = length l - i
|
||||
| 0, _ => rfl
|
||||
| succ i, [] => Eq.symm (Nat.zero_sub (succ i))
|
||||
| succ i, x :: l => calc
|
||||
length (drop (succ i) (x :: l)) = length l - i := length_drop i l
|
||||
_ = succ (length l) - succ i := (Nat.succ_sub_succ_eq_sub (length l) i).symm
|
||||
|
||||
theorem drop_length_le {l : List α} (h : l.length ≤ i) : drop i l = [] :=
|
||||
length_eq_zero.1 (length_drop .. ▸ Nat.sub_eq_zero_of_le h)
|
||||
|
||||
theorem take_length_le {l : List α} (h : l.length ≤ i) : take i l = l := by
|
||||
have := take_append_drop i l
|
||||
rw [drop_length_le h, append_nil] at this; exact this
|
||||
|
||||
@[simp] theorem take_zero (l : List α) : l.take 0 = [] := rfl
|
||||
|
||||
@[simp] theorem take_nil : ([] : List α).take i = [] := by cases i <;> rfl
|
||||
|
||||
@[simp] theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
@[simp] theorem drop_zero (l : List α) : l.drop 0 = l := rfl
|
||||
|
||||
@[simp] theorem drop_succ_cons : (a :: l).drop (n + 1) = l.drop n := rfl
|
||||
|
||||
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_length_le (Nat.le_refl _)
|
||||
|
||||
@[simp] theorem take_length (l : List α) : take l.length l = l := take_length_le (Nat.le_refl _)
|
||||
|
||||
theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
|
||||
(l.take i).concat l[i] = l.take (i+1) :=
|
||||
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
|
||||
rw [concat_eq_append, append_assoc, singleton_append, get_drop_eq_drop, take_append_drop]
|
||||
|
||||
theorem reverse_concat (l : List α) (a : α) : (l.concat a).reverse = a :: l.reverse := by
|
||||
rw [concat_eq_append, reverse_append]; rfl
|
||||
|
||||
/-! ### takeWhile and dropWhile -/
|
||||
|
||||
@[simp] theorem dropWhile_nil : ([] : List α).dropWhile p = [] := rfl
|
||||
|
||||
theorem dropWhile_cons :
|
||||
(x :: xs : List α).dropWhile p = if p x then xs.dropWhile p else x :: xs := by
|
||||
split <;> simp_all [dropWhile]
|
||||
|
||||
/-! ### foldlM and foldrM -/
|
||||
|
||||
@[simp] theorem foldlM_reverse [Monad m] (l : List α) (f : β → α → m β) (b) :
|
||||
l.reverse.foldlM f b = l.foldrM (fun x y => f y x) b := rfl
|
||||
|
||||
@[simp] theorem foldlM_nil [Monad m] (f : β → α → m β) (b) : [].foldlM f b = pure b := rfl
|
||||
|
||||
@[simp] theorem foldlM_cons [Monad m] (f : β → α → m β) (b) (a) (l : List α) :
|
||||
(a :: l).foldlM f b = f b a >>= l.foldlM f := by
|
||||
simp [List.foldlM]
|
||||
|
||||
@[simp] theorem foldlM_append [Monad m] [LawfulMonad m] (f : β → α → m β) (b) (l l' : List α) :
|
||||
(l ++ l').foldlM f b = l.foldlM f b >>= l'.foldlM f := by
|
||||
induction l generalizing b <;> simp [*]
|
||||
|
||||
@[simp] theorem foldrM_nil [Monad m] (f : α → β → m β) (b) : [].foldrM f b = pure b := rfl
|
||||
|
||||
@[simp] theorem foldrM_cons [Monad m] [LawfulMonad m] (a : α) (l) (f : α → β → m β) (b) :
|
||||
(a :: l).foldrM f b = l.foldrM f b >>= f a := by
|
||||
simp only [foldrM]
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem foldrM_reverse [Monad m] (l : List α) (f : α → β → m β) (b) :
|
||||
l.reverse.foldrM f b = l.foldlM (fun x y => f y x) b :=
|
||||
(foldlM_reverse ..).symm.trans <| by simp
|
||||
|
||||
theorem foldl_eq_foldlM (f : β → α → β) (b) (l : List α) :
|
||||
l.foldl f b = l.foldlM (m := Id) f b := by
|
||||
induction l generalizing b <;> simp [*, foldl]
|
||||
|
||||
theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
|
||||
l.foldr f b = l.foldrM (m := Id) f b := by
|
||||
induction l <;> simp [*, foldr]
|
||||
|
||||
/-! ### foldl and foldr -/
|
||||
|
||||
@[simp] theorem foldl_reverse (l : List α) (f : β → α → β) (b) :
|
||||
l.reverse.foldl f b = l.foldr (fun x y => f y x) b := by simp [foldl_eq_foldlM, foldr_eq_foldrM]
|
||||
|
||||
@[simp] theorem foldr_reverse (l : List α) (f : α → β → β) (b) :
|
||||
l.reverse.foldr f b = l.foldl (fun x y => f y x) b :=
|
||||
(foldl_reverse ..).symm.trans <| by simp
|
||||
|
||||
@[simp] theorem foldrM_append [Monad m] [LawfulMonad m] (f : α → β → m β) (b) (l l' : List α) :
|
||||
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[simp] theorem foldl_append {β : Type _} (f : β → α → β) (b) (l l' : List α) :
|
||||
(l ++ l').foldl f b = l'.foldl f (l.foldl f b) := by simp [foldl_eq_foldlM]
|
||||
|
||||
@[simp] theorem foldr_append (f : α → β → β) (b) (l l' : List α) :
|
||||
(l ++ l').foldr f b = l.foldr f (l'.foldr f b) := by simp [foldr_eq_foldrM]
|
||||
|
||||
@[simp] theorem foldl_nil : [].foldl f b = b := rfl
|
||||
|
||||
@[simp] theorem foldl_cons (l : List α) (b : β) : (a :: l).foldl f b = l.foldl f (f b a) := rfl
|
||||
|
||||
@[simp] theorem foldr_nil : [].foldr f b = b := rfl
|
||||
|
||||
@[simp] theorem foldr_cons (l : List α) : (a :: l).foldr f b = f a (l.foldr f b) := rfl
|
||||
|
||||
@[simp] theorem foldr_self_append (l : List α) : l.foldr cons l' = l ++ l' := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
theorem foldr_self (l : List α) : l.foldr cons [] = l := by simp
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
|
||||
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
| a :: l => return (← f a) :: (← l.mapM' f)
|
||||
|
||||
@[simp] theorem mapM'_nil [Monad m] {f : α → m β} : mapM' f [] = pure [] := rfl
|
||||
@[simp] theorem mapM'_cons [Monad m] {f : α → m β} :
|
||||
mapM' f (a :: l) = return ((← f a) :: (← l.mapM' f)) :=
|
||||
rfl
|
||||
|
||||
theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α) :
|
||||
mapM' f l = mapM f l := by simp [go, mapM] where
|
||||
go : ∀ l acc, mapM.loop f l acc = return acc.reverse ++ (← mapM' f l)
|
||||
| [], acc => by simp [mapM.loop, mapM']
|
||||
| a::l, acc => by simp [go l, mapM.loop, mapM']
|
||||
|
||||
@[simp] theorem mapM_nil [Monad m] (f : α → m β) : [].mapM f = pure [] := rfl
|
||||
|
||||
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α → m β) :
|
||||
(a :: l).mapM f = (return (← f a) :: (← l.mapM f)) := by simp [← mapM'_eq_mapM, mapM']
|
||||
|
||||
@[simp] theorem mapM_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 [*]
|
||||
|
||||
/-! ### forM -/
|
||||
|
||||
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
|
||||
-- As such we need to replace `List.forM_nil` and `List.forM_cons` from Lean:
|
||||
|
||||
@[simp] theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
|
||||
|
||||
@[simp] theorem forM_cons' [Monad m] :
|
||||
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
|
||||
List.forM_cons _ _ _
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
@[simp] theorem eraseIdx_nil : ([] : List α).eraseIdx i = [] := rfl
|
||||
@[simp] theorem eraseIdx_cons_zero : (a::as).eraseIdx 0 = as := rfl
|
||||
@[simp] theorem eraseIdx_cons_succ : (a::as).eraseIdx (i+1) = a :: as.eraseIdx i := rfl
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@[simp] theorem find?_nil : ([] : List α).find? p = none := rfl
|
||||
theorem find?_cons : (a::as).find? p = match p a with | true => some a | false => as.find? p :=
|
||||
rfl
|
||||
|
||||
/-! ### filter -/
|
||||
|
||||
@[simp] theorem filter_nil (p : α → Bool) : filter p [] = [] := rfl
|
||||
|
||||
@[simp] theorem filter_cons_of_pos {p : α → Bool} {a : α} (l) (pa : p a) :
|
||||
filter p (a :: l) = a :: filter p l := by rw [filter, pa]
|
||||
|
||||
@[simp] theorem filter_cons_of_neg {p : α → Bool} {a : α} (l) (pa : ¬ p a) :
|
||||
filter p (a :: l) = filter p l := by rw [filter, eq_false_of_ne_true pa]
|
||||
|
||||
theorem filter_cons :
|
||||
(x :: xs : List α).filter p = if p x then x :: (xs.filter p) else xs.filter p := by
|
||||
split <;> simp [*]
|
||||
|
||||
theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
|
||||
induction as with
|
||||
| nil => simp [filter]
|
||||
| cons a as ih =>
|
||||
by_cases h : p a
|
||||
· simp_all [or_and_left]
|
||||
· simp_all [or_and_right]
|
||||
|
||||
theorem filter_eq_nil {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
|
||||
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
|
||||
|
||||
/-! ### findSome? -/
|
||||
|
||||
@[simp] theorem findSome?_nil : ([] : List α).findSome? f = none := rfl
|
||||
theorem findSome?_cons {f : α → Option β} :
|
||||
(a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
|
||||
rfl
|
||||
|
||||
/-! ### replace -/
|
||||
|
||||
@[simp] theorem replace_nil [BEq α] : ([] : List α).replace a b = [] := rfl
|
||||
theorem replace_cons [BEq α] {a : α} :
|
||||
(a::as).replace b c = match a == b with | true => c::as | false => a :: replace as b c :=
|
||||
rfl
|
||||
@[simp] theorem replace_cons_self [BEq α] [LawfulBEq α] {a : α} : (a::as).replace a b = b::as := by
|
||||
simp [replace_cons]
|
||||
|
||||
/-! ### elem -/
|
||||
|
||||
@[simp] theorem elem_nil [BEq α] : ([] : List α).elem a = false := rfl
|
||||
theorem elem_cons [BEq α] {a : α} :
|
||||
(a::as).elem b = match b == a with | true => true | false => as.elem b :=
|
||||
rfl
|
||||
@[simp] theorem elem_cons_self [BEq α] [LawfulBEq α] {a : α} : (a::as).elem a = true := by
|
||||
simp [elem_cons]
|
||||
|
||||
/-! ### lookup -/
|
||||
|
||||
@[simp] theorem lookup_nil [BEq α] : ([] : List (α × β)).lookup a = none := rfl
|
||||
theorem lookup_cons [BEq α] {k : α} :
|
||||
((k,b)::es).lookup a = match a == k with | true => some b | false => es.lookup a :=
|
||||
rfl
|
||||
@[simp] theorem lookup_cons_self [BEq α] [LawfulBEq α] {k : α} : ((k,b)::es).lookup k = some b := by
|
||||
simp [lookup_cons]
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
@[simp] theorem zipWith_nil_left {f : α → β → γ} : zipWith f [] l = [] := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem zipWith_nil_right {f : α → β → γ} : zipWith f l [] = [] := by
|
||||
simp [zipWith]
|
||||
|
||||
@[simp] theorem zipWith_cons_cons {f : α → β → γ} :
|
||||
zipWith f (a :: as) (b :: bs) = f a b :: zipWith f as bs := by
|
||||
rfl
|
||||
|
||||
theorem zipWith_get? {f : α → β → γ} :
|
||||
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
induction as generalizing bs i with
|
||||
| nil => cases bs with
|
||||
| nil => simp
|
||||
| cons b bs => simp
|
||||
| cons a as aih => cases bs with
|
||||
| nil => simp
|
||||
| cons b bs => cases i <;> simp_all
|
||||
|
||||
/-! ### zipWithAll -/
|
||||
|
||||
theorem zipWithAll_get? {f : Option α → Option β → γ} :
|
||||
(zipWithAll f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
induction as generalizing bs i with
|
||||
| nil => induction bs generalizing i with
|
||||
| nil => simp
|
||||
| cons b bs bih => cases i <;> simp_all
|
||||
| cons a as aih => cases bs with
|
||||
| nil =>
|
||||
specialize @aih []
|
||||
cases i <;> simp_all
|
||||
| cons b bs => cases i <;> simp_all
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
@[simp] theorem zip_nil_left : zip ([] : List α) (l : List β) = [] := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem zip_nil_right : zip (l : List α) ([] : List β) = [] := by
|
||||
simp [zip]
|
||||
|
||||
@[simp] theorem zip_cons_cons : zip (a :: as) (b :: bs) = (a, b) :: zip as bs := by
|
||||
rfl
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
@[simp] theorem unzip_nil : ([] : List (α × β)).unzip = ([], []) := rfl
|
||||
@[simp] theorem unzip_cons {h : α × β} :
|
||||
(h :: t).unzip = match unzip t with | (al, bl) => (h.1::al, h.2::bl) := rfl
|
||||
|
||||
/-! ### all / any -/
|
||||
|
||||
@[simp] theorem all_eq_true {l : List α} : l.all p ↔ ∀ x, x ∈ l → p x := by induction l <;> simp [*]
|
||||
|
||||
@[simp] theorem any_eq_true {l : List α} : l.any p ↔ ∃ x, x ∈ l ∧ p x := by induction l <;> simp [*]
|
||||
|
||||
/-! ### enumFrom -/
|
||||
|
||||
@[simp] theorem enumFrom_nil : ([] : List α).enumFrom i = [] := rfl
|
||||
@[simp] theorem enumFrom_cons : (a::as).enumFrom i = (i, a) :: as.enumFrom (i+1) := rfl
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
@[simp] theorem iota_zero : iota 0 = [] := rfl
|
||||
@[simp] theorem iota_succ : iota (i+1) = (i+1) :: iota i := rfl
|
||||
|
||||
/-! ### intersperse -/
|
||||
|
||||
@[simp] theorem intersperse_nil (sep : α) : ([] : List α).intersperse sep = [] := rfl
|
||||
@[simp] theorem intersperse_single (sep : α) : [x].intersperse sep = [x] := rfl
|
||||
@[simp] theorem intersperse_cons₂ (sep : α) :
|
||||
(x::y::zs).intersperse sep = x::sep::((y::zs).intersperse sep) := rfl
|
||||
|
||||
/-! ### isPrefixOf -/
|
||||
|
||||
@[simp] theorem isPrefixOf_nil_left [BEq α] : isPrefixOf ([] : List α) l = true := by
|
||||
simp [isPrefixOf]
|
||||
@[simp] theorem isPrefixOf_cons_nil [BEq α] : isPrefixOf (a::as) ([] : List α) = false := rfl
|
||||
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
|
||||
isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
|
||||
@[simp] theorem isPrefixOf_cons₂_self [BEq α] [LawfulBEq α] {a : α} :
|
||||
isPrefixOf (a::as) (a::bs) = isPrefixOf as bs := by simp [isPrefixOf_cons₂]
|
||||
|
||||
/-! ### isEqv -/
|
||||
|
||||
@[simp] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
|
||||
@[simp] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
|
||||
@[simp] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
|
||||
theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
|
||||
|
||||
/-! ### dropLast -/
|
||||
|
||||
@[simp] theorem dropLast_nil : ([] : List α).dropLast = [] := rfl
|
||||
@[simp] theorem dropLast_single : [x].dropLast = [] := rfl
|
||||
@[simp] theorem dropLast_cons₂ :
|
||||
(x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
|
||||
|
||||
-- We may want to replace these `simp` attributes with explicit equational lemmas,
|
||||
-- as we already have for all the non-monadic functions.
|
||||
attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
|
||||
|
||||
-- Previously `range.loop`, `mapM.loop`, `filterMapM.loop`, `forIn.loop`, `forIn'.loop`
|
||||
-- had attribute `@[simp]`.
|
||||
-- We don't currently provide simp lemmas,
|
||||
-- as this is an internal implementation and they don't seem to be needed.
|
||||
|
||||
/-! ### minimum? -/
|
||||
|
||||
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `minimum?_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
|
||||
|
||||
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [minimum?]
|
||||
|
||||
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
|
||||
intro xs
|
||||
match xs with
|
||||
| nil => simp
|
||||
| x :: xs =>
|
||||
simp only [minimum?_cons, Option.some.injEq, List.mem_cons]
|
||||
intro eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
simp at eq
|
||||
simp [eq]
|
||||
| cons y xs ind =>
|
||||
simp at eq
|
||||
have p := ind _ eq
|
||||
cases p with
|
||||
| inl p =>
|
||||
cases min_eq_or x y with | _ q => simp [p, q]
|
||||
| inr p => simp [p, mem_cons]
|
||||
|
||||
theorem le_minimum?_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
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [minimum?]
|
||||
intro eq y
|
||||
simp only [Option.some.injEq] at eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
simp at eq
|
||||
simp [eq]
|
||||
| cons z xs ih =>
|
||||
simp at eq
|
||||
simp [ih _ eq, le_min_iff, and_assoc]
|
||||
|
||||
-- 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 ((· : α) ≤ ·)]
|
||||
(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 _)⟩, ?_⟩
|
||||
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))
|
||||
|
||||
@[simp] theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
|
||||
(a :: as).get ⟨i+1, h⟩ = as.get ⟨i, Nat.lt_of_succ_lt_succ h⟩ := rfl
|
||||
|
||||
@[simp] theorem get_cons_succ' {as : List α} {i : Fin as.length} :
|
||||
(a :: as).get i.succ = as.get i := rfl
|
||||
|
||||
@[simp] theorem set_nil (n : Nat) (a : α) : [].set n a = [] := rfl
|
||||
|
||||
@[simp] theorem set_zero (x : α) (xs : List α) (a : α) :
|
||||
(x :: xs).set 0 a = a :: xs := rfl
|
||||
|
||||
@[simp] theorem set_succ (x : α) (xs : List α) (n : Nat) (a : α) :
|
||||
(x :: xs).set n.succ a = x :: xs.set n a := rfl
|
||||
|
||||
@[simp] theorem get_set_eq (l : List α) (i : Nat) (a : α) (h : i < (l.set i a).length) :
|
||||
(l.set i a).get ⟨i, h⟩ = a :=
|
||||
match l, i with
|
||||
| [], _ => by
|
||||
simp at h
|
||||
contradiction
|
||||
| _ :: _, 0 => by
|
||||
simp
|
||||
| _ :: l, i + 1 => by
|
||||
simp [get_set_eq l]
|
||||
|
||||
@[simp] theorem get_set_ne (l : List α) {i j : Nat} (h : i ≠ j) (a : α)
|
||||
(hj : j < (l.set i a).length) :
|
||||
(l.set i a).get ⟨j, hj⟩ = l.get ⟨j, by simp at hj; exact hj⟩ :=
|
||||
match l, i, j with
|
||||
| [], _, _ => by
|
||||
simp
|
||||
| _ :: _, 0, 0 => by
|
||||
contradiction
|
||||
| _ :: _, 0, _ + 1 => by
|
||||
simp
|
||||
| _ :: _, _ + 1, 0 => by
|
||||
simp
|
||||
| _ :: l, i + 1, j + 1 => by
|
||||
have g : i ≠ j := h ∘ congrArg (· + 1)
|
||||
simp [get_set_ne l g]
|
||||
@@ -6,10 +6,14 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Data.Nat.Dvd
|
||||
import Init.Data.Nat.Gcd
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.Nat.Bitwise
|
||||
import Init.Data.Nat.Control
|
||||
import Init.Data.Nat.Log2
|
||||
import Init.Data.Nat.Power2
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Data.Nat.SOM
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Nat.Mod
|
||||
|
||||
@@ -147,13 +147,20 @@ protected theorem add_right_comm (n m k : Nat) : (n + m) + k = (n + k) + m := by
|
||||
|
||||
protected theorem add_left_cancel {n m k : Nat} : n + m = n + k → m = k := by
|
||||
induction n with
|
||||
| zero => simp; intros; assumption
|
||||
| zero => simp
|
||||
| succ n ih => simp [succ_add]; intro h; apply ih h
|
||||
|
||||
protected theorem add_right_cancel {n m k : Nat} (h : n + m = k + m) : n = k := by
|
||||
rw [Nat.add_comm n m, Nat.add_comm k m] at h
|
||||
apply Nat.add_left_cancel h
|
||||
|
||||
theorem eq_zero_of_add_eq_zero : ∀ {n m}, n + m = 0 → n = 0 ∧ m = 0
|
||||
| 0, 0, _ => ⟨rfl, rfl⟩
|
||||
| _+1, 0, h => Nat.noConfusion h
|
||||
|
||||
protected theorem eq_zero_of_add_eq_zero_left (h : n + m = 0) : m = 0 :=
|
||||
(Nat.eq_zero_of_add_eq_zero h).2
|
||||
|
||||
/-! # Nat.mul theorems -/
|
||||
|
||||
@[simp] protected theorem mul_zero (n : Nat) : n * 0 = 0 :=
|
||||
@@ -182,7 +189,7 @@ protected theorem mul_comm : ∀ (n m : Nat), n * m = m * n
|
||||
Nat.mul_comm n 1 ▸ Nat.mul_one n
|
||||
|
||||
protected theorem left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := by
|
||||
induction n generalizing m k with
|
||||
induction n with
|
||||
| zero => repeat rw [Nat.zero_mul]
|
||||
| succ n ih => simp [succ_mul, ih]; rw [Nat.add_assoc, Nat.add_assoc (n*m)]; apply congrArg; apply Nat.add_left_comm
|
||||
|
||||
@@ -206,16 +213,13 @@ protected theorem mul_left_comm (n m k : Nat) : n * (m * k) = m * (n * k) := by
|
||||
|
||||
attribute [simp] Nat.le_refl
|
||||
|
||||
theorem succ_lt_succ {n m : Nat} : n < m → succ n < succ m :=
|
||||
succ_le_succ
|
||||
theorem succ_lt_succ {n m : Nat} : n < m → succ n < succ m := succ_le_succ
|
||||
|
||||
theorem lt_succ_of_le {n m : Nat} : n ≤ m → n < succ m :=
|
||||
succ_le_succ
|
||||
theorem lt_succ_of_le {n m : Nat} : n ≤ m → n < succ m := succ_le_succ
|
||||
|
||||
@[simp] protected theorem sub_zero (n : Nat) : n - 0 = n :=
|
||||
rfl
|
||||
@[simp] protected theorem sub_zero (n : Nat) : n - 0 = n := rfl
|
||||
|
||||
theorem succ_sub_succ_eq_sub (n m : Nat) : succ n - succ m = n - m := by
|
||||
@[simp] theorem succ_sub_succ_eq_sub (n m : Nat) : succ n - succ m = n - m := by
|
||||
induction m with
|
||||
| zero => exact rfl
|
||||
| succ m ih => apply congrArg pred ih
|
||||
@@ -241,8 +245,7 @@ theorem sub_lt : ∀ {n m : Nat}, 0 < n → 0 < m → n - m < n
|
||||
show n - m < succ n from
|
||||
lt_succ_of_le (sub_le n m)
|
||||
|
||||
theorem sub_succ (n m : Nat) : n - succ m = pred (n - m) :=
|
||||
rfl
|
||||
theorem sub_succ (n m : Nat) : n - succ m = pred (n - m) := rfl
|
||||
|
||||
theorem succ_sub_succ (n m : Nat) : succ n - succ m = n - m :=
|
||||
succ_sub_succ_eq_sub n m
|
||||
@@ -277,20 +280,24 @@ instance : Trans (. ≤ . : Nat → Nat → Prop) (. < . : Nat → Nat → Prop)
|
||||
protected theorem le_of_eq {n m : Nat} (p : n = m) : n ≤ m :=
|
||||
p ▸ Nat.le_refl n
|
||||
|
||||
theorem le_of_succ_le {n m : Nat} (h : succ n ≤ m) : n ≤ m :=
|
||||
Nat.le_trans (le_succ n) h
|
||||
|
||||
protected theorem le_of_lt {n m : Nat} (h : n < m) : n ≤ m :=
|
||||
le_of_succ_le h
|
||||
|
||||
theorem lt.step {n m : Nat} : n < m → n < succ m := le_step
|
||||
|
||||
theorem le_of_succ_le {n m : Nat} (h : succ n ≤ m) : n ≤ m := Nat.le_trans (le_succ n) h
|
||||
theorem lt_of_succ_lt {n m : Nat} : succ n < m → n < m := le_of_succ_le
|
||||
protected theorem le_of_lt {n m : Nat} : n < m → n ≤ m := le_of_succ_le
|
||||
|
||||
theorem lt_of_succ_lt_succ {n m : Nat} : succ n < succ m → n < m := le_of_succ_le_succ
|
||||
|
||||
theorem lt_of_succ_le {n m : Nat} (h : succ n ≤ m) : n < m := h
|
||||
theorem succ_le_of_lt {n m : Nat} (h : n < m) : succ n ≤ m := h
|
||||
|
||||
theorem eq_zero_or_pos : ∀ (n : Nat), n = 0 ∨ n > 0
|
||||
| 0 => Or.inl rfl
|
||||
| _+1 => Or.inr (succ_pos _)
|
||||
|
||||
theorem lt.base (n : Nat) : n < succ n := Nat.le_refl (succ n)
|
||||
protected theorem pos_of_ne_zero {n : Nat} : n ≠ 0 → 0 < n := (eq_zero_or_pos n).resolve_left
|
||||
|
||||
theorem lt.base (n : Nat) : n < succ n := Nat.le_refl (succ n)
|
||||
theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
|
||||
|
||||
protected theorem le_total (m n : Nat) : m ≤ n ∨ n ≤ m :=
|
||||
@@ -298,20 +305,7 @@ protected theorem le_total (m n : Nat) : m ≤ n ∨ n ≤ m :=
|
||||
| Or.inl h => Or.inl (Nat.le_of_lt h)
|
||||
| Or.inr h => Or.inr h
|
||||
|
||||
theorem eq_zero_of_le_zero {n : Nat} (h : n ≤ 0) : n = 0 :=
|
||||
Nat.le_antisymm h (zero_le _)
|
||||
|
||||
theorem lt_of_succ_lt {n m : Nat} : succ n < m → n < m :=
|
||||
le_of_succ_le
|
||||
|
||||
theorem lt_of_succ_lt_succ {n m : Nat} : succ n < succ m → n < m :=
|
||||
le_of_succ_le_succ
|
||||
|
||||
theorem lt_of_succ_le {n m : Nat} (h : succ n ≤ m) : n < m :=
|
||||
h
|
||||
|
||||
theorem succ_le_of_lt {n m : Nat} (h : n < m) : succ n ≤ m :=
|
||||
h
|
||||
theorem eq_zero_of_le_zero {n : Nat} (h : n ≤ 0) : n = 0 := Nat.le_antisymm h (zero_le _)
|
||||
|
||||
theorem zero_lt_of_lt : {a b : Nat} → a < b → 0 < b
|
||||
| 0, _, h => h
|
||||
@@ -326,8 +320,7 @@ theorem zero_lt_of_ne_zero {a : Nat} (h : a ≠ 0) : 0 < a := by
|
||||
|
||||
attribute [simp] Nat.lt_irrefl
|
||||
|
||||
theorem ne_of_lt {a b : Nat} (h : a < b) : a ≠ b :=
|
||||
fun he => absurd (he ▸ h) (Nat.lt_irrefl a)
|
||||
theorem ne_of_lt {a b : Nat} (h : a < b) : a ≠ b := fun he => absurd (he ▸ h) (Nat.lt_irrefl a)
|
||||
|
||||
theorem le_or_eq_of_le_succ {m n : Nat} (h : m ≤ succ n) : m ≤ n ∨ m = succ n :=
|
||||
Decidable.byCases
|
||||
@@ -363,16 +356,51 @@ protected theorem not_le_of_gt {n m : Nat} (h : n > m) : ¬ n ≤ m := fun h₁
|
||||
| Or.inr h₂ =>
|
||||
have Heq : n = m := Nat.le_antisymm h₁ h₂
|
||||
absurd (@Eq.subst _ _ _ _ Heq h) (Nat.lt_irrefl m)
|
||||
protected theorem not_le_of_lt : ∀{a b : Nat}, a < b → ¬(b ≤ a) := Nat.not_le_of_gt
|
||||
protected theorem not_lt_of_ge : ∀{a b : Nat}, b ≥ a → ¬(b < a) := flip Nat.not_le_of_gt
|
||||
protected theorem not_lt_of_le : ∀{a b : Nat}, a ≤ b → ¬(b < a) := flip Nat.not_le_of_gt
|
||||
protected theorem lt_le_asymm : ∀{a b : Nat}, a < b → ¬(b ≤ a) := Nat.not_le_of_gt
|
||||
protected theorem le_lt_asymm : ∀{a b : Nat}, a ≤ b → ¬(b < a) := flip Nat.not_le_of_gt
|
||||
|
||||
theorem gt_of_not_le {n m : Nat} (h : ¬ n ≤ m) : n > m :=
|
||||
match Nat.lt_or_ge m n with
|
||||
| Or.inl h₁ => h₁
|
||||
| Or.inr h₁ => absurd h₁ h
|
||||
theorem gt_of_not_le {n m : Nat} (h : ¬ n ≤ m) : n > m := (Nat.lt_or_ge m n).resolve_right h
|
||||
protected theorem lt_of_not_ge : ∀{a b : Nat}, ¬(b ≥ a) → b < a := Nat.gt_of_not_le
|
||||
protected theorem lt_of_not_le : ∀{a b : Nat}, ¬(a ≤ b) → b < a := Nat.gt_of_not_le
|
||||
|
||||
theorem ge_of_not_lt {n m : Nat} (h : ¬ n < m) : n ≥ m :=
|
||||
match Nat.lt_or_ge n m with
|
||||
| Or.inl h₁ => absurd h₁ h
|
||||
| Or.inr h₁ => h₁
|
||||
theorem ge_of_not_lt {n m : Nat} (h : ¬ n < m) : n ≥ m := (Nat.lt_or_ge n m).resolve_left h
|
||||
protected theorem le_of_not_gt : ∀{a b : Nat}, ¬(b > a) → b ≤ a := Nat.ge_of_not_lt
|
||||
protected theorem le_of_not_lt : ∀{a b : Nat}, ¬(a < b) → b ≤ a := Nat.ge_of_not_lt
|
||||
|
||||
theorem ne_of_gt {a b : Nat} (h : b < a) : a ≠ b := (ne_of_lt h).symm
|
||||
protected theorem ne_of_lt' : ∀{a b : Nat}, a < b → b ≠ a := ne_of_gt
|
||||
|
||||
@[simp] protected theorem not_le {a b : Nat} : ¬ a ≤ b ↔ b < a :=
|
||||
Iff.intro Nat.gt_of_not_le Nat.not_le_of_gt
|
||||
@[simp] protected theorem not_lt {a b : Nat} : ¬ a < b ↔ b ≤ a :=
|
||||
Iff.intro Nat.ge_of_not_lt (flip Nat.not_le_of_gt)
|
||||
|
||||
protected theorem le_of_not_le {a b : Nat} (h : ¬ b ≤ a) : a ≤ b := Nat.le_of_lt (Nat.not_le.1 h)
|
||||
protected theorem le_of_not_ge : ∀{a b : Nat}, ¬(a ≥ b) → a ≤ b:= @Nat.le_of_not_le
|
||||
|
||||
protected theorem lt_trichotomy (a b : Nat) : a < b ∨ a = b ∨ b < a :=
|
||||
match Nat.lt_or_ge a b with
|
||||
| .inl h => .inl h
|
||||
| .inr h =>
|
||||
match Nat.eq_or_lt_of_le h with
|
||||
| .inl h => .inr (.inl h.symm)
|
||||
| .inr h => .inr (.inr h)
|
||||
|
||||
protected theorem lt_or_gt_of_ne {a b : Nat} (ne : a ≠ b) : a < b ∨ a > b :=
|
||||
match Nat.lt_trichotomy a b with
|
||||
| .inl h => .inl h
|
||||
| .inr (.inl e) => False.elim (ne e)
|
||||
| .inr (.inr h) => .inr h
|
||||
|
||||
protected theorem lt_or_lt_of_ne : ∀{a b : Nat}, a ≠ b → a < b ∨ b < a := Nat.lt_or_gt_of_ne
|
||||
|
||||
protected theorem le_antisymm_iff {a b : Nat} : a = b ↔ a ≤ b ∧ b ≤ a :=
|
||||
Iff.intro (fun p => And.intro (Nat.le_of_eq p) (Nat.le_of_eq p.symm))
|
||||
(fun ⟨hle, hge⟩ => Nat.le_antisymm hle hge)
|
||||
protected theorem eq_iff_le_and_ge : ∀{a b : Nat}, a = b ↔ a ≤ b ∧ b ≤ a := @Nat.le_antisymm_iff
|
||||
|
||||
instance : Antisymm ( . ≤ . : Nat → Nat → Prop) where
|
||||
antisymm h₁ h₂ := Nat.le_antisymm h₁ h₂
|
||||
@@ -401,6 +429,8 @@ protected theorem add_lt_add_right {n m : Nat} (h : n < m) (k : Nat) : n + k < m
|
||||
protected theorem zero_lt_one : 0 < (1:Nat) :=
|
||||
zero_lt_succ 0
|
||||
|
||||
protected theorem pos_iff_ne_zero : 0 < n ↔ n ≠ 0 := ⟨ne_of_gt, Nat.pos_of_ne_zero⟩
|
||||
|
||||
theorem add_le_add {a b c d : Nat} (h₁ : a ≤ b) (h₂ : c ≤ d) : a + c ≤ b + d :=
|
||||
Nat.le_trans (Nat.add_le_add_right h₁ c) (Nat.add_le_add_left h₂ b)
|
||||
|
||||
@@ -418,6 +448,9 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
|
||||
rw [Nat.add_comm _ b, Nat.add_comm _ b]
|
||||
apply Nat.le_of_add_le_add_left
|
||||
|
||||
protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _⟩
|
||||
|
||||
/-! # Basic theorems for comparing numerals -/
|
||||
|
||||
theorem ctor_eq_zero : Nat.zero = 0 :=
|
||||
@@ -470,10 +503,10 @@ theorem eq_of_mul_eq_mul_right {n m k : Nat} (hm : 0 < m) (h : n * m = k * m) :
|
||||
|
||||
/-! # power -/
|
||||
|
||||
theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
|
||||
protected theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
|
||||
rfl
|
||||
|
||||
theorem pow_zero (n : Nat) : n^0 = 1 := rfl
|
||||
protected theorem pow_zero (n : Nat) : n^0 = 1 := rfl
|
||||
|
||||
theorem pow_le_pow_of_le_left {n m : Nat} (h : n ≤ m) : ∀ (i : Nat), n^i ≤ m^i
|
||||
| 0 => Nat.le_refl _
|
||||
@@ -527,7 +560,20 @@ theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := by
|
||||
theorem pred_lt' {n m : Nat} (h : m < n) : pred n < n :=
|
||||
pred_lt (not_eq_zero_of_lt h)
|
||||
|
||||
/-! # sub/pred theorems -/
|
||||
/-! # pred theorems -/
|
||||
|
||||
@[simp] protected theorem pred_zero : pred 0 = 0 := rfl
|
||||
@[simp] protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
|
||||
|
||||
theorem succ_pred {a : Nat} (h : a ≠ 0) : a.pred.succ = a := by
|
||||
induction a with
|
||||
| zero => contradiction
|
||||
| succ => rfl
|
||||
|
||||
theorem succ_pred_eq_of_pos : ∀ {n}, 0 < n → succ (pred n) = n
|
||||
| _+1, _ => rfl
|
||||
|
||||
/-! # sub theorems -/
|
||||
|
||||
theorem add_sub_self_left (a b : Nat) : (a + b) - a = b := by
|
||||
induction a with
|
||||
@@ -561,11 +607,6 @@ theorem sub_succ_lt_self (a i : Nat) (h : i < a) : a - (i + 1) < a - i := by
|
||||
apply Nat.zero_lt_sub_of_lt
|
||||
assumption
|
||||
|
||||
theorem succ_pred {a : Nat} (h : a ≠ 0) : a.pred.succ = a := by
|
||||
induction a with
|
||||
| zero => contradiction
|
||||
| succ => rfl
|
||||
|
||||
theorem sub_ne_zero_of_lt : {a b : Nat} → a < b → b - a ≠ 0
|
||||
| 0, 0, h => absurd h (Nat.lt_irrefl 0)
|
||||
| 0, succ b, _ => by simp
|
||||
@@ -580,7 +621,7 @@ theorem add_sub_of_le {a b : Nat} (h : a ≤ b) : a + (b - a) = b := by
|
||||
have : a ≤ b := Nat.le_of_succ_le h
|
||||
rw [sub_succ, Nat.succ_add, ← Nat.add_succ, Nat.succ_pred hne, ih this]
|
||||
|
||||
protected theorem sub_add_cancel {n m : Nat} (h : m ≤ n) : n - m + m = n := by
|
||||
@[simp] protected theorem sub_add_cancel {n m : Nat} (h : m ≤ n) : n - m + m = n := by
|
||||
rw [Nat.add_comm, Nat.add_sub_of_le h]
|
||||
|
||||
protected theorem add_sub_add_right (n k m : Nat) : (n + k) - (m + k) = n - m := by
|
||||
@@ -591,7 +632,7 @@ protected theorem add_sub_add_right (n k m : Nat) : (n + k) - (m + k) = n - m :=
|
||||
protected theorem add_sub_add_left (k n m : Nat) : (k + n) - (k + m) = n - m := by
|
||||
rw [Nat.add_comm k n, Nat.add_comm k m, Nat.add_sub_add_right]
|
||||
|
||||
protected theorem add_sub_cancel (n m : Nat) : n + m - m = n :=
|
||||
@[simp] protected theorem add_sub_cancel (n m : Nat) : n + m - m = n :=
|
||||
suffices n + m - (0 + m) = n by rw [Nat.zero_add] at this; assumption
|
||||
by rw [Nat.add_sub_add_right, Nat.sub_zero]
|
||||
|
||||
@@ -680,12 +721,6 @@ theorem lt_sub_of_add_lt {a b c : Nat} (h : a + b < c) : a < c - b :=
|
||||
have : a.succ + b ≤ c := by simp [Nat.succ_add]; exact h
|
||||
le_sub_of_add_le this
|
||||
|
||||
@[simp] protected theorem pred_zero : pred 0 = 0 :=
|
||||
rfl
|
||||
|
||||
@[simp] protected theorem pred_succ (n : Nat) : pred n.succ = n :=
|
||||
rfl
|
||||
|
||||
theorem sub.elim {motive : Nat → Prop}
|
||||
(x y : Nat)
|
||||
(h₁ : y ≤ x → (k : Nat) → x = y + k → motive k)
|
||||
@@ -695,19 +730,76 @@ theorem sub.elim {motive : Nat → Prop}
|
||||
| inl hlt => rw [Nat.sub_eq_zero_of_le (Nat.le_of_lt hlt)]; exact h₂ hlt
|
||||
| inr hle => exact h₁ hle (x - y) (Nat.add_sub_of_le hle).symm
|
||||
|
||||
theorem mul_pred_left (n m : Nat) : pred n * m = n * m - m := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n => rw [Nat.pred_succ, succ_mul, Nat.add_sub_cancel]
|
||||
theorem succ_sub {m n : Nat} (h : n ≤ m) : succ m - n = succ (m - n) := by
|
||||
let ⟨k, hk⟩ := Nat.le.dest h
|
||||
rw [← hk, Nat.add_sub_cancel_left, ← add_succ, Nat.add_sub_cancel_left]
|
||||
|
||||
theorem mul_pred_right (n m : Nat) : n * pred m = n * m - n := by
|
||||
rw [Nat.mul_comm, mul_pred_left, Nat.mul_comm]
|
||||
protected theorem sub_pos_of_lt (h : m < n) : 0 < n - m :=
|
||||
Nat.pos_iff_ne_zero.2 (Nat.sub_ne_zero_of_lt h)
|
||||
|
||||
protected theorem sub_sub (n m k : Nat) : n - m - k = n - (m + k) := by
|
||||
induction k with
|
||||
| zero => simp
|
||||
| succ k ih => rw [Nat.add_succ, Nat.sub_succ, Nat.sub_succ, ih]
|
||||
|
||||
protected theorem sub_le_sub_left (h : n ≤ m) (k : Nat) : k - m ≤ k - n :=
|
||||
match m, le.dest h with
|
||||
| _, ⟨a, rfl⟩ => by rw [← Nat.sub_sub]; apply sub_le
|
||||
|
||||
protected theorem sub_le_sub_right {n m : Nat} (h : n ≤ m) : ∀ k, n - k ≤ m - k
|
||||
| 0 => h
|
||||
| z+1 => pred_le_pred (Nat.sub_le_sub_right h z)
|
||||
|
||||
protected theorem lt_of_sub_ne_zero (h : n - m ≠ 0) : m < n :=
|
||||
Nat.not_le.1 (mt Nat.sub_eq_zero_of_le h)
|
||||
|
||||
protected theorem sub_ne_zero_iff_lt : n - m ≠ 0 ↔ m < n :=
|
||||
⟨Nat.lt_of_sub_ne_zero, Nat.sub_ne_zero_of_lt⟩
|
||||
|
||||
protected theorem lt_of_sub_pos (h : 0 < n - m) : m < n :=
|
||||
Nat.lt_of_sub_ne_zero (Nat.pos_iff_ne_zero.1 h)
|
||||
|
||||
protected theorem lt_of_sub_eq_succ (h : m - n = succ l) : n < m :=
|
||||
Nat.lt_of_sub_pos (h ▸ Nat.zero_lt_succ _)
|
||||
|
||||
protected theorem sub_lt_left_of_lt_add {n k m : Nat} (H : n ≤ k) (h : k < n + m) : k - n < m := by
|
||||
have := Nat.sub_le_sub_right (succ_le_of_lt h) n
|
||||
rwa [Nat.add_sub_cancel_left, Nat.succ_sub H] at this
|
||||
|
||||
protected theorem sub_lt_right_of_lt_add {n k m : Nat} (H : n ≤ k) (h : k < m + n) : k - n < m :=
|
||||
Nat.sub_lt_left_of_lt_add H (Nat.add_comm .. ▸ h)
|
||||
|
||||
protected theorem le_of_sub_eq_zero : ∀ {n m}, n - m = 0 → n ≤ m
|
||||
| 0, _, _ => Nat.zero_le ..
|
||||
| _+1, _+1, h => Nat.succ_le_succ <| Nat.le_of_sub_eq_zero (Nat.succ_sub_succ .. ▸ h)
|
||||
|
||||
protected theorem le_of_sub_le_sub_right : ∀ {n m k : Nat}, k ≤ m → n - k ≤ m - k → n ≤ m
|
||||
| 0, _, _, _, _ => Nat.zero_le ..
|
||||
| _+1, _, 0, _, h₁ => h₁
|
||||
| _+1, _+1, _+1, h₀, h₁ => by
|
||||
simp only [Nat.succ_sub_succ] at h₁
|
||||
exact succ_le_succ <| Nat.le_of_sub_le_sub_right (le_of_succ_le_succ h₀) h₁
|
||||
|
||||
protected theorem sub_le_sub_iff_right {n : Nat} (h : k ≤ m) : n - k ≤ m - k ↔ n ≤ m :=
|
||||
⟨Nat.le_of_sub_le_sub_right h, fun h => Nat.sub_le_sub_right h _⟩
|
||||
|
||||
protected theorem sub_eq_iff_eq_add {c : Nat} (h : b ≤ a) : a - b = c ↔ a = c + b :=
|
||||
⟨fun | rfl => by rw [Nat.sub_add_cancel h], fun heq => by rw [heq, Nat.add_sub_cancel]⟩
|
||||
|
||||
protected theorem sub_eq_iff_eq_add' {c : Nat} (h : b ≤ a) : a - b = c ↔ a = b + c := by
|
||||
rw [Nat.add_comm, Nat.sub_eq_iff_eq_add h]
|
||||
|
||||
theorem mul_pred_left (n m : Nat) : pred n * m = n * m - m := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n => rw [Nat.pred_succ, succ_mul, Nat.add_sub_cancel]
|
||||
|
||||
/-! ## Mul sub distrib -/
|
||||
|
||||
theorem mul_pred_right (n m : Nat) : n * pred m = n * m - n := by
|
||||
rw [Nat.mul_comm, mul_pred_left, Nat.mul_comm]
|
||||
|
||||
|
||||
protected theorem mul_sub_right_distrib (n m k : Nat) : (n - m) * k = n * k - m * k := by
|
||||
induction m with
|
||||
| zero => simp
|
||||
@@ -719,14 +811,12 @@ protected theorem mul_sub_left_distrib (n m k : Nat) : n * (m - k) = n * m - n *
|
||||
/-! # Helper normalization theorems -/
|
||||
|
||||
theorem not_le_eq (a b : Nat) : (¬ (a ≤ b)) = (b + 1 ≤ a) :=
|
||||
propext <| Iff.intro (fun h => Nat.gt_of_not_le h) (fun h => Nat.not_le_of_gt h)
|
||||
|
||||
Eq.propIntro Nat.gt_of_not_le Nat.not_le_of_gt
|
||||
theorem not_ge_eq (a b : Nat) : (¬ (a ≥ b)) = (a + 1 ≤ b) :=
|
||||
not_le_eq b a
|
||||
|
||||
theorem not_lt_eq (a b : Nat) : (¬ (a < b)) = (b ≤ a) :=
|
||||
propext <| Iff.intro (fun h => have h := Nat.succ_le_of_lt (Nat.gt_of_not_le h); Nat.le_of_succ_le_succ h) (fun h => Nat.not_le_of_gt (Nat.succ_le_succ h))
|
||||
|
||||
Eq.propIntro Nat.le_of_not_lt Nat.not_lt_of_le
|
||||
theorem not_gt_eq (a b : Nat) : (¬ (a > b)) = (a ≤ b) :=
|
||||
not_lt_eq b a
|
||||
|
||||
|
||||
@@ -1,54 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Coe
|
||||
|
||||
namespace Nat
|
||||
|
||||
theorem bitwise_rec_lemma {n : Nat} (hNe : n ≠ 0) : n / 2 < n :=
|
||||
Nat.div_lt_self (Nat.zero_lt_of_ne_zero hNe) (Nat.lt_succ_self _)
|
||||
|
||||
def bitwise (f : Bool → Bool → Bool) (n m : Nat) : Nat :=
|
||||
if n = 0 then
|
||||
if f false true then m else 0
|
||||
else if m = 0 then
|
||||
if f true false then n else 0
|
||||
else
|
||||
let n' := n / 2
|
||||
let m' := m / 2
|
||||
let b₁ := n % 2 = 1
|
||||
let b₂ := m % 2 = 1
|
||||
let r := bitwise f n' m'
|
||||
if f b₁ b₂ then
|
||||
r+r+1
|
||||
else
|
||||
r+r
|
||||
decreasing_by apply bitwise_rec_lemma; assumption
|
||||
|
||||
@[extern "lean_nat_land"]
|
||||
def land : @& Nat → @& Nat → Nat := bitwise and
|
||||
@[extern "lean_nat_lor"]
|
||||
def lor : @& Nat → @& Nat → Nat := bitwise or
|
||||
@[extern "lean_nat_lxor"]
|
||||
def xor : @& Nat → @& Nat → Nat := bitwise bne
|
||||
@[extern "lean_nat_shiftl"]
|
||||
def shiftLeft : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
| n, succ m => shiftLeft (2*n) m
|
||||
@[extern "lean_nat_shiftr"]
|
||||
def shiftRight : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
| n, succ m => shiftRight n m / 2
|
||||
|
||||
instance : AndOp Nat := ⟨Nat.land⟩
|
||||
instance : OrOp Nat := ⟨Nat.lor⟩
|
||||
instance : Xor Nat := ⟨Nat.xor⟩
|
||||
instance : ShiftLeft Nat := ⟨Nat.shiftLeft⟩
|
||||
instance : ShiftRight Nat := ⟨Nat.shiftRight⟩
|
||||
|
||||
end Nat
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
import Init.Data.Nat.Bitwise.Lemmas
|
||||
|
||||
63
src/Init/Data/Nat/Bitwise/Basic.lean
Normal file
63
src/Init/Data/Nat/Bitwise/Basic.lean
Normal file
@@ -0,0 +1,63 @@
|
||||
/-
|
||||
Copyright (c) 2019 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.Nat.Basic
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Coe
|
||||
|
||||
namespace Nat
|
||||
|
||||
theorem bitwise_rec_lemma {n : Nat} (hNe : n ≠ 0) : n / 2 < n :=
|
||||
Nat.div_lt_self (Nat.zero_lt_of_ne_zero hNe) (Nat.lt_succ_self _)
|
||||
|
||||
def bitwise (f : Bool → Bool → Bool) (n m : Nat) : Nat :=
|
||||
if n = 0 then
|
||||
if f false true then m else 0
|
||||
else if m = 0 then
|
||||
if f true false then n else 0
|
||||
else
|
||||
let n' := n / 2
|
||||
let m' := m / 2
|
||||
let b₁ := n % 2 = 1
|
||||
let b₂ := m % 2 = 1
|
||||
let r := bitwise f n' m'
|
||||
if f b₁ b₂ then
|
||||
r+r+1
|
||||
else
|
||||
r+r
|
||||
decreasing_by apply bitwise_rec_lemma; assumption
|
||||
|
||||
@[extern "lean_nat_land"]
|
||||
def land : @& Nat → @& Nat → Nat := bitwise and
|
||||
@[extern "lean_nat_lor"]
|
||||
def lor : @& Nat → @& Nat → Nat := bitwise or
|
||||
@[extern "lean_nat_lxor"]
|
||||
def xor : @& Nat → @& Nat → Nat := bitwise bne
|
||||
@[extern "lean_nat_shiftl"]
|
||||
def shiftLeft : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
| n, succ m => shiftLeft (2*n) m
|
||||
@[extern "lean_nat_shiftr"]
|
||||
def shiftRight : @& Nat → @& Nat → Nat
|
||||
| n, 0 => n
|
||||
| n, succ m => shiftRight n m / 2
|
||||
|
||||
instance : AndOp Nat := ⟨Nat.land⟩
|
||||
instance : OrOp Nat := ⟨Nat.lor⟩
|
||||
instance : Xor Nat := ⟨Nat.xor⟩
|
||||
instance : ShiftLeft Nat := ⟨Nat.shiftLeft⟩
|
||||
instance : ShiftRight Nat := ⟨Nat.shiftRight⟩
|
||||
|
||||
/-!
|
||||
### testBit
|
||||
We define an operation for testing individual bits in the binary representation
|
||||
of a number.
|
||||
-/
|
||||
|
||||
/-- `testBit m n` returns whether the `(n+1)` least significant bit is `1` or `0`-/
|
||||
def testBit (m n : Nat) : Bool := (m >>> n) &&& 1 != 0
|
||||
|
||||
end Nat
|
||||
487
src/Init/Data/Nat/Bitwise/Lemmas.lean
Normal file
487
src/Init/Data/Nat/Bitwise/Lemmas.lean
Normal file
@@ -0,0 +1,487 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Bool
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.TacticsExtra
|
||||
import Init.Omega
|
||||
|
||||
/-
|
||||
This module defines properties of the bitwise operations on Natural numbers.
|
||||
|
||||
It is primarily intended to support the bitvector library.
|
||||
-/
|
||||
|
||||
namespace Nat
|
||||
|
||||
@[local simp]
|
||||
private theorem one_div_two : 1/2 = 0 := by trivial
|
||||
|
||||
private theorem two_pow_succ_sub_succ_div_two : (2 ^ (n+1) - (x + 1)) / 2 = 2^n - (x/2 + 1) := by
|
||||
omega
|
||||
|
||||
private theorem two_pow_succ_sub_one_div_two : (2 ^ (n+1) - 1) / 2 = 2^n - 1 :=
|
||||
two_pow_succ_sub_succ_div_two
|
||||
|
||||
private theorem two_mul_sub_one {n : Nat} (n_pos : n > 0) : (2*n - 1) % 2 = 1 := by
|
||||
omega
|
||||
|
||||
/-! ### Preliminaries -/
|
||||
|
||||
/--
|
||||
An induction principal that works on divison by two.
|
||||
-/
|
||||
noncomputable def div2Induction {motive : Nat → Sort u}
|
||||
(n : Nat) (ind : ∀(n : Nat), (n > 0 → motive (n/2)) → motive n) : motive n := by
|
||||
induction n using Nat.strongInductionOn with
|
||||
| ind n hyp =>
|
||||
apply ind
|
||||
intro n_pos
|
||||
if n_eq : n = 0 then
|
||||
simp [n_eq] at n_pos
|
||||
else
|
||||
apply hyp
|
||||
exact Nat.div_lt_self n_pos (Nat.le_refl _)
|
||||
|
||||
@[simp] theorem zero_and (x : Nat) : 0 &&& x = 0 := by rfl
|
||||
|
||||
@[simp] theorem and_zero (x : Nat) : x &&& 0 = 0 := by
|
||||
simp only [HAnd.hAnd, AndOp.and, land]
|
||||
unfold bitwise
|
||||
simp
|
||||
|
||||
@[simp] theorem and_one_is_mod (x : Nat) : x &&& 1 = x % 2 := by
|
||||
if xz : x = 0 then
|
||||
simp [xz, zero_and]
|
||||
else
|
||||
have andz := and_zero (x/2)
|
||||
simp only [HAnd.hAnd, AndOp.and, land] at andz
|
||||
simp only [HAnd.hAnd, AndOp.and, land]
|
||||
unfold bitwise
|
||||
cases mod_two_eq_zero_or_one x with | _ p =>
|
||||
simp [xz, p, andz, one_div_two, mod_eq_of_lt]
|
||||
|
||||
/-! ### testBit -/
|
||||
|
||||
@[simp] theorem zero_testBit (i : Nat) : testBit 0 i = false := by
|
||||
simp only [testBit, zero_shiftRight, zero_and, bne_self_eq_false]
|
||||
|
||||
@[simp] theorem testBit_zero (x : Nat) : testBit x 0 = decide (x % 2 = 1) := by
|
||||
cases mod_two_eq_zero_or_one x with | _ p => simp [testBit, p]
|
||||
|
||||
@[simp] theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
|
||||
unfold testBit
|
||||
simp [shiftRight_succ_inside]
|
||||
|
||||
theorem testBit_to_div_mod {x : Nat} : testBit x i = decide (x / 2^i % 2 = 1) := by
|
||||
induction i generalizing x with
|
||||
| zero =>
|
||||
unfold testBit
|
||||
cases mod_two_eq_zero_or_one x with | _ xz => simp [xz]
|
||||
| succ i hyp =>
|
||||
simp [hyp, Nat.div_div_eq_div_mul, Nat.pow_succ']
|
||||
|
||||
theorem toNat_testBit (x i : Nat) :
|
||||
(x.testBit i).toNat = x / 2 ^ i % 2 := by
|
||||
rw [Nat.testBit_to_div_mod]
|
||||
rcases Nat.mod_two_eq_zero_or_one (x / 2^i) <;> simp_all
|
||||
|
||||
theorem ne_zero_implies_bit_true {x : Nat} (xnz : x ≠ 0) : ∃ i, testBit x i := by
|
||||
induction x using div2Induction with
|
||||
| ind x hyp =>
|
||||
have x_pos : x > 0 := Nat.pos_of_ne_zero xnz
|
||||
match mod_two_eq_zero_or_one x with
|
||||
| Or.inl mod2_eq =>
|
||||
rw [←div_add_mod x 2] at xnz
|
||||
simp only [mod2_eq, ne_eq, Nat.mul_eq_zero, Nat.add_zero, false_or] at xnz
|
||||
have ⟨d, dif⟩ := hyp x_pos xnz
|
||||
apply Exists.intro (d+1)
|
||||
simp_all
|
||||
| Or.inr mod2_eq =>
|
||||
apply Exists.intro 0
|
||||
simp_all
|
||||
|
||||
theorem ne_implies_bit_diff {x y : Nat} (p : x ≠ y) : ∃ i, testBit x i ≠ testBit y i := by
|
||||
induction y using Nat.div2Induction generalizing x with
|
||||
| ind y hyp =>
|
||||
cases Nat.eq_zero_or_pos y with
|
||||
| inl yz =>
|
||||
simp only [yz, Nat.zero_testBit, Bool.eq_false_iff]
|
||||
simp only [yz] at p
|
||||
have ⟨i,ip⟩ := ne_zero_implies_bit_true p
|
||||
apply Exists.intro i
|
||||
simp [ip]
|
||||
| inr ypos =>
|
||||
if lsb_diff : x % 2 = y % 2 then
|
||||
rw [←Nat.div_add_mod x 2, ←Nat.div_add_mod y 2] at p
|
||||
simp only [ne_eq, lsb_diff, Nat.add_right_cancel_iff,
|
||||
Nat.zero_lt_succ, Nat.mul_left_cancel_iff] at p
|
||||
have ⟨i, ieq⟩ := hyp ypos p
|
||||
apply Exists.intro (i+1)
|
||||
simpa
|
||||
else
|
||||
apply Exists.intro 0
|
||||
simp only [testBit_zero]
|
||||
revert lsb_diff
|
||||
cases mod_two_eq_zero_or_one x with | _ p =>
|
||||
cases mod_two_eq_zero_or_one y with | _ q =>
|
||||
simp [p,q]
|
||||
|
||||
/--
|
||||
`eq_of_testBit_eq` allows proving two natural numbers are equal
|
||||
if their bits are all equal.
|
||||
-/
|
||||
theorem eq_of_testBit_eq {x y : Nat} (pred : ∀i, testBit x i = testBit y i) : x = y := by
|
||||
if h : x = y then
|
||||
exact h
|
||||
else
|
||||
let ⟨i,eq⟩ := ne_implies_bit_diff h
|
||||
have p := pred i
|
||||
contradiction
|
||||
|
||||
theorem ge_two_pow_implies_high_bit_true {x : Nat} (p : x ≥ 2^n) : ∃ i, i ≥ n ∧ testBit x i := by
|
||||
induction x using div2Induction generalizing n with
|
||||
| ind x hyp =>
|
||||
have x_pos : x > 0 := Nat.lt_of_lt_of_le (Nat.two_pow_pos n) p
|
||||
have x_ne_zero : x ≠ 0 := Nat.ne_of_gt x_pos
|
||||
match n with
|
||||
| zero =>
|
||||
let ⟨j, jp⟩ := ne_zero_implies_bit_true x_ne_zero
|
||||
exact Exists.intro j (And.intro (Nat.zero_le _) jp)
|
||||
| succ n =>
|
||||
have x_ge_n : x / 2 ≥ 2 ^ n := by
|
||||
simpa [le_div_iff_mul_le, ← Nat.pow_succ'] using p
|
||||
have ⟨j, jp⟩ := @hyp x_pos n x_ge_n
|
||||
apply Exists.intro (j+1)
|
||||
apply And.intro
|
||||
case left =>
|
||||
exact (Nat.succ_le_succ jp.left)
|
||||
case right =>
|
||||
simpa using jp.right
|
||||
|
||||
theorem testBit_implies_ge {x : Nat} (p : testBit x i = true) : x ≥ 2^i := by
|
||||
simp only [testBit_to_div_mod] at p
|
||||
apply Decidable.by_contra
|
||||
intro not_ge
|
||||
have x_lt : x < 2^i := Nat.lt_of_not_le not_ge
|
||||
simp [div_eq_of_lt x_lt] at p
|
||||
|
||||
theorem testBit_lt_two_pow {x i : Nat} (lt : x < 2^i) : x.testBit i = false := by
|
||||
match p : x.testBit i with
|
||||
| false => trivial
|
||||
| true =>
|
||||
exfalso
|
||||
exact Nat.not_le_of_gt lt (testBit_implies_ge p)
|
||||
|
||||
theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = false) : x < 2^n := by
|
||||
apply Decidable.by_contra
|
||||
intro not_lt
|
||||
have x_ge_n := Nat.ge_of_not_lt not_lt
|
||||
have ⟨i, ⟨i_ge_n, test_true⟩⟩ := ge_two_pow_implies_high_bit_true x_ge_n
|
||||
have test_false := p _ i_ge_n
|
||||
simp only [test_true] at test_false
|
||||
|
||||
/-! ### testBit -/
|
||||
|
||||
private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
|
||||
induction x with
|
||||
| zero =>
|
||||
trivial
|
||||
| succ x hyp =>
|
||||
have p : 2 ≤ x + 2 := Nat.le_add_left _ _
|
||||
simp [Nat.mod_eq (x+2) 2, p, hyp]
|
||||
cases Nat.mod_two_eq_zero_or_one x with | _ p => simp [p]
|
||||
|
||||
private theorem testBit_succ_zero : testBit (x + 1) 0 = not (testBit x 0) := by
|
||||
simp [testBit_to_div_mod, succ_mod_two]
|
||||
cases Nat.mod_two_eq_zero_or_one x with | _ p =>
|
||||
simp [p]
|
||||
|
||||
theorem testBit_two_pow_add_eq (x i : Nat) : testBit (2^i + x) i = not (testBit x i) := by
|
||||
simp [testBit_to_div_mod, add_div_left, Nat.two_pow_pos, succ_mod_two]
|
||||
cases mod_two_eq_zero_or_one (x / 2 ^ i) with
|
||||
| _ p => simp [p]
|
||||
|
||||
theorem testBit_mul_two_pow_add_eq (a b i : Nat) :
|
||||
testBit (2^i*a + b) i = Bool.xor (a%2 = 1) (testBit b i) := by
|
||||
match a with
|
||||
| 0 => simp
|
||||
| a+1 =>
|
||||
simp [Nat.mul_succ, Nat.add_assoc,
|
||||
testBit_mul_two_pow_add_eq a,
|
||||
testBit_two_pow_add_eq,
|
||||
Nat.succ_mod_two]
|
||||
cases mod_two_eq_zero_or_one a with
|
||||
| _ p => simp [p]
|
||||
|
||||
theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
|
||||
testBit (2^i + x) j = testBit x j := by
|
||||
have i_def : i = j + (i-j) := (Nat.add_sub_cancel' (Nat.le_of_lt j_lt_i)).symm
|
||||
rw [i_def]
|
||||
simp only [testBit_to_div_mod, Nat.pow_add,
|
||||
Nat.add_comm x, Nat.mul_add_div (Nat.two_pow_pos _)]
|
||||
match i_sub_j_eq : i - j with
|
||||
| 0 =>
|
||||
exfalso
|
||||
rw [Nat.sub_eq_zero_iff_le] at i_sub_j_eq
|
||||
exact Nat.not_le_of_gt j_lt_i i_sub_j_eq
|
||||
| d+1 =>
|
||||
simp [Nat.pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
|
||||
|
||||
@[simp] theorem testBit_mod_two_pow (x j i : Nat) :
|
||||
testBit (x % 2^j) i = (decide (i < j) && testBit x i) := by
|
||||
induction x using Nat.strongInductionOn generalizing j i with
|
||||
| ind x hyp =>
|
||||
rw [mod_eq]
|
||||
rcases Nat.lt_or_ge x (2^j) with x_lt_j | x_ge_j
|
||||
· have not_j_le_x := Nat.not_le_of_gt x_lt_j
|
||||
simp [not_j_le_x]
|
||||
rcases Nat.lt_or_ge i j with i_lt_j | i_ge_j
|
||||
· simp [i_lt_j]
|
||||
· have x_lt : x < 2^i :=
|
||||
calc x < 2^j := x_lt_j
|
||||
_ ≤ 2^i := Nat.pow_le_pow_of_le_right Nat.zero_lt_two i_ge_j
|
||||
simp [Nat.testBit_lt_two_pow x_lt]
|
||||
· generalize y_eq : x - 2^j = y
|
||||
have x_eq : x = y + 2^j := Nat.eq_add_of_sub_eq x_ge_j y_eq
|
||||
simp only [Nat.two_pow_pos, x_eq, Nat.le_add_left, true_and, ite_true]
|
||||
have y_lt_x : y < x := by
|
||||
simp [x_eq]
|
||||
exact Nat.lt_add_of_pos_right (Nat.two_pow_pos j)
|
||||
simp only [hyp y y_lt_x]
|
||||
if i_lt_j : i < j then
|
||||
rw [ Nat.add_comm _ (2^_), testBit_two_pow_add_gt i_lt_j]
|
||||
else
|
||||
simp [i_lt_j]
|
||||
|
||||
theorem testBit_one_zero : testBit 1 0 = true := by trivial
|
||||
|
||||
theorem not_decide_mod_two_eq_one (x : Nat)
|
||||
: (!decide (x % 2 = 1)) = decide (x % 2 = 0) := by
|
||||
cases Nat.mod_two_eq_zero_or_one x <;> (rename_i p; simp [p])
|
||||
|
||||
theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
|
||||
testBit (2^n - (x + 1)) i = (decide (i < n) && ! testBit x i) := by
|
||||
induction i generalizing n x with
|
||||
| zero =>
|
||||
match n with
|
||||
| 0 => simp
|
||||
| n+1 =>
|
||||
simp [not_decide_mod_two_eq_one]
|
||||
omega
|
||||
| succ i ih =>
|
||||
simp only [testBit_succ]
|
||||
match n with
|
||||
| 0 =>
|
||||
simp [decide_eq_false]
|
||||
| n+1 =>
|
||||
rw [Nat.two_pow_succ_sub_succ_div_two, ih]
|
||||
· simp [Nat.succ_lt_succ_iff]
|
||||
· omega
|
||||
|
||||
@[simp] theorem testBit_two_pow_sub_one (n i : Nat) : testBit (2^n-1) i = decide (i < n) := by
|
||||
rw [testBit_two_pow_sub_succ]
|
||||
· simp
|
||||
· exact Nat.two_pow_pos _
|
||||
|
||||
theorem testBit_bool_to_nat (b : Bool) (i : Nat) :
|
||||
testBit (Bool.toNat b) i = (decide (i = 0) && b) := by
|
||||
cases b <;> cases i <;>
|
||||
simp [testBit_to_div_mod, Nat.pow_succ, Nat.mul_comm _ 2,
|
||||
←Nat.div_div_eq_div_mul _ 2, one_div_two,
|
||||
Nat.mod_eq_of_lt]
|
||||
|
||||
/-! ### bitwise -/
|
||||
|
||||
theorem testBit_bitwise
|
||||
(false_false_axiom : f false false = false) (x y i : Nat)
|
||||
: (bitwise f x y).testBit i = f (x.testBit i) (y.testBit i) := by
|
||||
induction i using Nat.strongInductionOn generalizing x y with
|
||||
| ind i hyp =>
|
||||
unfold bitwise
|
||||
if x_zero : x = 0 then
|
||||
cases p : f false true <;>
|
||||
cases yi : testBit y i <;>
|
||||
simp [x_zero, p, yi, false_false_axiom]
|
||||
else if y_zero : y = 0 then
|
||||
simp [x_zero, y_zero]
|
||||
cases p : f true false <;>
|
||||
cases xi : testBit x i <;>
|
||||
simp [p, xi, false_false_axiom]
|
||||
else
|
||||
simp only [x_zero, y_zero, ←Nat.two_mul]
|
||||
cases i with
|
||||
| zero =>
|
||||
cases p : f (decide (x % 2 = 1)) (decide (y % 2 = 1)) <;>
|
||||
simp [p, Nat.mul_add_mod, mod_eq_of_lt]
|
||||
| succ i =>
|
||||
have hyp_i := hyp i (Nat.le_refl (i+1))
|
||||
cases p : f (decide (x % 2 = 1)) (decide (y % 2 = 1)) <;>
|
||||
simp [p, one_div_two, hyp_i, Nat.mul_add_div]
|
||||
|
||||
/-! ### bitwise -/
|
||||
|
||||
@[local simp]
|
||||
private theorem eq_0_of_lt_one (x : Nat) : x < 1 ↔ x = 0 :=
|
||||
Iff.intro
|
||||
(fun p =>
|
||||
match x with
|
||||
| 0 => Eq.refl 0
|
||||
| _+1 => False.elim (not_lt_zero _ (Nat.lt_of_succ_lt_succ p)))
|
||||
(fun p => by simp [p, Nat.zero_lt_succ])
|
||||
|
||||
private theorem eq_0_of_lt (x : Nat) : x < 2^ 0 ↔ x = 0 := eq_0_of_lt_one x
|
||||
|
||||
@[local simp]
|
||||
private theorem zero_lt_pow (n : Nat) : 0 < 2^n := by
|
||||
induction n
|
||||
case zero => simp [eq_0_of_lt]
|
||||
case succ n hyp => simpa [Nat.pow_succ]
|
||||
|
||||
private theorem div_two_le_of_lt_two {m n : Nat} (p : m < 2 ^ succ n) : m / 2 < 2^n := by
|
||||
simp [div_lt_iff_lt_mul Nat.zero_lt_two]
|
||||
exact p
|
||||
|
||||
/-- This provides a bound on bitwise operations. -/
|
||||
theorem bitwise_lt_two_pow (left : x < 2^n) (right : y < 2^n) : (Nat.bitwise f x y) < 2^n := by
|
||||
induction n generalizing x y with
|
||||
| zero =>
|
||||
simp only [eq_0_of_lt] at left right
|
||||
unfold bitwise
|
||||
simp [left, right]
|
||||
| succ n hyp =>
|
||||
unfold bitwise
|
||||
if x_zero : x = 0 then
|
||||
simp only [x_zero, if_pos]
|
||||
by_cases p : f false true = true <;> simp [p, right]
|
||||
else if y_zero : y = 0 then
|
||||
simp only [x_zero, y_zero, if_neg, if_pos]
|
||||
by_cases p : f true false = true <;> simp [p, left]
|
||||
else
|
||||
simp only [x_zero, y_zero, if_neg]
|
||||
have hyp1 := hyp (div_two_le_of_lt_two left) (div_two_le_of_lt_two right)
|
||||
by_cases p : f (decide (x % 2 = 1)) (decide (y % 2 = 1)) = true <;>
|
||||
simp [p, Nat.pow_succ, mul_succ, Nat.add_assoc]
|
||||
case pos =>
|
||||
apply lt_of_succ_le
|
||||
simp only [← Nat.succ_add]
|
||||
apply Nat.add_le_add <;> exact hyp1
|
||||
case neg =>
|
||||
apply Nat.add_lt_add <;> exact hyp1
|
||||
|
||||
/-! ### and -/
|
||||
|
||||
@[simp] theorem testBit_and (x y i : Nat) : (x &&& y).testBit i = (x.testBit i && y.testBit i) := by
|
||||
simp [HAnd.hAnd, AndOp.and, land, testBit_bitwise ]
|
||||
|
||||
theorem and_lt_two_pow (x : Nat) {y n : Nat} (right : y < 2^n) : (x &&& y) < 2^n := by
|
||||
apply lt_pow_two_of_testBit
|
||||
intro i i_ge_n
|
||||
have yf : testBit y i = false := by
|
||||
apply Nat.testBit_lt_two_pow
|
||||
apply Nat.lt_of_lt_of_le right
|
||||
exact pow_le_pow_of_le_right Nat.zero_lt_two i_ge_n
|
||||
simp [testBit_and, yf]
|
||||
|
||||
@[simp] theorem and_pow_two_is_mod (x n : Nat) : x &&& (2^n-1) = x % 2^n := by
|
||||
apply eq_of_testBit_eq
|
||||
intro i
|
||||
simp only [testBit_and, testBit_mod_two_pow]
|
||||
cases testBit x i <;> simp
|
||||
|
||||
theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
|
||||
rw [and_pow_two_is_mod]
|
||||
apply Nat.mod_eq_of_lt lt
|
||||
|
||||
/-! ### lor -/
|
||||
|
||||
@[simp] theorem or_zero (x : Nat) : 0 ||| x = x := by
|
||||
simp only [HOr.hOr, OrOp.or, lor]
|
||||
unfold bitwise
|
||||
simp [@eq_comm _ 0]
|
||||
|
||||
@[simp] theorem zero_or (x : Nat) : x ||| 0 = x := by
|
||||
simp only [HOr.hOr, OrOp.or, lor]
|
||||
unfold bitwise
|
||||
simp [@eq_comm _ 0]
|
||||
|
||||
@[simp] theorem testBit_or (x y i : Nat) : (x ||| y).testBit i = (x.testBit i || y.testBit i) := by
|
||||
simp [HOr.hOr, OrOp.or, lor, testBit_bitwise ]
|
||||
|
||||
theorem or_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ||| y < 2^n :=
|
||||
bitwise_lt_two_pow left right
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp] theorem testBit_xor (x y i : Nat) :
|
||||
(x ^^^ y).testBit i = Bool.xor (x.testBit i) (y.testBit i) := by
|
||||
simp [HXor.hXor, Xor.xor, xor, testBit_bitwise ]
|
||||
|
||||
theorem xor_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ^^^ y < 2^n :=
|
||||
bitwise_lt_two_pow left right
|
||||
|
||||
/-! ### Arithmetic -/
|
||||
|
||||
theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
|
||||
testBit (2 ^ i * a + b) j =
|
||||
if j < i then
|
||||
testBit b j
|
||||
else
|
||||
testBit a (j - i) := by
|
||||
cases Nat.lt_or_ge j i with
|
||||
| inl j_lt =>
|
||||
simp only [j_lt]
|
||||
have i_def : i = j + succ (pred (i-j)) := by
|
||||
rw [succ_pred_eq_of_pos] <;> omega
|
||||
rw [i_def]
|
||||
simp only [testBit_to_div_mod, Nat.pow_add, Nat.mul_assoc]
|
||||
simp only [Nat.mul_add_div (Nat.two_pow_pos _), Nat.mul_add_mod]
|
||||
simp [Nat.pow_succ, Nat.mul_comm _ 2, Nat.mul_assoc, Nat.mul_add_mod]
|
||||
| inr j_ge =>
|
||||
have j_def : j = i + (j-i) := (Nat.add_sub_cancel' j_ge).symm
|
||||
simp only [
|
||||
testBit_to_div_mod,
|
||||
Nat.not_lt_of_le,
|
||||
j_ge,
|
||||
ite_false]
|
||||
simp [congrArg (2^·) j_def, Nat.pow_add,
|
||||
←Nat.div_div_eq_div_mul,
|
||||
Nat.mul_add_div,
|
||||
Nat.div_eq_of_lt b_lt,
|
||||
Nat.two_pow_pos i]
|
||||
|
||||
theorem testBit_mul_pow_two :
|
||||
testBit (2 ^ i * a) j = (decide (j ≥ i) && testBit a (j-i)) := by
|
||||
have gen := testBit_mul_pow_two_add a (Nat.two_pow_pos i) j
|
||||
simp at gen
|
||||
rw [gen]
|
||||
cases Nat.lt_or_ge j i with
|
||||
| _ p => simp [p, Nat.not_le_of_lt, Nat.not_lt_of_le]
|
||||
|
||||
theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^i * a ||| b := by
|
||||
apply eq_of_testBit_eq
|
||||
intro j
|
||||
simp only [testBit_mul_pow_two_add _ b_lt,
|
||||
testBit_or, testBit_mul_pow_two]
|
||||
if j_lt : j < i then
|
||||
simp [Nat.not_le_of_lt, j_lt]
|
||||
else
|
||||
have i_le : i ≤ j := Nat.le_of_not_lt j_lt
|
||||
have b_lt_j :=
|
||||
calc b < 2 ^ i := b_lt
|
||||
_ ≤ 2 ^ j := Nat.pow_le_pow_of_le_right Nat.zero_lt_two i_le
|
||||
simp [i_le, j_lt, testBit_lt_two_pow, b_lt_j]
|
||||
|
||||
/-! ### shiftLeft and shiftRight -/
|
||||
|
||||
@[simp] theorem testBit_shiftLeft (x : Nat) : testBit (x <<< i) j =
|
||||
(decide (j ≥ i) && testBit x (j-i)) := by
|
||||
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_mul_pow_two]
|
||||
|
||||
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
|
||||
simp [testBit, ←shiftRight_add]
|
||||
@@ -7,6 +7,7 @@ prelude
|
||||
import Init.WF
|
||||
import Init.WFTactics
|
||||
import Init.Data.Nat.Basic
|
||||
|
||||
namespace Nat
|
||||
|
||||
theorem div_rec_lemma {x y : Nat} : 0 < y ∧ y ≤ x → x - y < x :=
|
||||
@@ -174,4 +175,136 @@ theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
|
||||
rw [Nat.left_distrib, Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, ih, Nat.add_comm, Nat.sub_add_cancel h.2]
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
theorem div_eq_sub_div (h₁ : 0 < b) (h₂ : b ≤ a) : a / b = (a - b) / b + 1 := by
|
||||
rw [div_eq a, if_pos]; constructor <;> assumption
|
||||
|
||||
|
||||
theorem mod_add_div (m k : Nat) : m % k + k * (m / k) = m := by
|
||||
induction m, k using mod.inductionOn with rw [div_eq, mod_eq]
|
||||
| base x y h => simp [h]
|
||||
| ind x y h IH => simp [h]; rw [Nat.mul_succ, ← Nat.add_assoc, IH, Nat.sub_add_cancel h.2]
|
||||
|
||||
@[simp] protected theorem div_one (n : Nat) : n / 1 = n := by
|
||||
have := mod_add_div n 1
|
||||
rwa [mod_one, Nat.zero_add, Nat.one_mul] at this
|
||||
|
||||
@[simp] protected theorem div_zero (n : Nat) : n / 0 = 0 := by
|
||||
rw [div_eq]; simp [Nat.lt_irrefl]
|
||||
|
||||
@[simp] protected theorem zero_div (b : Nat) : 0 / b = 0 :=
|
||||
(div_eq 0 b).trans <| if_neg <| And.rec Nat.not_le_of_gt
|
||||
|
||||
theorem le_div_iff_mul_le (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y := by
|
||||
induction y, k using mod.inductionOn generalizing x with
|
||||
(rw [div_eq]; simp [h]; cases x with | zero => simp [zero_le] | succ x => ?_)
|
||||
| base y k h =>
|
||||
simp [not_succ_le_zero x, succ_mul, Nat.add_comm]
|
||||
refine Nat.lt_of_lt_of_le ?_ (Nat.le_add_right ..)
|
||||
exact Nat.not_le.1 fun h' => h ⟨k0, h'⟩
|
||||
| ind y k h IH =>
|
||||
rw [← add_one, Nat.add_le_add_iff_right, IH k0, succ_mul,
|
||||
← Nat.add_sub_cancel (x*k) k, Nat.sub_le_sub_iff_right h.2, Nat.add_sub_cancel]
|
||||
|
||||
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 _)
|
||||
|
||||
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)
|
||||
|
||||
@[simp] theorem add_div_right (x : Nat) {z : Nat} (H : 0 < z) : (x + z) / z = succ (x / z) := by
|
||||
rw [div_eq_sub_div H (Nat.le_add_left _ _), Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem add_div_left (x : Nat) {z : Nat} (H : 0 < z) : (z + x) / z = succ (x / z) := by
|
||||
rw [Nat.add_comm, add_div_right x H]
|
||||
|
||||
theorem add_mul_div_left (x z : Nat) {y : Nat} (H : 0 < y) : (x + y * z) / y = x / y + z := by
|
||||
induction z with
|
||||
| zero => rw [Nat.mul_zero, Nat.add_zero, Nat.add_zero]
|
||||
| succ z ih => rw [mul_succ, ← Nat.add_assoc, add_div_right _ H, ih]; rfl
|
||||
|
||||
theorem add_mul_div_right (x y : Nat) {z : Nat} (H : 0 < z) : (x + y * z) / z = x / z + y := by
|
||||
rw [Nat.mul_comm, add_mul_div_left _ _ H]
|
||||
|
||||
@[simp] theorem add_mod_right (x z : Nat) : (x + z) % z = x % z := by
|
||||
rw [mod_eq_sub_mod (Nat.le_add_left ..), Nat.add_sub_cancel]
|
||||
|
||||
@[simp] theorem add_mod_left (x z : Nat) : (x + z) % x = z % x := by
|
||||
rw [Nat.add_comm, add_mod_right]
|
||||
|
||||
@[simp] theorem add_mul_mod_self_left (x y z : Nat) : (x + y * z) % y = x % y := by
|
||||
match z with
|
||||
| 0 => rw [Nat.mul_zero, Nat.add_zero]
|
||||
| succ z => rw [mul_succ, ← Nat.add_assoc, add_mod_right, add_mul_mod_self_left (z := z)]
|
||||
|
||||
@[simp] theorem add_mul_mod_self_right (x y z : Nat) : (x + y * z) % z = x % z := by
|
||||
rw [Nat.mul_comm, add_mul_mod_self_left]
|
||||
|
||||
@[simp] theorem mul_mod_right (m n : Nat) : (m * n) % m = 0 := by
|
||||
rw [← Nat.zero_add (m * n), add_mul_mod_self_left, zero_mod]
|
||||
|
||||
@[simp] theorem mul_mod_left (m n : Nat) : (m * n) % n = 0 := by
|
||||
rw [Nat.mul_comm, mul_mod_right]
|
||||
|
||||
protected theorem div_eq_of_lt_le (lo : k * n ≤ m) (hi : m < succ k * n) : m / n = k :=
|
||||
have npos : 0 < n := (eq_zero_or_pos _).resolve_left fun hn => by
|
||||
rw [hn, Nat.mul_zero] at hi lo; exact absurd lo (Nat.not_le_of_gt hi)
|
||||
Nat.le_antisymm
|
||||
(le_of_lt_succ ((Nat.div_lt_iff_lt_mul npos).2 hi))
|
||||
((Nat.le_div_iff_mul_le npos).2 lo)
|
||||
|
||||
theorem sub_mul_div (x n p : Nat) (h₁ : n*p ≤ x) : (x - n*p) / n = x / n - p := by
|
||||
match eq_zero_or_pos n with
|
||||
| .inl h₀ => rw [h₀, Nat.div_zero, Nat.div_zero, Nat.zero_sub]
|
||||
| .inr h₀ => induction p with
|
||||
| zero => rw [Nat.mul_zero, Nat.sub_zero, Nat.sub_zero]
|
||||
| succ p IH =>
|
||||
have h₂ : n * p ≤ x := Nat.le_trans (Nat.mul_le_mul_left _ (le_succ _)) h₁
|
||||
have h₃ : x - n * p ≥ n := by
|
||||
apply Nat.le_of_add_le_add_right
|
||||
rw [Nat.sub_add_cancel h₂, Nat.add_comm]
|
||||
rw [mul_succ] at h₁
|
||||
exact h₁
|
||||
rw [sub_succ, ← IH h₂, div_eq_sub_div h₀ h₃]
|
||||
simp [add_one, Nat.pred_succ, mul_succ, Nat.sub_sub]
|
||||
|
||||
theorem mul_sub_div (x n p : Nat) (h₁ : x < n*p) : (n * p - succ x) / n = p - succ (x / n) := by
|
||||
have npos : 0 < n := (eq_zero_or_pos _).resolve_left fun n0 => by
|
||||
rw [n0, Nat.zero_mul] at h₁; exact not_lt_zero _ h₁
|
||||
apply Nat.div_eq_of_lt_le
|
||||
focus
|
||||
rw [Nat.mul_sub_right_distrib, Nat.mul_comm]
|
||||
exact Nat.sub_le_sub_left ((div_lt_iff_lt_mul npos).1 (lt_succ_self _)) _
|
||||
focus
|
||||
show succ (pred (n * p - x)) ≤ (succ (pred (p - x / n))) * n
|
||||
rw [succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁),
|
||||
fun h => succ_pred_eq_of_pos (Nat.sub_pos_of_lt h)] -- TODO: why is the function needed?
|
||||
focus
|
||||
rw [Nat.mul_sub_right_distrib, Nat.mul_comm]
|
||||
exact Nat.sub_le_sub_left (div_mul_le_self ..) _
|
||||
focus
|
||||
rwa [div_lt_iff_lt_mul npos, Nat.mul_comm]
|
||||
|
||||
theorem mul_mod_mul_left (z x y : Nat) : (z * x) % (z * y) = z * (x % y) :=
|
||||
if y0 : y = 0 then by
|
||||
rw [y0, Nat.mul_zero, mod_zero, mod_zero]
|
||||
else if z0 : z = 0 then by
|
||||
rw [z0, Nat.zero_mul, Nat.zero_mul, Nat.zero_mul, mod_zero]
|
||||
else by
|
||||
induction x using Nat.strongInductionOn with
|
||||
| _ n IH =>
|
||||
have y0 : y > 0 := Nat.pos_of_ne_zero y0
|
||||
have z0 : z > 0 := Nat.pos_of_ne_zero z0
|
||||
cases Nat.lt_or_ge n y with
|
||||
| inl yn => rw [mod_eq_of_lt yn, mod_eq_of_lt (Nat.mul_lt_mul_of_pos_left yn z0)]
|
||||
| inr yn =>
|
||||
rw [mod_eq_sub_mod yn, mod_eq_sub_mod (Nat.mul_le_mul_left z yn),
|
||||
← Nat.mul_sub_left_distrib]
|
||||
exact IH _ (sub_lt (Nat.lt_of_lt_of_le y0 yn) y0)
|
||||
|
||||
theorem div_eq_of_lt (h₀ : a < b) : a / b = 0 := by
|
||||
rw [div_eq a, if_neg]
|
||||
intro h₁
|
||||
apply Nat.not_le_of_gt h₀ h₁.right
|
||||
|
||||
end Nat
|
||||
|
||||
100
src/Init/Data/Nat/Dvd.lean
Normal file
100
src/Init/Data/Nat/Dvd.lean
Normal file
@@ -0,0 +1,100 @@
|
||||
/-
|
||||
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Div
|
||||
|
||||
namespace Nat
|
||||
|
||||
/--
|
||||
Divisibility of natural numbers. `a ∣ b` (typed as `\|`) says that
|
||||
there is some `c` such that `b = a * c`.
|
||||
-/
|
||||
instance : Dvd Nat where
|
||||
dvd a b := Exists (fun c => b = a * c)
|
||||
|
||||
protected theorem dvd_refl (a : Nat) : a ∣ a := ⟨1, by simp⟩
|
||||
|
||||
protected theorem dvd_zero (a : Nat) : a ∣ 0 := ⟨0, by simp⟩
|
||||
|
||||
protected theorem dvd_mul_left (a b : Nat) : a ∣ b * a := ⟨b, Nat.mul_comm b a⟩
|
||||
protected theorem dvd_mul_right (a b : Nat) : a ∣ a * b := ⟨b, rfl⟩
|
||||
|
||||
protected theorem dvd_trans {a b c : Nat} (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c :=
|
||||
match h₁, h₂ with
|
||||
| ⟨d, (h₃ : b = a * d)⟩, ⟨e, (h₄ : c = b * e)⟩ =>
|
||||
⟨d * e, show c = a * (d * e) by simp[h₃,h₄, Nat.mul_assoc]⟩
|
||||
|
||||
protected theorem eq_zero_of_zero_dvd {a : Nat} (h : 0 ∣ a) : a = 0 :=
|
||||
let ⟨c, H'⟩ := h; H'.trans c.zero_mul
|
||||
|
||||
@[simp] protected theorem zero_dvd {n : Nat} : 0 ∣ n ↔ n = 0 :=
|
||||
⟨Nat.eq_zero_of_zero_dvd, fun h => h.symm ▸ Nat.dvd_zero 0⟩
|
||||
|
||||
protected theorem dvd_add {a b c : Nat} (h₁ : a ∣ b) (h₂ : a ∣ c) : a ∣ b + c :=
|
||||
let ⟨d, hd⟩ := h₁; let ⟨e, he⟩ := h₂; ⟨d + e, by simp [Nat.left_distrib, hd, he]⟩
|
||||
|
||||
protected theorem dvd_add_iff_right {k m n : Nat} (h : k ∣ m) : k ∣ n ↔ k ∣ m + n :=
|
||||
⟨Nat.dvd_add h,
|
||||
match m, h with
|
||||
| _, ⟨d, rfl⟩ => fun ⟨e, he⟩ =>
|
||||
⟨e - d, by rw [Nat.mul_sub_left_distrib, ← he, Nat.add_sub_cancel_left]⟩⟩
|
||||
|
||||
protected theorem dvd_add_iff_left {k m n : Nat} (h : k ∣ n) : k ∣ m ↔ k ∣ m + n := by
|
||||
rw [Nat.add_comm]; exact Nat.dvd_add_iff_right h
|
||||
|
||||
theorem dvd_mod_iff {k m n : Nat} (h: k ∣ n) : k ∣ m % n ↔ k ∣ m :=
|
||||
have := Nat.dvd_add_iff_left <| Nat.dvd_trans h <| Nat.dvd_mul_right n (m / n)
|
||||
by rwa [mod_add_div] at this
|
||||
|
||||
theorem le_of_dvd {m n : Nat} (h : 0 < n) : m ∣ n → m ≤ n
|
||||
| ⟨k, e⟩ => by
|
||||
revert h
|
||||
rw [e]
|
||||
match k with
|
||||
| 0 => intro hn; simp at hn
|
||||
| pk+1 =>
|
||||
intro
|
||||
have := Nat.mul_le_mul_left m (succ_pos pk)
|
||||
rwa [Nat.mul_one] at this
|
||||
|
||||
protected theorem dvd_antisymm : ∀ {m n : Nat}, m ∣ n → n ∣ m → m = n
|
||||
| _, 0, _, h₂ => Nat.eq_zero_of_zero_dvd h₂
|
||||
| 0, _, h₁, _ => (Nat.eq_zero_of_zero_dvd h₁).symm
|
||||
| _+1, _+1, h₁, h₂ => Nat.le_antisymm (le_of_dvd (succ_pos _) h₁) (le_of_dvd (succ_pos _) h₂)
|
||||
|
||||
theorem pos_of_dvd_of_pos {m n : Nat} (H1 : m ∣ n) (H2 : 0 < n) : 0 < m :=
|
||||
Nat.pos_of_ne_zero fun m0 => Nat.ne_of_gt H2 <| Nat.eq_zero_of_zero_dvd (m0 ▸ H1)
|
||||
|
||||
@[simp] protected theorem one_dvd (n : Nat) : 1 ∣ n := ⟨n, n.one_mul.symm⟩
|
||||
|
||||
theorem eq_one_of_dvd_one {n : Nat} (H : n ∣ 1) : n = 1 := Nat.dvd_antisymm H n.one_dvd
|
||||
|
||||
theorem mod_eq_zero_of_dvd {m n : Nat} (H : m ∣ n) : n % m = 0 := by
|
||||
let ⟨z, H⟩ := H; rw [H, mul_mod_right]
|
||||
|
||||
theorem dvd_of_mod_eq_zero {m n : Nat} (H : n % m = 0) : m ∣ n := by
|
||||
exists n / m
|
||||
have := (mod_add_div n m).symm
|
||||
rwa [H, Nat.zero_add] at this
|
||||
|
||||
theorem dvd_iff_mod_eq_zero (m n : Nat) : m ∣ n ↔ n % m = 0 :=
|
||||
⟨mod_eq_zero_of_dvd, dvd_of_mod_eq_zero⟩
|
||||
|
||||
instance decidable_dvd : @DecidableRel Nat (·∣·) :=
|
||||
fun _ _ => decidable_of_decidable_of_iff (dvd_iff_mod_eq_zero _ _).symm
|
||||
|
||||
theorem emod_pos_of_not_dvd {a b : Nat} (h : ¬ a ∣ b) : 0 < b % a := by
|
||||
rw [dvd_iff_mod_eq_zero] at h
|
||||
exact Nat.pos_of_ne_zero h
|
||||
|
||||
protected theorem mul_div_cancel' {n m : Nat} (H : n ∣ m) : n * (m / n) = m := by
|
||||
have := mod_add_div m n
|
||||
rwa [mod_eq_zero_of_dvd H, Nat.zero_add] at this
|
||||
|
||||
protected theorem div_mul_cancel {n m : Nat} (H : n ∣ m) : m / n * n = m := by
|
||||
rw [Nat.mul_comm, Nat.mul_div_cancel' H]
|
||||
|
||||
end Nat
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Div
|
||||
import Init.Data.Nat.Dvd
|
||||
|
||||
namespace Nat
|
||||
|
||||
@@ -38,4 +38,35 @@ theorem gcd_succ (x y : Nat) : gcd (succ x) y = gcd (y % succ x) (succ x) :=
|
||||
@[simp] theorem gcd_self (n : Nat) : gcd n n = n := by
|
||||
cases n <;> simp [gcd_succ]
|
||||
|
||||
theorem gcd_rec (m n : Nat) : gcd m n = gcd (n % m) m :=
|
||||
match m with
|
||||
| 0 => by have := (mod_zero n).symm; rwa [gcd_zero_right]
|
||||
| _ + 1 => by simp [gcd_succ]
|
||||
|
||||
@[elab_as_elim] theorem gcd.induction {P : Nat → Nat → Prop} (m n : Nat)
|
||||
(H0 : ∀n, P 0 n) (H1 : ∀ m n, 0 < m → P (n % m) m → P m n) : P m n :=
|
||||
Nat.strongInductionOn (motive := fun m => ∀ n, P m n) m
|
||||
(fun
|
||||
| 0, _ => H0
|
||||
| _+1, IH => fun _ => H1 _ _ (succ_pos _) (IH _ (mod_lt _ (succ_pos _)) _) )
|
||||
n
|
||||
|
||||
theorem gcd_dvd (m n : Nat) : (gcd m n ∣ m) ∧ (gcd m n ∣ n) := by
|
||||
induction m, n using gcd.induction with
|
||||
| H0 n => rw [gcd_zero_left]; exact ⟨Nat.dvd_zero n, Nat.dvd_refl n⟩
|
||||
| H1 m n _ IH => rw [← gcd_rec] at IH; exact ⟨IH.2, (dvd_mod_iff IH.2).1 IH.1⟩
|
||||
|
||||
theorem gcd_dvd_left (m n : Nat) : gcd m n ∣ m := (gcd_dvd m n).left
|
||||
|
||||
theorem gcd_dvd_right (m n : Nat) : gcd m n ∣ n := (gcd_dvd m n).right
|
||||
|
||||
theorem gcd_le_left (n) (h : 0 < m) : gcd m n ≤ m := le_of_dvd h <| gcd_dvd_left m n
|
||||
|
||||
theorem gcd_le_right (n) (h : 0 < n) : gcd m n ≤ n := le_of_dvd h <| gcd_dvd_right m n
|
||||
|
||||
theorem dvd_gcd : k ∣ m → k ∣ n → k ∣ gcd m n := by
|
||||
induction m, n using gcd.induction with intro km kn
|
||||
| H0 n => rw [gcd_zero_left]; exact kn
|
||||
| H1 n m _ IH => rw [gcd_rec]; exact IH ((dvd_mod_iff km).2 kn) km
|
||||
|
||||
end Nat
|
||||
|
||||
1066
src/Init/Data/Nat/Lemmas.lean
Normal file
1066
src/Init/Data/Nat/Lemmas.lean
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Coe
|
||||
import Init.Classical
|
||||
import Init.SimpLemmas
|
||||
import Init.ByCases
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.List.Basic
|
||||
import Init.Data.Prod
|
||||
@@ -539,13 +538,13 @@ theorem Expr.eq_of_toNormPoly (ctx : Context) (a b : Expr) (h : a.toNormPoly = b
|
||||
theorem Expr.of_cancel_eq (ctx : Context) (a b c d : Expr) (h : Poly.cancel a.toNormPoly b.toNormPoly = (c.toPoly, d.toPoly)) : (a.denote ctx = b.denote ctx) = (c.denote ctx = d.denote ctx) := by
|
||||
have := Poly.denote_eq_cancel_eq ctx a.toNormPoly b.toNormPoly
|
||||
rw [h] at this
|
||||
simp [toNormPoly, Poly.norm, Poly.denote_eq] at this
|
||||
simp [toNormPoly, Poly.norm, Poly.denote_eq, -eq_iff_iff] at this
|
||||
exact this.symm
|
||||
|
||||
theorem Expr.of_cancel_le (ctx : Context) (a b c d : Expr) (h : Poly.cancel a.toNormPoly b.toNormPoly = (c.toPoly, d.toPoly)) : (a.denote ctx ≤ b.denote ctx) = (c.denote ctx ≤ d.denote ctx) := by
|
||||
have := Poly.denote_le_cancel_eq ctx a.toNormPoly b.toNormPoly
|
||||
rw [h] at this
|
||||
simp [toNormPoly, Poly.norm,Poly.denote_le] at this
|
||||
simp [toNormPoly, Poly.norm,Poly.denote_le, -eq_iff_iff] at this
|
||||
exact this.symm
|
||||
|
||||
theorem Expr.of_cancel_lt (ctx : Context) (a b c d : Expr) (h : Poly.cancel a.inc.toNormPoly b.toNormPoly = (c.inc.toPoly, d.toPoly)) : (a.denote ctx < b.denote ctx) = (c.denote ctx < d.denote ctx) :=
|
||||
@@ -590,7 +589,7 @@ theorem PolyCnstr.denote_mul (ctx : Context) (k : Nat) (c : PolyCnstr) : (c.mul
|
||||
have : (1 == (0 : Nat)) = false := rfl
|
||||
have : (1 == (1 : Nat)) = true := rfl
|
||||
by_cases he : eq = true <;> simp [he, PolyCnstr.mul, PolyCnstr.denote, Poly.denote_le, Poly.denote_eq]
|
||||
<;> by_cases hk : k == 0 <;> (try simp [eq_of_beq hk]) <;> simp [*] <;> apply propext <;> apply Iff.intro <;> intro h
|
||||
<;> by_cases hk : k == 0 <;> (try simp [eq_of_beq hk]) <;> simp [*] <;> apply Iff.intro <;> intro h
|
||||
· exact Nat.eq_of_mul_eq_mul_left (Nat.zero_lt_succ _) h
|
||||
· rw [h]
|
||||
· exact Nat.le_of_mul_le_mul_left h (Nat.zero_lt_succ _)
|
||||
@@ -637,20 +636,18 @@ theorem Poly.of_isNonZero (ctx : Context) {p : Poly} (h : isNonZero p = true) :
|
||||
theorem PolyCnstr.eq_false_of_isUnsat (ctx : Context) {c : PolyCnstr} : c.isUnsat → c.denote ctx = False := by
|
||||
cases c; rename_i eq lhs rhs
|
||||
simp [isUnsat]
|
||||
by_cases he : eq = true <;> simp [he, denote, Poly.denote_eq, Poly.denote_le]
|
||||
by_cases he : eq = true <;> simp [he, denote, Poly.denote_eq, Poly.denote_le, -and_imp]
|
||||
· intro
|
||||
| Or.inl ⟨h₁, h₂⟩ => simp [Poly.of_isZero, h₁]; have := Nat.not_eq_zero_of_lt (Poly.of_isNonZero ctx h₂); simp [this.symm]
|
||||
| Or.inr ⟨h₁, h₂⟩ => simp [Poly.of_isZero, h₂]; have := Nat.not_eq_zero_of_lt (Poly.of_isNonZero ctx h₁); simp [this]
|
||||
· intro ⟨h₁, h₂⟩
|
||||
simp [Poly.of_isZero, h₂]
|
||||
have := Nat.not_eq_zero_of_lt (Poly.of_isNonZero ctx h₁)
|
||||
simp [this]
|
||||
done
|
||||
exact Poly.of_isNonZero ctx h₁
|
||||
|
||||
theorem PolyCnstr.eq_true_of_isValid (ctx : Context) {c : PolyCnstr} : c.isValid → c.denote ctx = True := by
|
||||
cases c; rename_i eq lhs rhs
|
||||
simp [isValid]
|
||||
by_cases he : eq = true <;> simp [he, denote, Poly.denote_eq, Poly.denote_le]
|
||||
by_cases he : eq = true <;> simp [he, denote, Poly.denote_eq, Poly.denote_le, -and_imp]
|
||||
· intro ⟨h₁, h₂⟩
|
||||
simp [Poly.of_isZero, h₁, h₂]
|
||||
· intro h
|
||||
@@ -658,12 +655,12 @@ theorem PolyCnstr.eq_true_of_isValid (ctx : Context) {c : PolyCnstr} : c.isValid
|
||||
|
||||
theorem ExprCnstr.eq_false_of_isUnsat (ctx : Context) (c : ExprCnstr) (h : c.toNormPoly.isUnsat) : c.denote ctx = False := by
|
||||
have := PolyCnstr.eq_false_of_isUnsat ctx h
|
||||
simp at this
|
||||
simp [-eq_iff_iff] at this
|
||||
assumption
|
||||
|
||||
theorem ExprCnstr.eq_true_of_isValid (ctx : Context) (c : ExprCnstr) (h : c.toNormPoly.isValid) : c.denote ctx = True := by
|
||||
have := PolyCnstr.eq_true_of_isValid ctx h
|
||||
simp at this
|
||||
simp [-eq_iff_iff] at this
|
||||
assumption
|
||||
|
||||
theorem Certificate.of_combineHyps (ctx : Context) (c : PolyCnstr) (cs : Certificate) (h : (combineHyps c cs).denote ctx → False) : c.denote ctx → cs.denote ctx := by
|
||||
@@ -712,7 +709,7 @@ theorem Poly.denote_toExpr (ctx : Context) (p : Poly) : p.toExpr.denote ctx = p.
|
||||
|
||||
theorem ExprCnstr.eq_of_toNormPoly_eq (ctx : Context) (c d : ExprCnstr) (h : c.toNormPoly == d.toPoly) : c.denote ctx = d.denote ctx := by
|
||||
have h := congrArg (PolyCnstr.denote ctx) (eq_of_beq h)
|
||||
simp at h
|
||||
simp [-eq_iff_iff] at h
|
||||
assumption
|
||||
|
||||
theorem Expr.eq_of_toNormPoly_eq (ctx : Context) (e e' : Expr) (h : e.toNormPoly == e'.toPoly) : e.denote ctx = e'.denote ctx := by
|
||||
|
||||
56
src/Init/Data/Nat/MinMax.lean
Normal file
56
src/Init/Data/Nat/MinMax.lean
Normal file
@@ -0,0 +1,56 @@
|
||||
/-
|
||||
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.ByCases
|
||||
|
||||
namespace Nat
|
||||
|
||||
/-! # min lemmas -/
|
||||
|
||||
protected theorem min_eq_min (a : Nat) : Nat.min a b = min a b := rfl
|
||||
|
||||
protected theorem min_comm (a b : Nat) : min a b = min b a := by
|
||||
match Nat.lt_trichotomy a b with
|
||||
| .inl h => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
|
||||
| .inr (.inl h) => simp [Nat.min_def, h]
|
||||
| .inr (.inr h) => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
|
||||
|
||||
protected theorem min_le_right (a b : Nat) : min a b ≤ b := by
|
||||
by_cases (a <= b) <;> simp [Nat.min_def, *]
|
||||
protected theorem min_le_left (a b : Nat) : min a b ≤ a :=
|
||||
Nat.min_comm .. ▸ Nat.min_le_right ..
|
||||
|
||||
protected theorem min_eq_left {a b : Nat} (h : a ≤ b) : min a b = a := if_pos h
|
||||
protected theorem min_eq_right {a b : Nat} (h : b ≤ a) : min a b = b :=
|
||||
Nat.min_comm .. ▸ Nat.min_eq_left h
|
||||
|
||||
protected theorem le_min_of_le_of_le {a b c : Nat} : a ≤ b → a ≤ c → a ≤ min b c := by
|
||||
intros; cases Nat.le_total b c with
|
||||
| inl h => rw [Nat.min_eq_left h]; assumption
|
||||
| inr h => rw [Nat.min_eq_right h]; assumption
|
||||
|
||||
protected theorem le_min {a b c : Nat} : a ≤ min b c ↔ a ≤ b ∧ a ≤ c :=
|
||||
⟨fun h => ⟨Nat.le_trans h (Nat.min_le_left ..), Nat.le_trans h (Nat.min_le_right ..)⟩,
|
||||
fun ⟨h₁, h₂⟩ => Nat.le_min_of_le_of_le h₁ h₂⟩
|
||||
|
||||
protected theorem lt_min {a b c : Nat} : a < min b c ↔ a < b ∧ a < c := Nat.le_min
|
||||
|
||||
/-! # max lemmas -/
|
||||
|
||||
protected theorem max_eq_max (a : Nat) : Nat.max a b = max a b := rfl
|
||||
|
||||
protected theorem max_comm (a b : Nat) : max a b = max b a := by
|
||||
simp only [Nat.max_def]
|
||||
by_cases h₁ : a ≤ b <;> by_cases h₂ : b ≤ a <;> simp [h₁, h₂]
|
||||
· exact Nat.le_antisymm h₂ h₁
|
||||
· cases not_or_intro h₁ h₂ <| Nat.le_total ..
|
||||
|
||||
protected theorem le_max_left ( a b : Nat) : a ≤ max a b := by
|
||||
by_cases (a <= b) <;> simp [Nat.max_def, *]
|
||||
protected theorem le_max_right (a b : Nat) : b ≤ max a b :=
|
||||
Nat.max_comm .. ▸ Nat.le_max_left ..
|
||||
|
||||
end Nat
|
||||
76
src/Init/Data/Nat/Mod.lean
Normal file
76
src/Init/Data/Nat/Mod.lean
Normal file
@@ -0,0 +1,76 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega
|
||||
|
||||
/-!
|
||||
# Further results about `mod`.
|
||||
|
||||
This file proves some results about `mod` that are useful for bitblasting,
|
||||
in particular
|
||||
`Nat.mod_mul : x % (a * b) = x % a + a * (x / a % b)`
|
||||
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 `/`,
|
||||
which should probably be moved to their own file.
|
||||
-/
|
||||
|
||||
namespace Nat
|
||||
|
||||
@[simp] protected theorem mul_lt_mul_left (a0 : 0 < a) : a * b < a * c ↔ b < c := by
|
||||
induction a with
|
||||
| zero => simp_all
|
||||
| succ a ih =>
|
||||
cases a
|
||||
· simp
|
||||
· simp_all [succ_eq_add_one, Nat.right_distrib]
|
||||
omega
|
||||
|
||||
@[simp] protected theorem mul_lt_mul_right (a0 : 0 < a) : b * a < c * a ↔ b < c := by
|
||||
rw [Nat.mul_comm b a, Nat.mul_comm c a, Nat.mul_lt_mul_left a0]
|
||||
|
||||
protected theorem lt_of_mul_lt_mul_left {a b c : Nat} (h : a * b < a * c) : b < c := by
|
||||
cases a <;> simp_all
|
||||
|
||||
protected theorem lt_of_mul_lt_mul_right {a b c : Nat} (h : b * a < c * a) : b < c := by
|
||||
rw [Nat.mul_comm b a, Nat.mul_comm c a] at h
|
||||
exact Nat.lt_of_mul_lt_mul_left h
|
||||
|
||||
protected theorem div_lt_of_lt_mul {m n k : Nat} (h : m < n * k) : m / n < k :=
|
||||
Nat.lt_of_mul_lt_mul_left <|
|
||||
calc
|
||||
n * (m / n) ≤ m % n + n * (m / n) := Nat.le_add_left _ _
|
||||
_ = m := mod_add_div _ _
|
||||
_ < n * k := h
|
||||
|
||||
theorem mod_mul_right_div_self (m n k : Nat) : m % (n * k) / n = m / n % k := by
|
||||
rcases Nat.eq_zero_or_pos n with (rfl | hn); simp [mod_zero]
|
||||
rcases Nat.eq_zero_or_pos k with (rfl | hk); simp [mod_zero]
|
||||
conv => rhs; rw [← mod_add_div m (n * k)]
|
||||
rw [Nat.mul_assoc, add_mul_div_left _ _ hn, add_mul_mod_self_left,
|
||||
mod_eq_of_lt (Nat.div_lt_of_lt_mul (mod_lt _ (Nat.mul_pos hn hk)))]
|
||||
|
||||
theorem mod_mul_left_div_self (m n k : Nat) : m % (k * n) / n = m / n % k := by
|
||||
rw [Nat.mul_comm k n, mod_mul_right_div_self]
|
||||
|
||||
@[simp 1100]
|
||||
theorem mod_mul_right_mod (a b c : Nat) : a % (b * c) % b = a % b :=
|
||||
Nat.mod_mod_of_dvd a (Nat.dvd_mul_right b c)
|
||||
|
||||
@[simp 1100]
|
||||
theorem mod_mul_left_mod (a b c : Nat) : a % (b * c) % c = a % c :=
|
||||
Nat.mod_mod_of_dvd a (Nat.mul_comm _ _ ▸ Nat.dvd_mul_left c b)
|
||||
|
||||
theorem mod_mul {a b x : Nat} : x % (a * b) = x % a + a * (x / a % b) := by
|
||||
rw [Nat.add_comm, ← Nat.div_add_mod (x % (a*b)) a, Nat.mod_mul_right_mod,
|
||||
Nat.mod_mul_right_div_self]
|
||||
|
||||
theorem mod_pow_succ {x b k : Nat} :
|
||||
x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b) := by
|
||||
rw [Nat.pow_succ, Nat.mod_mul]
|
||||
|
||||
end Nat
|
||||
@@ -8,6 +8,8 @@ import Init.Data.Nat.Linear
|
||||
|
||||
namespace Nat
|
||||
|
||||
protected theorem two_pow_pos (w : Nat) : 0 < 2^w := Nat.pos_pow_of_pos _ (by decide)
|
||||
|
||||
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
|
||||
have : power * 2 = power + power := by simp_arith
|
||||
rw [this, Nat.sub_add_eq]
|
||||
|
||||
@@ -7,3 +7,4 @@ prelude
|
||||
import Init.Data.Option.Basic
|
||||
import Init.Data.Option.BasicAux
|
||||
import Init.Data.Option.Instances
|
||||
import Init.Data.Option.Lemmas
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
@@ -10,6 +10,9 @@ import Init.Coe
|
||||
|
||||
namespace Option
|
||||
|
||||
deriving instance DecidableEq for Option
|
||||
deriving instance BEq for Option
|
||||
|
||||
def toMonad [Monad m] [Alternative m] : Option α → m α
|
||||
| none => failure
|
||||
| some a => pure a
|
||||
@@ -81,10 +84,131 @@ def merge (fn : α → α → α) : Option α → Option α → Option α
|
||||
| none , some y => some y
|
||||
| some x, some y => some <| fn x y
|
||||
|
||||
end Option
|
||||
@[simp] theorem getD_none : getD none a = a := rfl
|
||||
@[simp] theorem getD_some : getD (some a) b = a := rfl
|
||||
|
||||
deriving instance DecidableEq for Option
|
||||
deriving instance BEq for Option
|
||||
@[simp] theorem map_none' (f : α → β) : none.map f = none := rfl
|
||||
@[simp] theorem map_some' (a) (f : α → β) : (some a).map f = some (f a) := rfl
|
||||
|
||||
@[simp] theorem none_bind (f : α → Option β) : none.bind f = none := rfl
|
||||
@[simp] theorem some_bind (a) (f : α → Option β) : (some a).bind f = f a := rfl
|
||||
|
||||
|
||||
/-- An elimination principle for `Option`. It is a nondependent version of `Option.recOn`. -/
|
||||
@[simp, inline] protected def elim : Option α → β → (α → β) → β
|
||||
| some x, _, f => f x
|
||||
| none, y, _ => y
|
||||
|
||||
/-- Extracts the value `a` from an option that is known to be `some a` for some `a`. -/
|
||||
@[inline] def get {α : Type u} : (o : Option α) → isSome o → α
|
||||
| some x, _ => x
|
||||
|
||||
/-- `guard p a` returns `some a` if `p a` holds, otherwise `none`. -/
|
||||
@[inline] def guard (p : α → Prop) [DecidablePred p] (a : α) : Option α :=
|
||||
if p a then some a else none
|
||||
|
||||
/--
|
||||
Cast of `Option` to `List`. Returns `[a]` if the input is `some a`, and `[]` if it is `none`.
|
||||
-/
|
||||
@[inline] def toList : Option α → List α
|
||||
| none => .nil
|
||||
| some a => .cons a .nil
|
||||
|
||||
/--
|
||||
Cast of `Option` to `Array`. Returns `#[a]` if the input is `some a`, and `#[]` if it is `none`.
|
||||
-/
|
||||
@[inline] def toArray : Option α → Array α
|
||||
| none => List.toArray .nil
|
||||
| some a => List.toArray (.cons a .nil)
|
||||
|
||||
/--
|
||||
Two arguments failsafe function. Returns `f a b` if the inputs are `some a` and `some b`, and
|
||||
"does nothing" otherwise.
|
||||
-/
|
||||
def liftOrGet (f : α → α → α) : Option α → Option α → Option α
|
||||
| none, none => none
|
||||
| some a, none => some a
|
||||
| none, some b => some b
|
||||
| some a, some b => some (f a b)
|
||||
|
||||
/-- Lifts a relation `α → β → Prop` to a relation `Option α → Option β → Prop` by just adding
|
||||
`none ~ none`. -/
|
||||
inductive Rel (r : α → β → Prop) : Option α → Option β → Prop
|
||||
/-- If `a ~ b`, then `some a ~ some b` -/
|
||||
| some {a b} : r a b → Rel r (some a) (some b)
|
||||
/-- `none ~ none` -/
|
||||
| none : Rel r none none
|
||||
|
||||
/-- Flatten an `Option` of `Option`, a specialization of `joinM`. -/
|
||||
@[simp, inline] def join (x : Option (Option α)) : Option α := x.bind id
|
||||
|
||||
/-- Like `Option.mapM` but for applicative functors. -/
|
||||
@[inline] protected def mapA [Applicative m] {α β} (f : α → m β) : Option α → m (Option β)
|
||||
| none => pure none
|
||||
| some x => some <$> f x
|
||||
|
||||
/--
|
||||
If you maybe have a monadic computation in a `[Monad m]` which produces a term of type `α`, then
|
||||
there is a naturally associated way to always perform a computation in `m` which maybe produces a
|
||||
result.
|
||||
-/
|
||||
@[inline] def sequence [Monad m] {α : Type u} : Option (m α) → m (Option α)
|
||||
| none => pure none
|
||||
| some fn => some <$> fn
|
||||
|
||||
/-- A monadic analogue of `Option.elim`. -/
|
||||
@[inline] def elimM [Monad m] (x : m (Option α)) (y : m β) (z : α → m β) : m β :=
|
||||
do (← x).elim y z
|
||||
|
||||
/-- A monadic analogue of `Option.getD`. -/
|
||||
@[inline] def getDM [Monad m] (x : Option α) (y : m α) : m α :=
|
||||
match x with
|
||||
| some a => pure a
|
||||
| none => y
|
||||
|
||||
instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
rfl {x} :=
|
||||
match x with
|
||||
| some x => LawfulBEq.rfl (α := α)
|
||||
| none => rfl
|
||||
eq_of_beq {x y h} := by
|
||||
match x, y with
|
||||
| some x, some y => rw [LawfulBEq.eq_of_beq (α := α) h]
|
||||
| none, none => rfl
|
||||
|
||||
@[simp] theorem all_none : Option.all p none = true := rfl
|
||||
@[simp] theorem all_some : Option.all p (some x) = p x := rfl
|
||||
|
||||
/-- The minimum of two optional values. -/
|
||||
protected def min [Min α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Min.min x y)
|
||||
| some x, none => some x
|
||||
| none, some y => some y
|
||||
| none, none => none
|
||||
|
||||
instance [Min α] : Min (Option α) where min := Option.min
|
||||
|
||||
@[simp] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
|
||||
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = some a := rfl
|
||||
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = some b := rfl
|
||||
@[simp] theorem min_none_none [Min α] : min (none : Option α) none = none := rfl
|
||||
|
||||
/-- The maximum of two optional values. -/
|
||||
protected def max [Max α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Max.max x y)
|
||||
| some x, none => some x
|
||||
| none, some y => some y
|
||||
| none, none => none
|
||||
|
||||
instance [Max α] : Max (Option α) where max := Option.max
|
||||
|
||||
@[simp] theorem max_some_some [Max α] {a b : α} : max (some a) (some b) = some (max a b) := rfl
|
||||
@[simp] theorem max_some_none [Max α] {a : α} : max (some a) none = some a := rfl
|
||||
@[simp] theorem max_none_some [Max α] {b : α} : max none (some b) = some b := rfl
|
||||
@[simp] theorem max_none_none [Max α] : max (none : Option α) none = none := rfl
|
||||
|
||||
|
||||
end Option
|
||||
|
||||
instance [LT α] : LT (Option α) where
|
||||
lt := Option.lt (· < ·)
|
||||
|
||||
@@ -8,11 +8,82 @@ import Init.Data.Option.Basic
|
||||
|
||||
universe u v
|
||||
|
||||
theorem Option.eq_of_eq_some {α : Type u} : ∀ {x y : Option α}, (∀z, x = some z ↔ y = some z) → x = y
|
||||
namespace Option
|
||||
|
||||
theorem eq_of_eq_some {α : Type u} : ∀ {x y : Option α}, (∀z, x = some z ↔ y = some z) → x = y
|
||||
| none, none, _ => rfl
|
||||
| none, some z, h => Option.noConfusion ((h z).2 rfl)
|
||||
| some z, none, h => Option.noConfusion ((h z).1 rfl)
|
||||
| some _, some w, h => Option.noConfusion ((h w).2 rfl) (congrArg some)
|
||||
|
||||
theorem Option.eq_none_of_isNone {α : Type u} : ∀ {o : Option α}, o.isNone → o = none
|
||||
theorem eq_none_of_isNone {α : Type u} : ∀ {o : Option α}, o.isNone → o = none
|
||||
| none, _ => rfl
|
||||
|
||||
instance : Membership α (Option α) := ⟨fun a b => b = some a⟩
|
||||
|
||||
@[simp] theorem mem_def {a : α} {b : Option α} : a ∈ b ↔ b = some a := .rfl
|
||||
|
||||
instance [DecidableEq α] (j : α) (o : Option α) : Decidable (j ∈ o) :=
|
||||
inferInstanceAs <| Decidable (o = some j)
|
||||
|
||||
theorem isNone_iff_eq_none {o : Option α} : o.isNone ↔ o = none :=
|
||||
⟨Option.eq_none_of_isNone, fun e => e.symm ▸ rfl⟩
|
||||
|
||||
theorem some_inj {a b : α} : some a = some b ↔ a = b := by simp; rfl
|
||||
|
||||
/--
|
||||
`o = none` is decidable even if the wrapped type does not have decidable equality.
|
||||
This is not an instance because it is not definitionally equal to `instance : DecidableEq Option`.
|
||||
Try to use `o.isNone` or `o.isSome` instead.
|
||||
-/
|
||||
@[inline] def decidable_eq_none {o : Option α} : Decidable (o = none) :=
|
||||
decidable_of_decidable_of_iff isNone_iff_eq_none
|
||||
|
||||
instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (∀ a, a ∈ o → p a)
|
||||
| none => isTrue nofun
|
||||
| some a =>
|
||||
if h : p a then isTrue fun _ e => some_inj.1 e ▸ h
|
||||
else isFalse <| mt (· _ rfl) h
|
||||
|
||||
instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (Exists fun a => a ∈ o ∧ p a)
|
||||
| none => isFalse nofun
|
||||
| some a => if h : p a then isTrue ⟨_, rfl, h⟩ else isFalse fun ⟨_, ⟨rfl, hn⟩⟩ => h hn
|
||||
|
||||
/--
|
||||
Partial bind. If for some `x : Option α`, `f : Π (a : α), a ∈ x → Option β` is a
|
||||
partial function defined on `a : α` giving an `Option β`, where `some a = x`,
|
||||
then `pbind x f h` is essentially the same as `bind x f`
|
||||
but is defined only when all `x = some a`, using the proof to apply `f`.
|
||||
-/
|
||||
@[simp, inline]
|
||||
def pbind : ∀ x : Option α, (∀ a : α, a ∈ x → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a rfl
|
||||
|
||||
/--
|
||||
Partial map. If `f : Π a, p a → β` is a partial function defined on `a : α` satisfying `p`,
|
||||
then `pmap f x h` is essentially the same as `map f x` but is defined only when all members of `x`
|
||||
satisfy `p`, using the proof to apply `f`.
|
||||
-/
|
||||
@[simp, inline] def pmap {p : α → Prop} (f : ∀ a : α, p a → β) :
|
||||
∀ x : Option α, (∀ a, a ∈ x → p a) → Option β
|
||||
| none, _ => none
|
||||
| some a, H => f a (H a rfl)
|
||||
|
||||
/-- Map a monadic function which returns `Unit` over an `Option`. -/
|
||||
@[inline] protected def forM [Pure m] : Option α → (α → m PUnit) → m PUnit
|
||||
| none , _ => pure ()
|
||||
| some a, f => f a
|
||||
|
||||
instance : ForM m (Option α) α :=
|
||||
⟨Option.forM⟩
|
||||
|
||||
instance : ForIn' m (Option α) α inferInstance where
|
||||
forIn' x init f := do
|
||||
match x with
|
||||
| none => return init
|
||||
| some a =>
|
||||
match ← f a rfl init with
|
||||
| .done r | .yield r => return r
|
||||
|
||||
end Option
|
||||
|
||||
238
src/Init/Data/Option/Lemmas.lean
Normal file
238
src/Init/Data/Option/Lemmas.lean
Normal file
@@ -0,0 +1,238 @@
|
||||
/-
|
||||
Copyright (c) 2017 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Option.Instances
|
||||
import Init.Classical
|
||||
import Init.Ext
|
||||
|
||||
namespace Option
|
||||
|
||||
theorem mem_iff {a : α} {b : Option α} : a ∈ b ↔ b = a := .rfl
|
||||
|
||||
theorem some_ne_none (x : α) : some x ≠ none := nofun
|
||||
|
||||
protected theorem «forall» {p : Option α → Prop} : (∀ x, p x) ↔ p none ∧ ∀ x, p (some x) :=
|
||||
⟨fun h => ⟨h _, fun _ => h _⟩, fun h x => Option.casesOn x h.1 h.2⟩
|
||||
|
||||
protected theorem «exists» {p : Option α → Prop} :
|
||||
(∃ x, p x) ↔ p none ∨ ∃ x, p (some x) :=
|
||||
⟨fun | ⟨none, hx⟩ => .inl hx | ⟨some x, hx⟩ => .inr ⟨x, hx⟩,
|
||||
fun | .inl h => ⟨_, h⟩ | .inr ⟨_, hx⟩ => ⟨_, hx⟩⟩
|
||||
|
||||
theorem get_mem : ∀ {o : Option α} (h : isSome o), o.get h ∈ o
|
||||
| some _, _ => rfl
|
||||
|
||||
theorem get_of_mem : ∀ {o : Option α} (h : isSome o), a ∈ o → o.get h = a
|
||||
| _, _, rfl => rfl
|
||||
|
||||
theorem not_mem_none (a : α) : a ∉ (none : Option α) := nofun
|
||||
|
||||
@[simp] theorem some_get : ∀ {x : Option α} (h : isSome x), some (x.get h) = x
|
||||
| some _, _ => rfl
|
||||
|
||||
@[simp] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl
|
||||
|
||||
theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.getD y) = x := by
|
||||
cases x; {contradiction}; rw [getD_some]
|
||||
|
||||
theorem getD_eq_iff {o : Option α} {a b} : o.getD a = b ↔ (o = some b ∨ o = none ∧ a = b) := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem mem_unique {o : Option α} {a b : α} (ha : a ∈ o) (hb : b ∈ o) : a = b :=
|
||||
some.inj <| ha ▸ hb
|
||||
|
||||
@[ext] theorem ext : ∀ {o₁ o₂ : Option α}, (∀ a, a ∈ o₁ ↔ a ∈ o₂) → o₁ = o₂
|
||||
| none, none, _ => rfl
|
||||
| some _, _, H => ((H _).1 rfl).symm
|
||||
| _, some _, H => (H _).2 rfl
|
||||
|
||||
theorem eq_none_iff_forall_not_mem : o = none ↔ ∀ a, a ∉ o :=
|
||||
⟨fun e a h => by rw [e] at h; (cases h), fun h => ext <| by simp; exact h⟩
|
||||
|
||||
@[simp] theorem isSome_none : @isSome α none = false := rfl
|
||||
|
||||
@[simp] theorem isSome_some : isSome (some a) = true := rfl
|
||||
|
||||
theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> simp [isSome]
|
||||
|
||||
@[simp] theorem isNone_none : @isNone α none = true := rfl
|
||||
|
||||
@[simp] theorem isNone_some : isNone (some a) = false := rfl
|
||||
|
||||
@[simp] theorem not_isSome : isSome a = false ↔ a.isNone = true := by
|
||||
cases a <;> simp
|
||||
|
||||
theorem eq_some_iff_get_eq : o = some a ↔ ∃ h : o.isSome, o.get h = a := by
|
||||
cases o <;> simp; nofun
|
||||
|
||||
theorem eq_some_of_isSome : ∀ {o : Option α} (h : o.isSome), o = some (o.get h)
|
||||
| some _, _ => rfl
|
||||
|
||||
theorem not_isSome_iff_eq_none : ¬o.isSome ↔ o = none := by
|
||||
cases o <;> simp
|
||||
|
||||
theorem ne_none_iff_isSome : o ≠ none ↔ o.isSome := by cases o <;> simp
|
||||
|
||||
theorem ne_none_iff_exists : o ≠ none ↔ ∃ x, some x = o := by cases o <;> simp
|
||||
|
||||
theorem ne_none_iff_exists' : o ≠ none ↔ ∃ x, o = some x :=
|
||||
ne_none_iff_exists.trans <| exists_congr fun _ => eq_comm
|
||||
|
||||
theorem bex_ne_none {p : Option α → Prop} : (∃ x, ∃ (_ : x ≠ none), p x) ↔ ∃ x, p (some x) :=
|
||||
⟨fun ⟨x, hx, hp⟩ => ⟨x.get <| ne_none_iff_isSome.1 hx, by rwa [some_get]⟩,
|
||||
fun ⟨x, hx⟩ => ⟨some x, some_ne_none x, hx⟩⟩
|
||||
|
||||
theorem ball_ne_none {p : Option α → Prop} : (∀ x (_ : x ≠ none), p x) ↔ ∀ x, p (some x) :=
|
||||
⟨fun h x => h (some x) (some_ne_none x),
|
||||
fun h x hx => by
|
||||
have := h <| x.get <| ne_none_iff_isSome.1 hx
|
||||
simp [some_get] at this ⊢
|
||||
exact this⟩
|
||||
|
||||
@[simp] theorem pure_def : pure = @some α := rfl
|
||||
|
||||
@[simp] theorem bind_eq_bind : bind = @Option.bind α β := rfl
|
||||
|
||||
@[simp] theorem bind_some (x : Option α) : x.bind some = x := by cases x <;> rfl
|
||||
|
||||
@[simp] theorem bind_none (x : Option α) : x.bind (fun _ => none (α := β)) = none := by
|
||||
cases x <;> rfl
|
||||
|
||||
@[simp] theorem bind_eq_some : x.bind f = some b ↔ ∃ a, x = some a ∧ f a = some b := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem bind_eq_none {o : Option α} {f : α → Option β} :
|
||||
o.bind f = none ↔ ∀ a, o = some a → f a = none := by cases o <;> simp
|
||||
|
||||
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 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
|
||||
|
||||
theorem bind_assoc (x : Option α) (f : α → Option β) (g : β → Option γ) :
|
||||
(x.bind f).bind g = x.bind fun y => (f y).bind g := by cases x <;> rfl
|
||||
|
||||
theorem join_eq_some : x.join = some a ↔ x = some (some a) := by
|
||||
simp
|
||||
|
||||
theorem join_ne_none : x.join ≠ none ↔ ∃ z, x = some (some z) := by
|
||||
simp only [ne_none_iff_exists', join_eq_some, iff_self]
|
||||
|
||||
theorem join_ne_none' : ¬x.join = none ↔ ∃ z, x = some (some z) :=
|
||||
join_ne_none
|
||||
|
||||
theorem join_eq_none : o.join = none ↔ o = none ∨ o = some none :=
|
||||
match o with | none | some none | some (some _) => by simp
|
||||
|
||||
theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
|
||||
|
||||
@[simp] theorem map_eq_map : Functor.map f = Option.map f := rfl
|
||||
|
||||
theorem map_none : f <$> none = none := rfl
|
||||
|
||||
theorem map_some : f <$> some a = some (f a) := rfl
|
||||
|
||||
@[simp] theorem map_eq_some' : x.map f = some b ↔ ∃ a, x = some a ∧ f a = b := by cases x <;> simp
|
||||
|
||||
theorem map_eq_some : f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := map_eq_some'
|
||||
|
||||
@[simp] theorem map_eq_none' : x.map f = none ↔ x = none := by
|
||||
cases x <;> simp only [map_none', map_some', eq_self_iff_true]
|
||||
|
||||
theorem map_eq_none : f <$> x = none ↔ x = none := map_eq_none'
|
||||
|
||||
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some ∘ f) := by
|
||||
cases x <;> simp [Option.bind]
|
||||
|
||||
theorem map_congr {x : Option α} (h : ∀ a, a ∈ x → f a = g a) : x.map f = x.map g := by
|
||||
cases x <;> simp only [map_none', map_some', h, mem_def]
|
||||
|
||||
@[simp] theorem map_id' : Option.map (@id α) = id := map_id
|
||||
@[simp] theorem map_id'' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
|
||||
|
||||
@[simp] theorem map_map (h : β → γ) (g : α → β) (x : Option α) :
|
||||
(x.map g).map h = x.map (h ∘ g) := by
|
||||
cases x <;> simp only [map_none', map_some', ·∘·]
|
||||
|
||||
theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘ g) = (x.map g).map h :=
|
||||
(map_map ..).symm
|
||||
|
||||
@[simp] theorem map_comp_map (f : α → β) (g : β → γ) :
|
||||
Option.map g ∘ Option.map f = Option.map (g ∘ f) := by funext x; simp
|
||||
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some' ..
|
||||
|
||||
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α → β} :
|
||||
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
|
||||
|
||||
theorem join_map_eq_map_join {f : α → β} {x : Option (Option α)} :
|
||||
(x.map (Option.map f)).join = x.join.map f := by cases x <;> simp
|
||||
|
||||
theorem join_join {x : Option (Option (Option α))} : x.join.join = (x.map join).join := by
|
||||
cases x <;> simp
|
||||
|
||||
theorem mem_of_mem_join {a : α} {x : Option (Option α)} (h : a ∈ x.join) : some a ∈ x :=
|
||||
h.symm ▸ join_eq_some.1 h
|
||||
|
||||
@[simp] theorem some_orElse (a : α) (x : Option α) : (some a <|> x) = some a := rfl
|
||||
|
||||
@[simp] theorem none_orElse (x : Option α) : (none <|> x) = x := rfl
|
||||
|
||||
@[simp] theorem orElse_none (x : Option α) : (x <|> none) = x := by cases x <;> rfl
|
||||
|
||||
theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) := by
|
||||
cases x <;> simp
|
||||
|
||||
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b ↔ a = b ∧ p a :=
|
||||
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
|
||||
|
||||
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 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
|
||||
cases b <;> rfl
|
||||
|
||||
@[simp] theorem liftOrGet_none_right {f} {a : Option α} : liftOrGet f a none = a := by
|
||||
cases a <;> rfl
|
||||
|
||||
@[simp] theorem liftOrGet_some_some {f} {a b : α} :
|
||||
liftOrGet f (some a) (some b) = f a b := rfl
|
||||
|
||||
theorem elim_none (x : β) (f : α → β) : none.elim x f = x := rfl
|
||||
|
||||
theorem elim_some (x : β) (f : α → β) (a : α) : (some a).elim x f = f a := rfl
|
||||
|
||||
@[simp] theorem getD_map (f : α → β) (x : α) (o : Option α) :
|
||||
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
|
||||
|
||||
section
|
||||
|
||||
attribute [local instance] Classical.propDecidable
|
||||
|
||||
/-- An arbitrary `some a` with `a : α` if `α` is nonempty, and otherwise `none`. -/
|
||||
noncomputable def choice (α : Type _) : Option α :=
|
||||
if h : Nonempty α then some (Classical.choice h) else none
|
||||
|
||||
theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a := by
|
||||
simp [choice]
|
||||
rw [dif_pos (⟨a⟩ : Nonempty α)]
|
||||
simp; apply Subsingleton.elim
|
||||
|
||||
theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome ↔ Nonempty α :=
|
||||
⟨fun h => ⟨(choice α).get h⟩, fun h => by simp only [choice, dif_pos h, isSome_some]⟩
|
||||
|
||||
end
|
||||
|
||||
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
|
||||
|
||||
@[simp] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
|
||||
@@ -12,16 +12,105 @@ inductive Ordering where
|
||||
| lt | eq | gt
|
||||
deriving Inhabited, BEq
|
||||
|
||||
namespace Ordering
|
||||
|
||||
deriving instance DecidableEq for Ordering
|
||||
|
||||
/-- Swaps less and greater ordering results -/
|
||||
def swap : Ordering → Ordering
|
||||
| .lt => .gt
|
||||
| .eq => .eq
|
||||
| .gt => .lt
|
||||
|
||||
/--
|
||||
If `o₁` and `o₂` are `Ordering`, then `o₁.then o₂` returns `o₁` unless it is `.eq`,
|
||||
in which case it returns `o₂`. Additionally, it has "short-circuiting" semantics similar to
|
||||
boolean `x && y`: if `o₁` is not `.eq` then the expression for `o₂` is not evaluated.
|
||||
This is a useful primitive for constructing lexicographic comparator functions:
|
||||
```
|
||||
structure Person where
|
||||
name : String
|
||||
age : Nat
|
||||
|
||||
instance : Ord Person where
|
||||
compare a b := (compare a.name b.name).then (compare b.age a.age)
|
||||
```
|
||||
This example will sort people first by name (in ascending order) and will sort people with
|
||||
the same name by age (in descending order). (If all fields are sorted ascending and in the same
|
||||
order as they are listed in the structure, you can also use `deriving Ord` on the structure
|
||||
definition for the same effect.)
|
||||
-/
|
||||
@[macro_inline] def «then» : Ordering → Ordering → Ordering
|
||||
| .eq, f => f
|
||||
| o, _ => o
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'equal'.
|
||||
-/
|
||||
def isEq : Ordering → Bool
|
||||
| eq => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'not equal'.
|
||||
-/
|
||||
def isNe : Ordering → Bool
|
||||
| eq => false
|
||||
| _ => true
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'less than or equal to'.
|
||||
-/
|
||||
def isLE : Ordering → Bool
|
||||
| gt => false
|
||||
| _ => true
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'less than'.
|
||||
-/
|
||||
def isLT : Ordering → Bool
|
||||
| lt => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'greater than'.
|
||||
-/
|
||||
def isGT : Ordering → Bool
|
||||
| gt => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Check whether the ordering is 'greater than or equal'.
|
||||
-/
|
||||
def isGE : Ordering → Bool
|
||||
| lt => false
|
||||
| _ => true
|
||||
|
||||
end Ordering
|
||||
|
||||
@[inline] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
|
||||
if x < y then Ordering.lt
|
||||
else if x = y then Ordering.eq
|
||||
else Ordering.gt
|
||||
|
||||
/--
|
||||
Compare `a` and `b` lexicographically by `cmp₁` and `cmp₂`. `a` and `b` are
|
||||
first compared by `cmp₁`. If this returns 'equal', `a` and `b` are compared
|
||||
by `cmp₂` to break the tie.
|
||||
-/
|
||||
@[inline] def compareLex (cmp₁ cmp₂ : α → β → Ordering) (a : α) (b : β) : Ordering :=
|
||||
(cmp₁ a b).then (cmp₂ a b)
|
||||
|
||||
class Ord (α : Type u) where
|
||||
compare : α → α → Ordering
|
||||
|
||||
export Ord (compare)
|
||||
|
||||
@[inline] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
|
||||
if x < y then Ordering.lt
|
||||
else if x = y then Ordering.eq
|
||||
else Ordering.gt
|
||||
/--
|
||||
Compare `x` and `y` by comparing `f x` and `f y`.
|
||||
-/
|
||||
@[inline] def compareOn [ord : Ord β] (f : α → β) (x y : α) : Ordering :=
|
||||
compare (f x) (f y)
|
||||
|
||||
instance : Ord Nat where
|
||||
compare x y := compareOfLessAndEq x y
|
||||
@@ -71,13 +160,55 @@ def ltOfOrd [Ord α] : LT α where
|
||||
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b == Ordering.lt))
|
||||
|
||||
def Ordering.isLE : Ordering → Bool
|
||||
| Ordering.lt => true
|
||||
| Ordering.eq => true
|
||||
| Ordering.gt => false
|
||||
|
||||
def leOfOrd [Ord α] : LE α where
|
||||
le a b := (compare a b).isLE
|
||||
|
||||
instance [Ord α] : DecidableRel (@LE.le α leOfOrd) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => (compare a b).isLE))
|
||||
|
||||
namespace Ord
|
||||
|
||||
/--
|
||||
Derive a `BEq` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toBEq (ord : Ord α) : BEq α where
|
||||
beq x y := ord.compare x y == .eq
|
||||
|
||||
/--
|
||||
Derive an `LT` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toLT (_ : Ord α) : LT α :=
|
||||
ltOfOrd
|
||||
|
||||
/--
|
||||
Derive an `LE` instance from an `Ord` instance.
|
||||
-/
|
||||
protected def toLE (_ : Ord α) : LE α :=
|
||||
leOfOrd
|
||||
|
||||
/--
|
||||
Invert the order of an `Ord` instance.
|
||||
-/
|
||||
protected def opposite (ord : Ord α) : Ord α where
|
||||
compare x y := ord.compare y x
|
||||
|
||||
/--
|
||||
`ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
|
||||
-/
|
||||
protected def on (ord : Ord β) (f : α → β) : Ord α where
|
||||
compare := compareOn f
|
||||
|
||||
/--
|
||||
Derive the lexicographic order on products `α × β` from orders for `α` and `β`.
|
||||
-/
|
||||
protected def lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
|
||||
lexOrd
|
||||
|
||||
/--
|
||||
Create an order which compares elements first by `ord₁` and then, if this
|
||||
returns 'equal', by `ord₂`.
|
||||
-/
|
||||
protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
|
||||
compare := compareLex ord₁.compare ord₂.compare
|
||||
|
||||
end Ord
|
||||
|
||||
@@ -42,17 +42,15 @@ instance : Repr StdGen where
|
||||
|
||||
def stdNext : StdGen → Nat × StdGen
|
||||
| ⟨s1, s2⟩ =>
|
||||
let s1 : Int := s1
|
||||
let s2 : Int := s2
|
||||
let k : Int := s1 / 53668
|
||||
let s1' : Int := 40014 * ((s1 : Int) - k * 53668) - k * 12211
|
||||
let s1'' : Int := if s1' < 0 then s1' + 2147483563 else s1'
|
||||
let k' : Int := s2 / 52774
|
||||
let s2' : Int := 40692 * ((s2 : Int) - k' * 52774) - k' * 3791
|
||||
let s2'' : Int := if s2' < 0 then s2' + 2147483399 else s2'
|
||||
let z : Int := s1'' - s2''
|
||||
let z' : Int := if z < 1 then z + 2147483562 else z % 2147483562
|
||||
(z'.toNat, ⟨s1''.toNat, s2''.toNat⟩)
|
||||
let k : Int := Int.ofNat (s1 / 53668)
|
||||
let s1' : Int := 40014 * (Int.ofNat s1 - k * 53668) - k * 12211
|
||||
let s1'' : Nat := if s1' < 0 then (s1' + 2147483563).toNat else s1'.toNat
|
||||
let k' : Int := Int.ofNat (s2 / 52774)
|
||||
let s2' : Int := 40692 * (Int.ofNat s2 - k' * 52774) - k' * 3791
|
||||
let s2'' : Nat := if s2' < 0 then (s2' + 2147483399).toNat else s2'.toNat
|
||||
let z : Int := Int.ofNat s1'' - Int.ofNat s2''
|
||||
let z' : Nat := if z < 1 then (z + 2147483562).toNat else z.toNat % 2147483562
|
||||
(z', ⟨s1'', s2''⟩)
|
||||
|
||||
def stdSplit : StdGen → StdGen × StdGen
|
||||
| g@⟨s1, s2⟩ =>
|
||||
|
||||
@@ -290,17 +290,40 @@ where go (acc : String) (s : String) : List String → String
|
||||
| a :: as => go (acc ++ s ++ a) s as
|
||||
| [] => acc
|
||||
|
||||
/-- Iterator for `String`. That is, a `String` and a position in that string. -/
|
||||
/-- Iterator over the characters (`Char`) of a `String`.
|
||||
|
||||
Typically created by `s.iter`, where `s` is a `String`.
|
||||
|
||||
An iterator is *valid* if the position `i` is *valid* for the string `s`, meaning `0 ≤ i ≤ s.endPos`
|
||||
and `i` lies on a UTF8 byte boundary. If `i = s.endPos`, the iterator is at the end of the string.
|
||||
|
||||
Most operations on iterators return arbitrary values if the iterator is not valid. The functions in
|
||||
the `String.Iterator` API should rule out the creation of invalid iterators, with two exceptions:
|
||||
|
||||
- `Iterator.next iter` is invalid if `iter` is already at the end of the string (`iter.atEnd` is
|
||||
`true`), and
|
||||
- `Iterator.forward iter n`/`Iterator.nextn iter n` is invalid if `n` is strictly greater than the
|
||||
number of remaining characters.
|
||||
-/
|
||||
structure Iterator where
|
||||
/-- The string the iterator is for. -/
|
||||
s : String
|
||||
/-- The current position.
|
||||
|
||||
This position is not necessarily valid for the string, for instance if one keeps calling
|
||||
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
|
||||
current character is `(default : Char)`, similar to `String.get` on an invalid position. -/
|
||||
i : Pos
|
||||
deriving DecidableEq
|
||||
|
||||
/-- Creates an iterator at the beginning of a string. -/
|
||||
def mkIterator (s : String) : Iterator :=
|
||||
⟨s, 0⟩
|
||||
|
||||
@[inherit_doc mkIterator]
|
||||
abbrev iter := mkIterator
|
||||
|
||||
/-- The size of a string iterator is the number of bytes remaining. -/
|
||||
instance : SizeOf String.Iterator where
|
||||
sizeOf i := i.1.utf8ByteSize - i.2.byteIdx
|
||||
|
||||
@@ -308,55 +331,90 @@ theorem Iterator.sizeOf_eq (i : String.Iterator) : sizeOf i = i.1.utf8ByteSize -
|
||||
rfl
|
||||
|
||||
namespace Iterator
|
||||
def toString : Iterator → String
|
||||
| ⟨s, _⟩ => s
|
||||
@[inherit_doc Iterator.s]
|
||||
def toString := Iterator.s
|
||||
|
||||
/-- Number of bytes remaining in the iterator. -/
|
||||
def remainingBytes : Iterator → Nat
|
||||
| ⟨s, i⟩ => s.endPos.byteIdx - i.byteIdx
|
||||
|
||||
def pos : Iterator → Pos
|
||||
| ⟨_, i⟩ => i
|
||||
@[inherit_doc Iterator.i]
|
||||
def pos := Iterator.i
|
||||
|
||||
/-- The character at the current position.
|
||||
|
||||
On an invalid position, returns `(default : Char)`. -/
|
||||
def curr : Iterator → Char
|
||||
| ⟨s, i⟩ => get s i
|
||||
|
||||
/-- Moves the iterator's position forward by one character, unconditionally.
|
||||
|
||||
It is only valid to call this function if the iterator is not at the end of the string, *i.e.*
|
||||
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
|
||||
def next : Iterator → Iterator
|
||||
| ⟨s, i⟩ => ⟨s, s.next i⟩
|
||||
|
||||
/-- Decreases the iterator's position.
|
||||
|
||||
If the position is zero, this function is the identity. -/
|
||||
def prev : Iterator → Iterator
|
||||
| ⟨s, i⟩ => ⟨s, s.prev i⟩
|
||||
|
||||
/-- True if the iterator is past the string's last character. -/
|
||||
def atEnd : Iterator → Bool
|
||||
| ⟨s, i⟩ => i.byteIdx ≥ s.endPos.byteIdx
|
||||
|
||||
/-- True if the iterator is not past the string's last character. -/
|
||||
def hasNext : Iterator → Bool
|
||||
| ⟨s, i⟩ => i.byteIdx < s.endPos.byteIdx
|
||||
|
||||
/-- True if the position is not zero. -/
|
||||
def hasPrev : Iterator → Bool
|
||||
| ⟨_, i⟩ => i.byteIdx > 0
|
||||
|
||||
/-- Replaces the current character in the string.
|
||||
|
||||
Does nothing if the iterator is at the end of the string. If the iterator contains the only
|
||||
reference to its string, this function will mutate the string in-place instead of allocating a new
|
||||
one. -/
|
||||
def setCurr : Iterator → Char → Iterator
|
||||
| ⟨s, i⟩, c => ⟨s.set i c, i⟩
|
||||
|
||||
/-- Moves the iterator's position to the end of the string.
|
||||
|
||||
Note that `i.toEnd.atEnd` is always `true`. -/
|
||||
def toEnd : Iterator → Iterator
|
||||
| ⟨s, _⟩ => ⟨s, s.endPos⟩
|
||||
|
||||
/-- Extracts the substring between the positions of two iterators.
|
||||
|
||||
Returns the empty string if the iterators are for different strings, or if the position of the first
|
||||
iterator is past the position of the second iterator. -/
|
||||
def extract : Iterator → Iterator → String
|
||||
| ⟨s₁, b⟩, ⟨s₂, e⟩ =>
|
||||
if s₁ ≠ s₂ || b > e then ""
|
||||
else s₁.extract b e
|
||||
|
||||
/-- Moves the iterator's position several characters forward.
|
||||
|
||||
The resulting iterator is only valid if the number of characters to skip is less than or equal to
|
||||
the number of characters left in the iterator. -/
|
||||
def forward : Iterator → Nat → Iterator
|
||||
| it, 0 => it
|
||||
| it, n+1 => forward it.next n
|
||||
|
||||
/-- The remaining characters in an iterator, as a string. -/
|
||||
def remainingToString : Iterator → String
|
||||
| ⟨s, i⟩ => s.extract i s.endPos
|
||||
|
||||
@[inherit_doc forward]
|
||||
def nextn : Iterator → Nat → Iterator
|
||||
| it, 0 => it
|
||||
| it, i+1 => nextn it.next i
|
||||
|
||||
/-- Moves the iterator's position several characters back.
|
||||
|
||||
If asked to go back more characters than available, stops at the beginning of the string. -/
|
||||
def prevn : Iterator → Nat → Iterator
|
||||
| it, 0 => it
|
||||
| it, i+1 => prevn it.prev i
|
||||
|
||||
@@ -4,12 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Control.Except
|
||||
import Init.Data.ByteArray
|
||||
import Init.SimpLemmas
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Util
|
||||
import Init.WFTactics
|
||||
|
||||
namespace String
|
||||
|
||||
|
||||
24
src/Init/Data/Sum.lean
Normal file
24
src/Init/Data/Sum.lean
Normal file
@@ -0,0 +1,24 @@
|
||||
/-
|
||||
Copyright (c) 2017 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro, Yury G. Kudryashov
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
|
||||
namespace Sum
|
||||
|
||||
deriving instance DecidableEq for Sum
|
||||
deriving instance BEq for Sum
|
||||
|
||||
/-- Check if a sum is `inl` and if so, retrieve its contents. -/
|
||||
def getLeft? : α ⊕ β → Option α
|
||||
| inl a => some a
|
||||
| inr _ => none
|
||||
|
||||
/-- Check if a sum is `inr` and if so, retrieve its contents. -/
|
||||
def getRight? : α ⊕ β → Option β
|
||||
| inr b => some b
|
||||
| inl _ => none
|
||||
|
||||
end Sum
|
||||
113
src/Init/Ext.lean
Normal file
113
src/Init/Ext.lean
Normal file
@@ -0,0 +1,113 @@
|
||||
/-
|
||||
Copyright (c) 2021 Gabriel Ebner. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Gabriel Ebner, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.TacticsExtra
|
||||
import Init.RCases
|
||||
|
||||
namespace Lean
|
||||
namespace Parser.Attr
|
||||
/-- Registers an extensionality theorem.
|
||||
|
||||
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
|
||||
them for the `ext` tactic.
|
||||
|
||||
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic.
|
||||
|
||||
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
|
||||
|
||||
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
|
||||
rather than flattening the parents' fields into the lemma's equality hypotheses.
|
||||
structures in the generated extensionality theorems. -/
|
||||
syntax (name := ext) "ext" (" (" &"flat" " := " term ")")? (ppSpace prio)? : attr
|
||||
end Parser.Attr
|
||||
|
||||
-- TODO: rename this namespace?
|
||||
-- Remark: `ext` has scoped syntax, Mathlib may depend on the actual namespace name.
|
||||
namespace Elab.Tactic.Ext
|
||||
/--
|
||||
Creates the type of the extensionality theorem for the given structure,
|
||||
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
|
||||
-/
|
||||
scoped syntax (name := extType) "ext_type% " term:max ppSpace ident : term
|
||||
|
||||
/--
|
||||
Creates the type of the iff-variant of the extensionality theorem for the given structure,
|
||||
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
|
||||
-/
|
||||
scoped syntax (name := extIffType) "ext_iff_type% " term:max ppSpace ident : term
|
||||
|
||||
/--
|
||||
`declare_ext_theorems_for A` declares the extensionality theorems for the structure `A`.
|
||||
|
||||
These theorems state that two expressions with the structure type are equal if their fields are equal.
|
||||
-/
|
||||
syntax (name := declareExtTheoremFor) "declare_ext_theorems_for " ("(" &"flat" " := " term ") ")? ident (ppSpace prio)? : command
|
||||
|
||||
macro_rules | `(declare_ext_theorems_for $[(flat := $f)]? $struct:ident $(prio)?) => do
|
||||
let flat := f.getD (mkIdent `true)
|
||||
let names ← Macro.resolveGlobalName struct.getId.eraseMacroScopes
|
||||
let name ← match names.filter (·.2.isEmpty) with
|
||||
| [] => Macro.throwError s!"unknown constant {struct.getId}"
|
||||
| [(name, _)] => pure name
|
||||
| _ => Macro.throwError s!"ambiguous name {struct.getId}"
|
||||
let extName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext"
|
||||
let extIffName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext_iff"
|
||||
`(@[ext $(prio)?] protected theorem $extName:ident : ext_type% $flat $struct:ident :=
|
||||
fun {..} {..} => by intros; subst_eqs; rfl
|
||||
protected theorem $extIffName:ident : ext_iff_type% $flat $struct:ident :=
|
||||
fun {..} {..} =>
|
||||
⟨fun h => by cases h; and_intros <;> rfl,
|
||||
fun _ => by (repeat cases ‹_ ∧ _›); subst_eqs; rfl⟩)
|
||||
|
||||
/--
|
||||
Applies extensionality lemmas that are registered with the `@[ext]` attribute.
|
||||
* `ext pat*` applies extensionality theorems as much as possible,
|
||||
using the patterns `pat*` to introduce the variables in extensionality theorems using `rintro`.
|
||||
For example, the patterns are used to name the variables introduced by lemmas such as `funext`.
|
||||
* Without patterns,`ext` applies extensionality lemmas as much
|
||||
as possible but introduces anonymous hypotheses whenever needed.
|
||||
* `ext pat* : n` applies ext theorems only up to depth `n`.
|
||||
|
||||
The `ext1 pat*` tactic is like `ext pat*` except that it only applies a single extensionality theorem.
|
||||
|
||||
Unused patterns will generate warning.
|
||||
Patterns that don't match the variables will typically result in the introduction of anonymous hypotheses.
|
||||
-/
|
||||
syntax (name := ext) "ext" (colGt ppSpace rintroPat)* (" : " num)? : tactic
|
||||
|
||||
/-- Apply a single extensionality theorem to the current goal. -/
|
||||
syntax (name := applyExtTheorem) "apply_ext_theorem" : tactic
|
||||
|
||||
/--
|
||||
`ext1 pat*` is like `ext pat*` except that it only applies a single extensionality theorem rather
|
||||
than recursively applying as many extensionality theorems as possible.
|
||||
|
||||
The `pat*` patterns are processed using the `rintro` tactic.
|
||||
If no patterns are supplied, then variables are introduced anonymously using the `intros` tactic.
|
||||
-/
|
||||
macro "ext1" xs:(colGt ppSpace rintroPat)* : tactic =>
|
||||
if xs.isEmpty then `(tactic| apply_ext_theorem <;> intros)
|
||||
else `(tactic| apply_ext_theorem <;> rintro $xs*)
|
||||
|
||||
end Elab.Tactic.Ext
|
||||
end Lean
|
||||
|
||||
attribute [ext] funext propext Subtype.eq
|
||||
|
||||
@[ext] theorem Prod.ext : {x y : Prod α β} → x.fst = y.fst → x.snd = y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, rfl => rfl
|
||||
|
||||
@[ext] theorem PProd.ext : {x y : PProd α β} → x.fst = y.fst → x.snd = y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, rfl => rfl
|
||||
|
||||
@[ext] theorem Sigma.ext : {x y : Sigma β} → x.fst = y.fst → HEq x.snd y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, .rfl => rfl
|
||||
|
||||
@[ext] theorem PSigma.ext : {x y : PSigma β} → x.fst = y.fst → HEq x.snd y.snd → x = y
|
||||
| ⟨_,_⟩, ⟨_,_⟩, rfl, .rfl => rfl
|
||||
|
||||
@[ext] protected theorem PUnit.ext (x y : PUnit) : x = y := rfl
|
||||
protected theorem Unit.ext (x y : Unit) : x = y := rfl
|
||||
@@ -9,6 +9,7 @@ prelude
|
||||
import Init.MetaTypes
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Option.BasicAux
|
||||
import Init.Data.String.Extra
|
||||
|
||||
namespace Lean
|
||||
|
||||
@@ -105,6 +106,42 @@ def idEndEscape := '»'
|
||||
def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
|
||||
def isIdEndEscape (c : Char) : Bool := c = idEndEscape
|
||||
|
||||
private def findLeadingSpacesSize (s : String) : Nat :=
|
||||
let it := s.iter
|
||||
let it := it.find (· == '\n') |>.next
|
||||
consumeSpaces it 0 s.length
|
||||
where
|
||||
consumeSpaces (it : String.Iterator) (curr min : Nat) : Nat :=
|
||||
if it.atEnd then min
|
||||
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces it.next (curr + 1) min
|
||||
else if it.curr == '\n' then findNextLine it.next min
|
||||
else findNextLine it.next (Nat.min curr min)
|
||||
findNextLine (it : String.Iterator) (min : Nat) : Nat :=
|
||||
if it.atEnd then min
|
||||
else if it.curr == '\n' then consumeSpaces it.next 0 min
|
||||
else findNextLine it.next min
|
||||
|
||||
private def removeNumLeadingSpaces (n : Nat) (s : String) : String :=
|
||||
consumeSpaces n s.iter ""
|
||||
where
|
||||
consumeSpaces (n : Nat) (it : String.Iterator) (r : String) : String :=
|
||||
match n with
|
||||
| 0 => saveLine it r
|
||||
| n+1 =>
|
||||
if it.atEnd then r
|
||||
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces n it.next r
|
||||
else saveLine it r
|
||||
termination_by (it, 1)
|
||||
saveLine (it : String.Iterator) (r : String) : String :=
|
||||
if it.atEnd then r
|
||||
else if it.curr == '\n' then consumeSpaces n it.next (r.push '\n')
|
||||
else saveLine it.next (r.push it.curr)
|
||||
termination_by (it, 0)
|
||||
|
||||
def removeLeadingSpaces (s : String) : String :=
|
||||
let n := findLeadingSpacesSize s
|
||||
if n == 0 then s else removeNumLeadingSpaces n s
|
||||
|
||||
namespace Name
|
||||
|
||||
def getRoot : Name → Name
|
||||
@@ -587,6 +624,9 @@ def mkLit (kind : SyntaxNodeKind) (val : String) (info := SourceInfo.none) : TSy
|
||||
let atom : Syntax := Syntax.atom info val
|
||||
mkNode kind #[atom]
|
||||
|
||||
def mkCharLit (val : Char) (info := SourceInfo.none) : CharLit :=
|
||||
mkLit charLitKind (Char.quote val) info
|
||||
|
||||
def mkStrLit (val : String) (info := SourceInfo.none) : StrLit :=
|
||||
mkLit strLitKind (String.quote val) info
|
||||
|
||||
@@ -1004,6 +1044,7 @@ instance [Quote α k] [CoeHTCT (TSyntax k) (TSyntax [k'])] : Quote α k' := ⟨f
|
||||
|
||||
instance : Quote Term := ⟨id⟩
|
||||
instance : Quote Bool := ⟨fun | true => mkCIdent ``Bool.true | false => mkCIdent ``Bool.false⟩
|
||||
instance : Quote Char charLitKind := ⟨Syntax.mkCharLit⟩
|
||||
instance : Quote String strLitKind := ⟨Syntax.mkStrLit⟩
|
||||
instance : Quote Nat numLitKind := ⟨fun n => Syntax.mkNumLit <| toString n⟩
|
||||
instance : Quote Substring := ⟨fun s => Syntax.mkCApp ``String.toSubstring' #[quote s.toString]⟩
|
||||
@@ -1257,6 +1298,11 @@ def expandInterpolatedStr (interpStr : TSyntax interpolatedStrKind) (type : Term
|
||||
let r ← expandInterpolatedStrChunks interpStr.raw.getArgs (fun a b => `($a ++ $b)) (fun a => `($toTypeFn $a))
|
||||
`(($r : $type))
|
||||
|
||||
def getDocString (stx : TSyntax `Lean.Parser.Command.docComment) : String :=
|
||||
match stx.raw[1] with
|
||||
| Syntax.atom _ val => val.extract 0 (val.endPos - ⟨2⟩)
|
||||
| _ => ""
|
||||
|
||||
end TSyntax
|
||||
|
||||
namespace Meta
|
||||
@@ -1281,9 +1327,59 @@ structure Config where
|
||||
|
||||
end Rewrite
|
||||
|
||||
namespace Omega
|
||||
|
||||
/-- Configures the behaviour of the `omega` tactic. -/
|
||||
structure OmegaConfig where
|
||||
/--
|
||||
Split disjunctions in the context.
|
||||
|
||||
Note that with `splitDisjunctions := false` omega will not be able to solve `x = y` goals
|
||||
as these are usually handled by introducing `¬ x = y` as a hypothesis, then replacing this with
|
||||
`x < y ∨ x > y`.
|
||||
|
||||
On the other hand, `omega` does not currently detect disjunctions which, when split,
|
||||
introduce no new useful information, so the presence of irrelevant disjunctions in the context
|
||||
can significantly increase run time.
|
||||
-/
|
||||
splitDisjunctions : Bool := true
|
||||
/--
|
||||
Whenever `((a - b : Nat) : Int)` is found, register the disjunction
|
||||
`b ≤ a ∧ ((a - b : Nat) : Int) = a - b ∨ a < b ∧ ((a - b : Nat) : Int) = 0`
|
||||
for later splitting.
|
||||
-/
|
||||
splitNatSub : Bool := true
|
||||
/--
|
||||
Whenever `Int.natAbs a` is found, register the disjunction
|
||||
`0 ≤ a ∧ Int.natAbs a = a ∨ a < 0 ∧ Int.natAbs a = - a` for later splitting.
|
||||
-/
|
||||
splitNatAbs : Bool := true
|
||||
/--
|
||||
Whenever `min a b` or `max a b` is found, rewrite in terms of the definition
|
||||
`if a ≤ b ...`, for later case splitting.
|
||||
-/
|
||||
splitMinMax : Bool := true
|
||||
|
||||
end Omega
|
||||
|
||||
namespace CheckTactic
|
||||
|
||||
/--
|
||||
Type used to lift an arbitrary value into a type parameter so it can
|
||||
appear in a proof goal.
|
||||
|
||||
It is used by the #check_tactic command.
|
||||
-/
|
||||
inductive CheckGoalType {α : Sort u} : (val : α) → Prop where
|
||||
| intro : (val : α) → CheckGoalType val
|
||||
|
||||
end CheckTactic
|
||||
|
||||
end Meta
|
||||
|
||||
namespace Parser.Tactic
|
||||
namespace Parser
|
||||
|
||||
namespace Tactic
|
||||
|
||||
/-- `erw [rules]` is a shorthand for `rw (config := { transparency := .default }) [rules]`.
|
||||
This does rewriting up to unfolding of regular definitions (by comparison to regular `rw`
|
||||
@@ -1344,6 +1440,8 @@ This will rewrite with all equation lemmas, which can be used to
|
||||
partially evaluate many definitions. -/
|
||||
declare_simp_like_tactic (dsimp := true) dsimpAutoUnfold "dsimp! " fun (c : Lean.Meta.DSimp.Config) => { c with autoUnfold := true }
|
||||
|
||||
end Parser.Tactic
|
||||
end Tactic
|
||||
|
||||
end Parser
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -43,6 +43,7 @@ inductive EtaStructMode where
|
||||
namespace DSimp
|
||||
|
||||
structure Config where
|
||||
/-- `let x := v; e[x]` reduces to `e[v]`. -/
|
||||
zeta : Bool := true
|
||||
beta : Bool := true
|
||||
eta : Bool := true
|
||||
@@ -57,6 +58,8 @@ structure Config where
|
||||
/-- If `unfoldPartialApp := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded. -/
|
||||
unfoldPartialApp : Bool := false
|
||||
/-- Given a local context containing entry `x : t := e`, free variable `x` reduces to `e`. -/
|
||||
zetaDelta : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
end DSimp
|
||||
@@ -71,6 +74,7 @@ structure Config where
|
||||
contextual : Bool := false
|
||||
memoize : Bool := true
|
||||
singlePass : Bool := false
|
||||
/-- `let x := v; e[x]` reduces to `e[v]`. -/
|
||||
zeta : Bool := true
|
||||
beta : Bool := true
|
||||
eta : Bool := true
|
||||
@@ -95,6 +99,8 @@ structure Config where
|
||||
/-- If `unfoldPartialApp := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded. -/
|
||||
unfoldPartialApp : Bool := false
|
||||
/-- Given a local context containing entry `x : t := e`, free variable `x` reduces to `e`. -/
|
||||
zetaDelta : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
-- Configuration object for `simp_all`
|
||||
@@ -111,6 +117,7 @@ def neutralConfig : Simp.Config := {
|
||||
arith := false
|
||||
autoUnfold := false
|
||||
ground := false
|
||||
zetaDelta := false
|
||||
}
|
||||
|
||||
end Simp
|
||||
|
||||
@@ -464,6 +464,14 @@ macro "without_expected_type " x:term : term => `(let aux := $x; aux)
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
* The `by_elab doSeq` expression runs the `doSeq` as a `TermElabM Expr` to
|
||||
synthesize the expression.
|
||||
* `by_elab fun expectedType? => do doSeq` receives the expected type (an `Option Expr`)
|
||||
as well.
|
||||
-/
|
||||
syntax (name := byElab) "by_elab " doSeq : term
|
||||
|
||||
/--
|
||||
Category for carrying raw syntax trees between macros; any content is printed as is by the pretty printer.
|
||||
The only accepted parser for this category is an antiquotation.
|
||||
@@ -476,6 +484,9 @@ instance : Coe Syntax (TSyntax `rawStx) where
|
||||
/-- `with_annotate_term stx e` annotates the lexical range of `stx : Syntax` with term info for `e`. -/
|
||||
scoped syntax (name := withAnnotateTerm) "with_annotate_term " rawStx ppSpace term : term
|
||||
|
||||
/-- Normalize casts in an expression using the same method as the `norm_cast` tactic. -/
|
||||
syntax (name := modCast) "mod_cast " term : term
|
||||
|
||||
/--
|
||||
The attribute `@[deprecated]` on a declaration indicates that the declaration
|
||||
is discouraged for use in new code, and/or should be migrated away from in
|
||||
@@ -492,9 +503,140 @@ applications of this function as `↑` when printing expressions.
|
||||
-/
|
||||
syntax (name := Attr.coe) "coe" : attr
|
||||
|
||||
/--
|
||||
This attribute marks a code action, which is used to suggest new tactics or replace existing ones.
|
||||
|
||||
* `@[command_code_action kind]`: This is a code action which applies to applications of the command
|
||||
`kind` (a command syntax kind), which can replace the command or insert things before or after it.
|
||||
|
||||
* `@[command_code_action kind₁ kind₂]`: shorthand for
|
||||
`@[command_code_action kind₁, command_code_action kind₂]`.
|
||||
|
||||
* `@[command_code_action]`: This is a command code action that applies to all commands.
|
||||
Use sparingly.
|
||||
-/
|
||||
syntax (name := command_code_action) "command_code_action" (ppSpace ident)* : attr
|
||||
|
||||
/--
|
||||
Builtin command code action. See `command_code_action`.
|
||||
-/
|
||||
syntax (name := builtin_command_code_action) "builtin_command_code_action" (ppSpace ident)* : attr
|
||||
|
||||
/--
|
||||
When `parent_dir` contains the current Lean file, `include_str "path" / "to" / "file"` becomes
|
||||
a string literal with the contents of the file at `"parent_dir" / "path" / "to" / "file"`. If this
|
||||
file cannot be read, elaboration fails.
|
||||
-/
|
||||
syntax (name := includeStr) "include_str " term : term
|
||||
|
||||
/--
|
||||
The `run_cmd doSeq` command executes code in `CommandElabM Unit`.
|
||||
This is almost the same as `#eval show CommandElabM Unit from do doSeq`,
|
||||
except that it doesn't print an empty diagnostic.
|
||||
-/
|
||||
syntax (name := runCmd) "run_cmd " doSeq : command
|
||||
|
||||
/--
|
||||
The `run_elab doSeq` command executes code in `TermElabM Unit`.
|
||||
This is almost the same as `#eval show TermElabM Unit from do doSeq`,
|
||||
except that it doesn't print an empty diagnostic.
|
||||
-/
|
||||
syntax (name := runElab) "run_elab " doSeq : command
|
||||
|
||||
/--
|
||||
The `run_meta doSeq` command executes code in `MetaM Unit`.
|
||||
This is almost the same as `#eval show MetaM Unit from do doSeq`,
|
||||
except that it doesn't print an empty diagnostic.
|
||||
|
||||
(This is effectively a synonym for `run_elab`.)
|
||||
-/
|
||||
syntax (name := runMeta) "run_meta " doSeq : command
|
||||
|
||||
/-- Element that can be part of a `#guard_msgs` specification. -/
|
||||
syntax guardMsgsSpecElt := &"drop"? (&"info" <|> &"warning" <|> &"error" <|> &"all")
|
||||
|
||||
/-- Specification for `#guard_msgs` command. -/
|
||||
syntax guardMsgsSpec := "(" guardMsgsSpecElt,* ")"
|
||||
|
||||
/--
|
||||
`#guard_msgs` captures the messages generated by another command and checks that they
|
||||
match the contents of the docstring attached to the `#guard_msgs` command.
|
||||
|
||||
Basic example:
|
||||
```lean
|
||||
/--
|
||||
error: unknown identifier 'x'
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : α := x
|
||||
```
|
||||
This checks that there is such an error and then consumes the message entirely.
|
||||
|
||||
By default, the command intercepts all messages, but there is a way to specify which types
|
||||
of messages to consider. For example, we can select only warnings:
|
||||
```lean
|
||||
/--
|
||||
warning: declaration uses 'sorry'
|
||||
-/
|
||||
#guard_msgs(warning) in
|
||||
example : α := sorry
|
||||
```
|
||||
or only errors
|
||||
```lean
|
||||
#guard_msgs(error) in
|
||||
example : α := sorry
|
||||
```
|
||||
In this last example, since the message is not intercepted there is a warning on `sorry`.
|
||||
We can drop the warning completely with
|
||||
```lean
|
||||
#guard_msgs(error, drop warning) in
|
||||
example : α := sorry
|
||||
```
|
||||
|
||||
Syntax description:
|
||||
```
|
||||
#guard_msgs (drop? info|warning|error|all,*)? in cmd
|
||||
```
|
||||
|
||||
If there is no specification, `#guard_msgs` intercepts all messages.
|
||||
Otherwise, if there is one, the specification is considered in left-to-right order, and the first
|
||||
that applies chooses the outcome of the message:
|
||||
- `info`, `warning`, `error`: intercept a message with the given severity level.
|
||||
- `all`: intercept any message (so `#guard_msgs in cmd` and `#guard_msgs (all) in cmd`
|
||||
are equivalent).
|
||||
- `drop info`, `drop warning`, `drop error`: intercept a message with the given severity
|
||||
level and then drop it. These messages are not checked.
|
||||
- `drop all`: intercept a message and drop it.
|
||||
|
||||
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and then drop
|
||||
everything else.
|
||||
-/
|
||||
syntax (name := guardMsgsCmd)
|
||||
(docComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
|
||||
|
||||
namespace Parser
|
||||
|
||||
/--
|
||||
`#check_tactic t ~> r by commands` runs the tactic sequence `commands`
|
||||
on a goal with `t` and sees if the resulting expression has reduced it
|
||||
to `r`.
|
||||
-/
|
||||
syntax (name := checkTactic) "#check_tactic " term "~>" term "by" tactic : command
|
||||
|
||||
/--
|
||||
`#check_tactic_failure t by tac` runs the tactic `tac`
|
||||
on a goal with `t` and verifies it fails.
|
||||
-/
|
||||
syntax (name := checkTacticFailure) "#check_tactic_failure " term "by" tactic : command
|
||||
|
||||
/--
|
||||
`#check_simp t ~> r` checks `simp` reduces `t` to `r`.
|
||||
-/
|
||||
syntax (name := checkSimp) "#check_simp " term "~>" term : command
|
||||
|
||||
/--
|
||||
`#check_simp t !~>` checks `simp` fails on reducing `t`.
|
||||
-/
|
||||
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
|
||||
|
||||
end Parser
|
||||
|
||||
@@ -170,20 +170,6 @@ See [Theorem Proving in Lean 4][tpil4] for more information.
|
||||
-/
|
||||
syntax (name := calcTactic) "calc" calcSteps : tactic
|
||||
|
||||
/--
|
||||
Denotes a term that was omitted by the pretty printer.
|
||||
This is only used for pretty printing, and it cannot be elaborated.
|
||||
The presence of `⋯` is controlled by the `pp.deepTerms` and `pp.deepTerms.threshold`
|
||||
options.
|
||||
-/
|
||||
syntax "⋯" : term
|
||||
|
||||
macro_rules | `(⋯) => Macro.throwError "\
|
||||
Error: The '⋯' token is used by the pretty printer to indicate omitted terms, \
|
||||
and it cannot be elaborated. \
|
||||
Its presence in pretty printing output is controlled by the 'pp.deepTerms' and \
|
||||
`pp.deepTerms.threshold` options."
|
||||
|
||||
@[app_unexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_)) => `(())
|
||||
|
||||
@@ -391,6 +377,23 @@ macro_rules
|
||||
`($mods:declModifiers class $id $params* extends $parents,* $[: $ty]?
|
||||
attribute [instance] $ctor)
|
||||
|
||||
macro_rules
|
||||
| `(haveI $hy:hygieneInfo $bs* $[: $ty]? := $val; $body) =>
|
||||
`(haveI $(HygieneInfo.mkIdent hy `this (canonical := true)) $bs* $[: $ty]? := $val; $body)
|
||||
| `(haveI _ $bs* := $val; $body) => `(haveI x $bs* : _ := $val; $body)
|
||||
| `(haveI _ $bs* : $ty := $val; $body) => `(haveI x $bs* : $ty := $val; $body)
|
||||
| `(haveI $x:ident $bs* := $val; $body) => `(haveI $x $bs* : _ := $val; $body)
|
||||
| `(haveI $_:ident $_* : $_ := $_; $_) => Lean.Macro.throwUnsupported -- handled by elab
|
||||
|
||||
macro_rules
|
||||
| `(letI $hy:hygieneInfo $bs* $[: $ty]? := $val; $body) =>
|
||||
`(letI $(HygieneInfo.mkIdent hy `this (canonical := true)) $bs* $[: $ty]? := $val; $body)
|
||||
| `(letI _ $bs* := $val; $body) => `(letI x $bs* : _ := $val; $body)
|
||||
| `(letI _ $bs* : $ty := $val; $body) => `(letI x $bs* : $ty := $val; $body)
|
||||
| `(letI $x:ident $bs* := $val; $body) => `(letI $x $bs* : _ := $val; $body)
|
||||
| `(letI $_:ident $_* : $_ := $_; $_) => Lean.Macro.throwUnsupported -- handled by elab
|
||||
|
||||
|
||||
syntax cdotTk := patternIgnore("· " <|> ". ")
|
||||
/-- `· tac` focuses on the main goal and tries to solve it using `tac`, or else fails. -/
|
||||
syntax (name := cdot) cdotTk tacticSeqIndentGt : tactic
|
||||
@@ -444,3 +447,25 @@ macro:50 e:term:51 " matches " p:sepBy1(term:51, " | ") : term =>
|
||||
`(((match $e:term with | $[$p:term]|* => true | _ => false) : Bool))
|
||||
|
||||
end Lean
|
||||
|
||||
syntax "{" term,+ "}" : term
|
||||
|
||||
macro_rules
|
||||
| `({$x:term}) => `(singleton $x)
|
||||
| `({$x:term, $xs:term,*}) => `(insert $x {$xs:term,*})
|
||||
|
||||
namespace Lean
|
||||
|
||||
/-- Unexpander for the `{ x }` notation. -/
|
||||
@[app_unexpander singleton]
|
||||
def singletonUnexpander : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $a) => `({ $a:term })
|
||||
| _ => throw ()
|
||||
|
||||
/-- Unexpander for the `{ x, y, ... }` notation. -/
|
||||
@[app_unexpander insert]
|
||||
def insertUnexpander : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $a { $ts:term,* }) => `({$a:term, $ts,*})
|
||||
| _ => throw ()
|
||||
|
||||
end Lean
|
||||
|
||||
11
src/Init/Omega.lean
Normal file
11
src/Init/Omega.lean
Normal file
@@ -0,0 +1,11 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.Int
|
||||
import Init.Omega.IntList
|
||||
import Init.Omega.LinearCombo
|
||||
import Init.Omega.Constraint
|
||||
import Init.Omega.Logic
|
||||
112
src/Init/Omega/Coeffs.lean
Normal file
112
src/Init/Omega/Coeffs.lean
Normal file
@@ -0,0 +1,112 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.IntList
|
||||
|
||||
/-!
|
||||
# `Coeffs` as a wrapper for `IntList`
|
||||
|
||||
Currently `omega` uses a dense representation for coefficients.
|
||||
However, we can swap this out for a sparse representation.
|
||||
|
||||
This file sets up `Coeffs` as a type synonym for `IntList`,
|
||||
and abbreviations for the functions in the `IntList` namespace which we need to use in the
|
||||
`omega` algorithm.
|
||||
|
||||
There is an equivalent file setting up `Coeffs` as a type synonym for `AssocList Nat Int`,
|
||||
currently in a private branch.
|
||||
Not all the theorems about the algebraic operations on that representation have been proved yet.
|
||||
When they are ready, we can replace the implementation in `omega` simply by importing
|
||||
`Init.Omega.IntDict` instead of `Init.Omega.IntList`.
|
||||
|
||||
For small problems, the sparse representation is actually slightly slower,
|
||||
so it is not urgent to make this replacement.
|
||||
-/
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
/-- Type synonym for `IntList := List Int`. -/
|
||||
abbrev Coeffs := IntList
|
||||
|
||||
namespace Coeffs
|
||||
|
||||
/-- Identity, turning `Coeffs` into `List Int`. -/
|
||||
abbrev toList (xs : Coeffs) : List Int := xs
|
||||
/-- Identity, turning `List Int` into `Coeffs`. -/
|
||||
abbrev ofList (xs : List Int) : Coeffs := xs
|
||||
/-- Are the coefficients all zero? -/
|
||||
abbrev isZero (xs : Coeffs) : Prop := ∀ x, x ∈ xs → x = 0
|
||||
/-- Shim for `IntList.set`. -/
|
||||
abbrev set (xs : Coeffs) (i : Nat) (y : Int) : Coeffs := IntList.set xs i y
|
||||
/-- Shim for `IntList.get`. -/
|
||||
abbrev get (xs : Coeffs) (i : Nat) : Int := IntList.get xs i
|
||||
/-- Shim for `IntList.gcd`. -/
|
||||
abbrev gcd (xs : Coeffs) : Nat := IntList.gcd xs
|
||||
/-- Shim for `IntList.smul`. -/
|
||||
abbrev smul (xs : Coeffs) (g : Int) : Coeffs := IntList.smul xs g
|
||||
/-- Shim for `IntList.sdiv`. -/
|
||||
abbrev sdiv (xs : Coeffs) (g : Int) : Coeffs := IntList.sdiv xs g
|
||||
/-- Shim for `IntList.dot`. -/
|
||||
abbrev dot (xs ys : Coeffs) : Int := IntList.dot xs ys
|
||||
/-- Shim for `IntList.add`. -/
|
||||
abbrev add (xs ys : Coeffs) : Coeffs := IntList.add xs ys
|
||||
/-- Shim for `IntList.sub`. -/
|
||||
abbrev sub (xs ys : Coeffs) : Coeffs := IntList.sub xs ys
|
||||
/-- Shim for `IntList.neg`. -/
|
||||
abbrev neg (xs : Coeffs) : Coeffs := IntList.neg xs
|
||||
/-- Shim for `IntList.combo`. -/
|
||||
abbrev combo (a : Int) (xs : Coeffs) (b : Int) (ys : Coeffs) : Coeffs := IntList.combo a xs b ys
|
||||
/-- Shim for `List.length`. -/
|
||||
abbrev length (xs : Coeffs) := List.length xs
|
||||
/-- Shim for `IntList.leading`. -/
|
||||
abbrev leading (xs : Coeffs) : Int := IntList.leading xs
|
||||
/-- Shim for `List.map`. -/
|
||||
abbrev map (f : Int → Int) (xs : Coeffs) : Coeffs := List.map f xs
|
||||
/-- Shim for `.enum.find?`. -/
|
||||
abbrev findIdx? (f : Int → Bool) (xs : Coeffs) : Option Nat :=
|
||||
-- List.findIdx? f xs
|
||||
-- We could avoid `Std.Data.List.Basic` by using the less efficient:
|
||||
xs.enum.find? (f ·.2) |>.map (·.1)
|
||||
/-- Shim for `IntList.bmod`. -/
|
||||
abbrev bmod (x : Coeffs) (m : Nat) : Coeffs := IntList.bmod x m
|
||||
/-- Shim for `IntList.bmod_dot_sub_dot_bmod`. -/
|
||||
abbrev bmod_dot_sub_dot_bmod (m : Nat) (a b : Coeffs) : Int :=
|
||||
IntList.bmod_dot_sub_dot_bmod m a b
|
||||
theorem bmod_length (x : Coeffs) (m : Nat) : (bmod x m).length ≤ x.length :=
|
||||
IntList.bmod_length x m
|
||||
theorem dvd_bmod_dot_sub_dot_bmod (m : Nat) (xs ys : Coeffs) :
|
||||
(m : Int) ∣ bmod_dot_sub_dot_bmod m xs ys := IntList.dvd_bmod_dot_sub_dot_bmod m xs ys
|
||||
|
||||
theorem get_of_length_le {i : Nat} {xs : Coeffs} (h : length xs ≤ i) : get xs i = 0 :=
|
||||
IntList.get_of_length_le h
|
||||
theorem dot_set_left (xs ys : Coeffs) (i : Nat) (z : Int) :
|
||||
dot (set xs i z) ys = dot xs ys + (z - get xs i) * get ys i :=
|
||||
IntList.dot_set_left xs ys i z
|
||||
theorem dot_sdiv_left (xs ys : Coeffs) {d : Int} (h : d ∣ xs.gcd) :
|
||||
dot (xs.sdiv d) ys = (dot xs ys) / d :=
|
||||
IntList.dot_sdiv_left xs ys h
|
||||
theorem dot_smul_left (xs ys : Coeffs) (i : Int) : dot (i * xs) ys = i * dot xs ys :=
|
||||
IntList.dot_smul_left xs ys i
|
||||
theorem dot_distrib_left (xs ys zs : Coeffs) : (xs + ys).dot zs = xs.dot zs + ys.dot zs :=
|
||||
IntList.dot_distrib_left xs ys zs
|
||||
theorem sub_eq_add_neg (xs ys : Coeffs) : xs - ys = xs + -ys :=
|
||||
IntList.sub_eq_add_neg xs ys
|
||||
theorem combo_eq_smul_add_smul (a : Int) (xs : Coeffs) (b : Int) (ys : Coeffs) :
|
||||
combo a xs b ys = (a * xs) + (b * ys) :=
|
||||
IntList.combo_eq_smul_add_smul a xs b ys
|
||||
theorem gcd_dvd_dot_left (xs ys : Coeffs) : (gcd xs : Int) ∣ dot xs ys :=
|
||||
IntList.gcd_dvd_dot_left xs ys
|
||||
theorem map_length {xs : Coeffs} : (xs.map f).length ≤ xs.length :=
|
||||
Nat.le_of_eq (List.length_map xs f)
|
||||
|
||||
theorem dot_nil_right {xs : Coeffs} : dot xs .nil = 0 := IntList.dot_nil_right
|
||||
theorem get_nil : get .nil i = 0 := IntList.get_nil
|
||||
theorem dot_neg_left (xs ys : IntList) : dot (-xs) ys = -dot xs ys :=
|
||||
IntList.dot_neg_left xs ys
|
||||
|
||||
end Coeffs
|
||||
|
||||
end Lean.Omega
|
||||
395
src/Init/Omega/Constraint.lean
Normal file
395
src/Init/Omega/Constraint.lean
Normal file
@@ -0,0 +1,395 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.LinearCombo
|
||||
import Init.Omega.Int
|
||||
|
||||
/-!
|
||||
A `Constraint` consists of an optional lower and upper bound (inclusive),
|
||||
constraining a value to a set of the form `∅`, `{x}`, `[x, y]`, `[x, ∞)`, `(-∞, y]`, or `(-∞, ∞)`.
|
||||
-/
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
/-- An optional lower bound on a integer. -/
|
||||
abbrev LowerBound : Type := Option Int
|
||||
/-- An optional upper bound on a integer. -/
|
||||
abbrev UpperBound : Type := Option Int
|
||||
|
||||
/-- A lower bound at `x` is satisfied at `t` if `x ≤ t`. -/
|
||||
abbrev LowerBound.sat (b : LowerBound) (t : Int) := b.all fun x => x ≤ t
|
||||
/-- A upper bound at `y` is satisfied at `t` if `t ≤ y`. -/
|
||||
abbrev UpperBound.sat (b : UpperBound) (t : Int) := b.all fun y => t ≤ y
|
||||
|
||||
/--
|
||||
A `Constraint` consists of an optional lower and upper bound (inclusive),
|
||||
constraining a value to a set of the form `∅`, `{x}`, `[x, y]`, `[x, ∞)`, `(-∞, y]`, or `(-∞, ∞)`.
|
||||
-/
|
||||
structure Constraint where
|
||||
/-- A lower bound. -/
|
||||
lowerBound : LowerBound
|
||||
/-- An upper bound. -/
|
||||
upperBound : UpperBound
|
||||
deriving BEq, DecidableEq, Repr
|
||||
|
||||
namespace Constraint
|
||||
|
||||
instance : ToString Constraint where
|
||||
toString := fun
|
||||
| ⟨none, none⟩ => "(-∞, ∞)"
|
||||
| ⟨none, some y⟩ => s!"(-∞, {y}]"
|
||||
| ⟨some x, none⟩ => s!"[{x}, ∞)"
|
||||
| ⟨some x, some y⟩ =>
|
||||
if y < x then "∅" else if x = y then s!"\{{x}}" else s!"[{x}, {y}]"
|
||||
|
||||
/-- A constraint is satisfied at `t` is both the lower bound and upper bound are satisfied. -/
|
||||
def sat (c : Constraint) (t : Int) : Bool := c.lowerBound.sat t ∧ c.upperBound.sat t
|
||||
|
||||
/-- Apply a function to both the lower bound and upper bound. -/
|
||||
def map (c : Constraint) (f : Int → Int) : Constraint where
|
||||
lowerBound := c.lowerBound.map f
|
||||
upperBound := c.upperBound.map f
|
||||
|
||||
/-- Translate a constraint. -/
|
||||
def translate (c : Constraint) (t : Int) : Constraint := c.map (· + t)
|
||||
|
||||
theorem translate_sat : {c : Constraint} → {v : Int} → sat c v → sat (c.translate t) (v + t) := by
|
||||
rintro ⟨_ | l, _ | u⟩ v w <;> simp_all [sat, translate, map]
|
||||
· exact Int.add_le_add_right w t
|
||||
· exact Int.add_le_add_right w t
|
||||
· rcases w with ⟨w₁, w₂⟩; constructor
|
||||
· exact Int.add_le_add_right w₁ t
|
||||
· exact Int.add_le_add_right w₂ t
|
||||
|
||||
/--
|
||||
Flip a constraint.
|
||||
This operation is not useful by itself, but is used to implement `neg` and `scale`.
|
||||
-/
|
||||
def flip (c : Constraint) : Constraint where
|
||||
lowerBound := c.upperBound
|
||||
upperBound := c.lowerBound
|
||||
|
||||
/--
|
||||
Negate a constraint. `[x, y]` becomes `[-y, -x]`.
|
||||
-/
|
||||
def neg (c : Constraint) : Constraint := c.flip.map (- ·)
|
||||
|
||||
theorem neg_sat : {c : Constraint} → {v : Int} → sat c v → sat (c.neg) (-v) := by
|
||||
rintro ⟨_ | l, _ | u⟩ v w <;> simp_all [sat, neg, flip, map]
|
||||
· exact Int.neg_le_neg w
|
||||
· exact Int.neg_le_neg w
|
||||
· rcases w with ⟨w₁, w₂⟩; constructor
|
||||
· exact Int.neg_le_neg w₂
|
||||
· exact Int.neg_le_neg w₁
|
||||
|
||||
/-- The trivial constraint, satisfied everywhere. -/
|
||||
def trivial : Constraint := ⟨none, none⟩
|
||||
/-- The impossible constraint, unsatisfiable. -/
|
||||
def impossible : Constraint := ⟨some 1, some 0⟩
|
||||
/-- An exact constraint. -/
|
||||
def exact (r : Int) : Constraint := ⟨some r, some r⟩
|
||||
|
||||
@[simp] theorem trivial_say : trivial.sat t := by
|
||||
simp [sat, trivial]
|
||||
|
||||
@[simp] theorem exact_sat (r : Int) (t : Int) : (exact r).sat t = decide (r = t) := by
|
||||
simp only [sat, exact, Option.all_some, decide_eq_true_eq, decide_eq_decide]
|
||||
exact Int.eq_iff_le_and_ge.symm
|
||||
|
||||
/-- Check if a constraint is unsatisfiable. -/
|
||||
def isImpossible : Constraint → Bool
|
||||
| ⟨some x, some y⟩ => y < x
|
||||
| _ => false
|
||||
|
||||
/-- Check if a constraint requires an exact value. -/
|
||||
def isExact : Constraint → Bool
|
||||
| ⟨some x, some y⟩ => x = y
|
||||
| _ => false
|
||||
|
||||
theorem not_sat_of_isImpossible (h : isImpossible c) {t} : ¬ c.sat t := by
|
||||
rcases c with ⟨_ | l, _ | u⟩ <;> simp [isImpossible, sat] at h ⊢
|
||||
intro w
|
||||
rw [Int.not_le]
|
||||
exact Int.lt_of_lt_of_le h w
|
||||
|
||||
/--
|
||||
Scale a constraint by multiplying by an integer.
|
||||
* If `k = 0` this is either impossible, if the original constraint was impossible,
|
||||
or the `= 0` exact constraint.
|
||||
* If `k` is positive this takes `[x, y]` to `[k * x, k * y]`
|
||||
* If `k` is negative this takes `[x, y]` to `[k * y, k * x]`.
|
||||
-/
|
||||
def scale (k : Int) (c : Constraint) : Constraint :=
|
||||
if k = 0 then
|
||||
if c.isImpossible then c else ⟨some 0, some 0⟩
|
||||
else if 0 < k then
|
||||
c.map (k * ·)
|
||||
else
|
||||
c.flip.map (k * ·)
|
||||
|
||||
theorem scale_sat {c : Constraint} (k) (w : c.sat t) : (scale k c).sat (k * t) := by
|
||||
simp [scale]
|
||||
split
|
||||
· split
|
||||
· simp_all [not_sat_of_isImpossible]
|
||||
· simp_all [sat]
|
||||
· rcases c with ⟨_ | l, _ | u⟩ <;> split <;> rename_i h <;> simp_all [sat, flip, map]
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· replace h := Int.le_of_lt h
|
||||
exact Int.mul_le_mul_of_nonneg_left w h
|
||||
· rw [Int.not_lt] at h
|
||||
exact Int.mul_le_mul_of_nonpos_left h w
|
||||
· constructor
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.1 (Int.le_of_lt h)
|
||||
· exact Int.mul_le_mul_of_nonneg_left w.2 (Int.le_of_lt h)
|
||||
· replace h := Int.not_lt.mp h
|
||||
constructor
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.2
|
||||
· exact Int.mul_le_mul_of_nonpos_left h w.1
|
||||
|
||||
/-- The sum of two constraints. `[a, b] + [c, d] = [a + c, b + d]`. -/
|
||||
def add (x y : Constraint) : Constraint where
|
||||
lowerBound := x.lowerBound.bind fun a => y.lowerBound.map fun b => a + b
|
||||
upperBound := x.upperBound.bind fun a => y.upperBound.map fun b => a + b
|
||||
|
||||
theorem add_sat (w₁ : c₁.sat x₁) (w₂ : c₂.sat x₂) : (add c₁ c₂).sat (x₁ + x₂) := by
|
||||
rcases c₁ with ⟨_ | l₁, _ | u₁⟩ <;> rcases c₂ with ⟨_ | l₂, _ | u₂⟩
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, add] at *
|
||||
· exact Int.add_le_add w₁ w₂
|
||||
· exact Int.add_le_add w₁ w₂.2
|
||||
· exact Int.add_le_add w₁ w₂
|
||||
· exact Int.add_le_add w₁ w₂.1
|
||||
· exact Int.add_le_add w₁.2 w₂
|
||||
· exact Int.add_le_add w₁.1 w₂
|
||||
· constructor
|
||||
· exact Int.add_le_add w₁.1 w₂.1
|
||||
· exact Int.add_le_add w₁.2 w₂.2
|
||||
|
||||
/-- A linear combination of two constraints. -/
|
||||
def combo (a : Int) (x : Constraint) (b : Int) (y : Constraint) : Constraint :=
|
||||
add (scale a x) (scale b y)
|
||||
|
||||
theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
|
||||
(combo a c₁ b c₂).sat (a * x₁ + b * x₂) :=
|
||||
add_sat (scale_sat a w₁) (scale_sat b w₂)
|
||||
|
||||
/-- The conjunction of two constraints. -/
|
||||
def combine (x y : Constraint) : Constraint where
|
||||
lowerBound := max x.lowerBound y.lowerBound
|
||||
upperBound := min x.upperBound y.upperBound
|
||||
|
||||
theorem combine_sat : (c : Constraint) → (c' : Constraint) → (t : Int) →
|
||||
(c.combine c').sat t = (c.sat t ∧ c'.sat t) := by
|
||||
rintro ⟨_ | l₁, _ | u₁⟩ <;> rintro ⟨_ | l₂, _ | u₂⟩ t
|
||||
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le] at *
|
||||
· rw [And.comm]
|
||||
· rw [← and_assoc, And.comm (a := l₂ ≤ t), and_assoc]
|
||||
· rw [and_assoc]
|
||||
· rw [and_assoc]
|
||||
· rw [and_assoc, and_assoc, And.comm (a := l₂ ≤ t)]
|
||||
· rw [and_assoc, ← and_assoc (a := l₂ ≤ t), And.comm (a := l₂ ≤ t), and_assoc, and_assoc]
|
||||
|
||||
/--
|
||||
Dividing a constraint by a natural number, and tightened to integer bounds.
|
||||
Thus the lower bound is rounded up, and the upper bound is rounded down.
|
||||
-/
|
||||
def div (c : Constraint) (k : Nat) : Constraint where
|
||||
lowerBound := c.lowerBound.map fun x => (- ((- x) / k))
|
||||
upperBound := c.upperBound.map fun y => y / k
|
||||
|
||||
theorem div_sat (c : Constraint) (t : Int) (k : Nat) (n : k ≠ 0) (h : (k : Int) ∣ t) (w : c.sat t) :
|
||||
(c.div k).sat (t / k) := by
|
||||
replace n : (k : Int) > 0 := Int.ofNat_lt.mpr (Nat.pos_of_ne_zero n)
|
||||
rcases c with ⟨_ | l, _ | u⟩
|
||||
· simp_all [sat, div]
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w
|
||||
· simp [sat, div] at w ⊢
|
||||
constructor
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [Int.sub_neg, ← Int.add_ediv_of_dvd_left h, ← ge_iff_le,
|
||||
Int.div_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.1
|
||||
· apply Int.le_of_sub_nonneg
|
||||
rw [← Int.sub_ediv_of_dvd _ h, ← ge_iff_le, Int.div_nonneg_iff_of_pos n]
|
||||
exact Int.sub_nonneg_of_le w.2
|
||||
|
||||
/--
|
||||
It is convenient below to say that a constraint is satisfied at the dot product of two vectors,
|
||||
so we make an abbreviation `sat'` for this.
|
||||
-/
|
||||
abbrev sat' (c : Constraint) (x y : Coeffs) := c.sat (Coeffs.dot x y)
|
||||
|
||||
theorem combine_sat' {s t : Constraint} {x y} (ws : s.sat' x y) (wt : t.sat' x y) :
|
||||
(s.combine t).sat' x y := (combine_sat _ _ _).mpr ⟨ws, wt⟩
|
||||
|
||||
theorem div_sat' {c : Constraint} {x y} (h : Coeffs.gcd x ≠ 0) (w : c.sat (Coeffs.dot x y)) :
|
||||
(c.div (Coeffs.gcd x)).sat' (Coeffs.sdiv x (Coeffs.gcd x)) y := by
|
||||
dsimp [sat']
|
||||
rw [Coeffs.dot_sdiv_left _ _ (Int.dvd_refl _)]
|
||||
exact div_sat c _ (Coeffs.gcd x) h (Coeffs.gcd_dvd_dot_left x y) w
|
||||
|
||||
theorem not_sat'_of_isImpossible (h : isImpossible c) {x y} : ¬ c.sat' x y :=
|
||||
not_sat_of_isImpossible h
|
||||
|
||||
theorem addInequality_sat (w : c + Coeffs.dot x y ≥ 0) :
|
||||
Constraint.sat' { lowerBound := some (-c), upperBound := none } x y := by
|
||||
simp [Constraint.sat', Constraint.sat]
|
||||
rw [← Int.zero_sub c]
|
||||
exact Int.sub_left_le_of_le_add w
|
||||
|
||||
theorem addEquality_sat (w : c + Coeffs.dot x y = 0) :
|
||||
Constraint.sat' { lowerBound := some (-c), upperBound := some (-c) } x y := by
|
||||
simp [Constraint.sat', Constraint.sat]
|
||||
rw [Int.eq_iff_le_and_ge] at w
|
||||
rwa [Int.add_le_zero_iff_le_neg', Int.add_nonnneg_iff_neg_le', and_comm] at w
|
||||
|
||||
end Constraint
|
||||
|
||||
/--
|
||||
Normalize a constraint, by dividing through by the GCD.
|
||||
|
||||
Return `none` if there is nothing to do, to avoid adding unnecessary steps to the proof term.
|
||||
-/
|
||||
def normalize? : Constraint × Coeffs → Option (Constraint × Coeffs)
|
||||
| ⟨s, x⟩ =>
|
||||
let gcd := Coeffs.gcd x -- TODO should we be caching this?
|
||||
if gcd = 0 then
|
||||
if s.sat 0 then
|
||||
some (.trivial, x)
|
||||
else
|
||||
some (.impossible, x)
|
||||
else if gcd = 1 then
|
||||
none
|
||||
else
|
||||
some (s.div gcd, Coeffs.sdiv x gcd)
|
||||
|
||||
/-- Normalize a constraint, by dividing through by the GCD. -/
|
||||
def normalize (p : Constraint × Coeffs) : Constraint × Coeffs :=
|
||||
normalize? p |>.getD p
|
||||
|
||||
/-- Shorthand for the first component of `normalize`. -/
|
||||
-- This `noncomputable` (and others below) is a safeguard that we only use this in proofs.
|
||||
noncomputable abbrev normalizeConstraint (s : Constraint) (x : Coeffs) : Constraint :=
|
||||
(normalize (s, x)).1
|
||||
/-- Shorthand for the second component of `normalize`. -/
|
||||
noncomputable abbrev normalizeCoeffs (s : Constraint) (x : Coeffs) : Coeffs :=
|
||||
(normalize (s, x)).2
|
||||
|
||||
theorem normalize?_eq_some (w : normalize? (s, x) = some (s', x')) :
|
||||
normalizeConstraint s x = s' ∧ normalizeCoeffs s x = x' := by
|
||||
simp_all [normalizeConstraint, normalizeCoeffs, normalize]
|
||||
|
||||
theorem normalize_sat {s x v} (w : s.sat' x v) :
|
||||
(normalizeConstraint s x).sat' (normalizeCoeffs s x) v := by
|
||||
dsimp [normalizeConstraint, normalizeCoeffs, normalize, normalize?]
|
||||
split <;> rename_i h
|
||||
· split
|
||||
· simp
|
||||
· dsimp [Constraint.sat'] at w
|
||||
simp_all
|
||||
· split
|
||||
· exact w
|
||||
· exact Constraint.div_sat' h w
|
||||
|
||||
/-- Multiply by `-1` if the leading coefficient is negative, otherwise return `none`. -/
|
||||
def positivize? : Constraint × Coeffs → Option (Constraint × Coeffs)
|
||||
| ⟨s, x⟩ =>
|
||||
if 0 ≤ x.leading then
|
||||
none
|
||||
else
|
||||
(s.neg, Coeffs.smul x (-1))
|
||||
|
||||
/-- Multiply by `-1` if the leading coefficient is negative, otherwise do nothing. -/
|
||||
noncomputable def positivize (p : Constraint × Coeffs) : Constraint × Coeffs :=
|
||||
positivize? p |>.getD p
|
||||
|
||||
/-- Shorthand for the first component of `positivize`. -/
|
||||
noncomputable abbrev positivizeConstraint (s : Constraint) (x : Coeffs) : Constraint :=
|
||||
(positivize (s, x)).1
|
||||
/-- Shorthand for the second component of `positivize`. -/
|
||||
noncomputable abbrev positivizeCoeffs (s : Constraint) (x : Coeffs) : Coeffs :=
|
||||
(positivize (s, x)).2
|
||||
|
||||
theorem positivize?_eq_some (w : positivize? (s, x) = some (s', x')) :
|
||||
positivizeConstraint s x = s' ∧ positivizeCoeffs s x = x' := by
|
||||
simp_all [positivizeConstraint, positivizeCoeffs, positivize]
|
||||
|
||||
theorem positivize_sat {s x v} (w : s.sat' x v) :
|
||||
(positivizeConstraint s x).sat' (positivizeCoeffs s x) v := by
|
||||
dsimp [positivizeConstraint, positivizeCoeffs, positivize, positivize?]
|
||||
split
|
||||
· exact w
|
||||
· simp [Constraint.sat']
|
||||
erw [Coeffs.dot_smul_left, ← Int.neg_eq_neg_one_mul]
|
||||
exact Constraint.neg_sat w
|
||||
|
||||
/-- `positivize` and `normalize`, returning `none` if neither does anything. -/
|
||||
def tidy? : Constraint × Coeffs → Option (Constraint × Coeffs)
|
||||
| ⟨s, x⟩ =>
|
||||
match positivize? (s, x) with
|
||||
| none => match normalize? (s, x) with
|
||||
| none => none
|
||||
| some (s', x') => some (s', x')
|
||||
| some (s', x') => normalize (s', x')
|
||||
|
||||
/-- `positivize` and `normalize` -/
|
||||
def tidy (p : Constraint × Coeffs) : Constraint × Coeffs :=
|
||||
tidy? p |>.getD p
|
||||
|
||||
/-- Shorthand for the first component of `tidy`. -/
|
||||
abbrev tidyConstraint (s : Constraint) (x : Coeffs) : Constraint := (tidy (s, x)).1
|
||||
/-- Shorthand for the second component of `tidy`. -/
|
||||
abbrev tidyCoeffs (s : Constraint) (x : Coeffs) : Coeffs := (tidy (s, x)).2
|
||||
|
||||
theorem tidy_sat {s x v} (w : s.sat' x v) : (tidyConstraint s x).sat' (tidyCoeffs s x) v := by
|
||||
dsimp [tidyConstraint, tidyCoeffs, tidy, tidy?]
|
||||
split <;> rename_i hp
|
||||
· split <;> rename_i hn
|
||||
· simp_all
|
||||
· rcases normalize?_eq_some hn with ⟨rfl, rfl⟩
|
||||
exact normalize_sat w
|
||||
· rcases positivize?_eq_some hp with ⟨rfl, rfl⟩
|
||||
exact normalize_sat (positivize_sat w)
|
||||
|
||||
theorem combo_sat' (s t : Constraint)
|
||||
(a : Int) (x : Coeffs) (b : Int) (y : Coeffs) (v : Coeffs)
|
||||
(wx : s.sat' x v) (wy : t.sat' y v) :
|
||||
(Constraint.combo a s b t).sat' (Coeffs.combo a x b y) v := by
|
||||
rw [Constraint.sat', Coeffs.combo_eq_smul_add_smul, Coeffs.dot_distrib_left,
|
||||
Coeffs.dot_smul_left, Coeffs.dot_smul_left]
|
||||
exact Constraint.combo_sat a wx b wy
|
||||
|
||||
/-- The value of the new variable introduced when solving a hard equality. -/
|
||||
abbrev bmod_div_term (m : Nat) (a b : Coeffs) : Int := Coeffs.bmod_dot_sub_dot_bmod m a b / m
|
||||
|
||||
/-- The coefficients of the new equation generated when solving a hard equality. -/
|
||||
def bmod_coeffs (m : Nat) (i : Nat) (x : Coeffs) : Coeffs :=
|
||||
Coeffs.set (Coeffs.bmod x m) i m
|
||||
|
||||
theorem bmod_sat (m : Nat) (r : Int) (i : Nat) (x v : Coeffs)
|
||||
(h : x.length ≤ i) -- during proof reconstruction this will be by `decide`
|
||||
(p : Coeffs.get v i = bmod_div_term m x v) -- and this will be by `rfl`
|
||||
(w : (Constraint.exact r).sat' x v) :
|
||||
(Constraint.exact (Int.bmod r m)).sat' (bmod_coeffs m i x) v := by
|
||||
simp at w
|
||||
simp only [p, bmod_coeffs, Constraint.exact_sat, Coeffs.dot_set_left, decide_eq_true_eq]
|
||||
replace h := Nat.le_trans (Coeffs.bmod_length x m) h
|
||||
rw [Coeffs.get_of_length_le h, Int.sub_zero,
|
||||
Int.mul_ediv_cancel' (Coeffs.dvd_bmod_dot_sub_dot_bmod _ _ _), w,
|
||||
← Int.add_sub_assoc, Int.add_comm, Int.add_sub_assoc, Int.sub_self, Int.add_zero]
|
||||
|
||||
end Lean.Omega
|
||||
214
src/Init/Omega/Int.lean
Normal file
214
src/Init/Omega/Int.lean
Normal file
@@ -0,0 +1,214 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.Int.DivModLemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `Nat`, `Int`, and `Fin` needed internally by `omega`.
|
||||
|
||||
These statements are useful for constructing proof expressions,
|
||||
but unlikely to be widely useful, so are inside the `Lean.Omega` namespace.
|
||||
|
||||
If you do find a use for them, please move them into the appropriate file and namespace!
|
||||
-/
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
namespace Int
|
||||
|
||||
theorem ofNat_pow (a b : Nat) : ((a ^ b : Nat) : Int) = (a : Int) ^ b := by
|
||||
induction b with
|
||||
| zero => rfl
|
||||
| succ b ih => rw [Nat.pow_succ, Int.ofNat_mul, ih]; rfl
|
||||
|
||||
theorem pos_pow_of_pos (a : Int) (b : Nat) (h : 0 < a) : 0 < a ^ b := by
|
||||
rw [Int.eq_natAbs_of_zero_le (Int.le_of_lt h), ← Int.ofNat_zero, ← Int.ofNat_pow, Int.ofNat_lt]
|
||||
exact Nat.pos_pow_of_pos _ (Int.natAbs_pos.mpr (Int.ne_of_gt h))
|
||||
|
||||
theorem ofNat_pos {a : Nat} : 0 < (a : Int) ↔ 0 < a := by
|
||||
rw [← Int.ofNat_zero, Int.ofNat_lt]
|
||||
|
||||
theorem ofNat_pos_of_pos {a : Nat} (h : 0 < a) : 0 < (a : Int) :=
|
||||
ofNat_pos.mpr h
|
||||
|
||||
theorem natCast_ofNat {x : Nat} :
|
||||
@Nat.cast Int instNatCastInt (no_index (OfNat.ofNat x)) = OfNat.ofNat x := rfl
|
||||
|
||||
theorem ofNat_lt_of_lt {x y : Nat} (h : x < y) : (x : Int) < (y : Int) :=
|
||||
Int.ofNat_lt.mpr h
|
||||
|
||||
theorem ofNat_le_of_le {x y : Nat} (h : x ≤ y) : (x : Int) ≤ (y : Int) :=
|
||||
Int.ofNat_le.mpr h
|
||||
|
||||
theorem ofNat_shiftLeft_eq {x y : Nat} : (x <<< y : Int) = (x : Int) * (2 ^ y : Nat) := by
|
||||
simp [Nat.shiftLeft_eq]
|
||||
|
||||
theorem ofNat_shiftRight_eq_div_pow {x y : Nat} : (x >>> y : Int) = (x : Int) / (2 ^ y : Nat) := by
|
||||
simp [Nat.shiftRight_eq_div_pow]
|
||||
|
||||
-- FIXME these are insane:
|
||||
theorem lt_of_not_ge {x y : Int} (h : ¬ (x ≤ y)) : y < x := Int.not_le.mp h
|
||||
theorem lt_of_not_le {x y : Int} (h : ¬ (x ≤ y)) : y < x := Int.not_le.mp h
|
||||
theorem not_le_of_lt {x y : Int} (h : y < x) : ¬ (x ≤ y) := Int.not_le.mpr h
|
||||
theorem lt_le_asymm {x y : Int} (h₁ : y < x) (h₂ : x ≤ y) : False := Int.not_le.mpr h₁ h₂
|
||||
theorem le_lt_asymm {x y : Int} (h₁ : y ≤ x) (h₂ : x < y) : False := Int.not_lt.mpr h₁ h₂
|
||||
|
||||
theorem le_of_not_gt {x y : Int} (h : ¬ (y > x)) : y ≤ x := Int.not_lt.mp h
|
||||
theorem not_lt_of_ge {x y : Int} (h : y ≥ x) : ¬ (y < x) := Int.not_lt.mpr h
|
||||
theorem le_of_not_lt {x y : Int} (h : ¬ (x < y)) : y ≤ x := Int.not_lt.mp h
|
||||
theorem not_lt_of_le {x y : Int} (h : y ≤ x) : ¬ (x < y) := Int.not_lt.mpr h
|
||||
|
||||
theorem add_congr {a b c d : Int} (h₁ : a = b) (h₂ : c = d) : a + c = b + d := by
|
||||
subst h₁; subst h₂; rfl
|
||||
|
||||
theorem mul_congr {a b c d : Int} (h₁ : a = b) (h₂ : c = d) : a * c = b * d := by
|
||||
subst h₁; subst h₂; rfl
|
||||
|
||||
theorem mul_congr_left {a b : Int} (h₁ : a = b) (c : Int) : a * c = b * c := by
|
||||
subst h₁; rfl
|
||||
|
||||
theorem sub_congr {a b c d : Int} (h₁ : a = b) (h₂ : c = d) : a - c = b - d := by
|
||||
subst h₁; subst h₂; rfl
|
||||
|
||||
theorem neg_congr {a b : Int} (h₁ : a = b) : -a = -b := by
|
||||
subst h₁; rfl
|
||||
|
||||
theorem lt_of_gt {x y : Int} (h : x > y) : y < x := gt_iff_lt.mp h
|
||||
theorem le_of_ge {x y : Int} (h : x ≥ y) : y ≤ x := ge_iff_le.mp h
|
||||
|
||||
theorem ofNat_sub_eq_zero {b a : Nat} (h : ¬ b ≤ a) : ((a - b : Nat) : Int) = 0 :=
|
||||
Int.ofNat_eq_zero.mpr (Nat.sub_eq_zero_of_le (Nat.le_of_lt (Nat.not_le.mp h)))
|
||||
|
||||
theorem ofNat_sub_dichotomy {a b : Nat} :
|
||||
b ≤ a ∧ ((a - b : Nat) : Int) = a - b ∨ a < b ∧ ((a - b : Nat) : Int) = 0 := by
|
||||
by_cases h : b ≤ a
|
||||
· left
|
||||
have t := Int.ofNat_sub h
|
||||
simp at t
|
||||
exact ⟨h, t⟩
|
||||
· right
|
||||
have t := Nat.not_le.mp h
|
||||
simp [Int.ofNat_sub_eq_zero h]
|
||||
exact t
|
||||
|
||||
theorem ofNat_congr {a b : Nat} (h : a = b) : (a : Int) = (b : Int) := congrArg _ h
|
||||
|
||||
theorem ofNat_sub_sub {a b c : Nat} : ((a - b - c : Nat) : Int) = ((a - (b + c) : Nat) : Int) :=
|
||||
congrArg _ (Nat.sub_sub _ _ _)
|
||||
|
||||
theorem ofNat_min (a b : Nat) : ((min a b : Nat) : Int) = min (a : Int) (b : Int) := by
|
||||
simp only [Nat.min_def, Int.min_def, Int.ofNat_le]
|
||||
split <;> rfl
|
||||
|
||||
theorem ofNat_max (a b : Nat) : ((max a b : Nat) : Int) = max (a : Int) (b : Int) := by
|
||||
simp only [Nat.max_def, Int.max_def, Int.ofNat_le]
|
||||
split <;> rfl
|
||||
|
||||
theorem ofNat_natAbs (a : Int) : (a.natAbs : Int) = if 0 ≤ a then a else -a := by
|
||||
rw [Int.natAbs]
|
||||
split <;> rename_i n
|
||||
· simp only [Int.ofNat_eq_coe]
|
||||
rw [if_pos (Int.ofNat_nonneg n)]
|
||||
· simp; rfl
|
||||
|
||||
theorem natAbs_dichotomy {a : Int} : 0 ≤ a ∧ a.natAbs = a ∨ a < 0 ∧ a.natAbs = -a := by
|
||||
by_cases h : 0 ≤ a
|
||||
· left
|
||||
simp_all [Int.natAbs_of_nonneg]
|
||||
· right
|
||||
rw [Int.not_le] at h
|
||||
rw [Int.ofNat_natAbs_of_nonpos (Int.le_of_lt h)]
|
||||
simp_all
|
||||
|
||||
theorem neg_le_natAbs {a : Int} : -a ≤ a.natAbs := by
|
||||
have t := Int.le_natAbs (a := -a)
|
||||
simp at t
|
||||
exact t
|
||||
|
||||
theorem add_le_iff_le_sub (a b c : Int) : a + b ≤ c ↔ a ≤ c - b := by
|
||||
conv =>
|
||||
lhs
|
||||
rw [← Int.add_zero c, ← Int.sub_self (-b), Int.sub_eq_add_neg, ← Int.add_assoc, Int.neg_neg,
|
||||
Int.add_le_add_iff_right]
|
||||
|
||||
theorem le_add_iff_sub_le (a b c : Int) : a ≤ b + c ↔ a - c ≤ b := by
|
||||
conv =>
|
||||
lhs
|
||||
rw [← Int.neg_neg c, ← Int.sub_eq_add_neg, ← add_le_iff_le_sub]
|
||||
|
||||
theorem add_le_zero_iff_le_neg (a b : Int) : a + b ≤ 0 ↔ a ≤ - b := by
|
||||
rw [add_le_iff_le_sub, Int.zero_sub]
|
||||
theorem add_le_zero_iff_le_neg' (a b : Int) : a + b ≤ 0 ↔ b ≤ -a := by
|
||||
rw [Int.add_comm, add_le_zero_iff_le_neg]
|
||||
theorem add_nonnneg_iff_neg_le (a b : Int) : 0 ≤ a + b ↔ -b ≤ a := by
|
||||
rw [le_add_iff_sub_le, Int.zero_sub]
|
||||
theorem add_nonnneg_iff_neg_le' (a b : Int) : 0 ≤ a + b ↔ -a ≤ b := by
|
||||
rw [Int.add_comm, add_nonnneg_iff_neg_le]
|
||||
|
||||
theorem ofNat_fst_mk {β} {x : Nat} {y : β} : (Prod.mk x y).fst = (x : Int) := rfl
|
||||
theorem ofNat_snd_mk {α} {x : α} {y : Nat} : (Prod.mk x y).snd = (y : Int) := rfl
|
||||
|
||||
|
||||
end Int
|
||||
|
||||
namespace Nat
|
||||
|
||||
theorem lt_of_gt {x y : Nat} (h : x > y) : y < x := gt_iff_lt.mp h
|
||||
theorem le_of_ge {x y : Nat} (h : x ≥ y) : y ≤ x := ge_iff_le.mp h
|
||||
|
||||
end Nat
|
||||
|
||||
namespace Fin
|
||||
|
||||
theorem ne_iff_lt_or_gt {i j : Fin n} : i ≠ j ↔ i < j ∨ i > j := by
|
||||
cases i; cases j; simp only [ne_eq, Fin.mk.injEq, Nat.ne_iff_lt_or_gt, gt_iff_lt]; rfl
|
||||
|
||||
protected theorem lt_or_gt_of_ne {i j : Fin n} (h : i ≠ j) : i < j ∨ i > j := Fin.ne_iff_lt_or_gt.mp h
|
||||
|
||||
theorem not_le {i j : Fin n} : ¬ i ≤ j ↔ j < i := by
|
||||
cases i; cases j; exact Nat.not_le
|
||||
|
||||
theorem not_lt {i j : Fin n} : ¬ i < j ↔ j ≤ i := by
|
||||
cases i; cases j; exact Nat.not_lt
|
||||
|
||||
protected theorem lt_of_not_le {i j : Fin n} (h : ¬ i ≤ j) : j < i := Fin.not_le.mp h
|
||||
protected theorem le_of_not_lt {i j : Fin n} (h : ¬ i < j) : j ≤ i := Fin.not_lt.mp h
|
||||
|
||||
theorem ofNat_val_add {x y : Fin n} :
|
||||
(((x + y : Fin n)) : Int) = ((x : Int) + (y : Int)) % n := rfl
|
||||
|
||||
theorem ofNat_val_sub {x y : Fin n} :
|
||||
(((x - y : Fin n)) : Int) = ((x : Int) + ((n - y : Nat) : Int)) % n := rfl
|
||||
|
||||
theorem ofNat_val_mul {x y : Fin n} :
|
||||
(((x * y : Fin n)) : Int) = ((x : Int) * (y : Int)) % n := rfl
|
||||
|
||||
theorem ofNat_val_natCast {n x y : Nat} (h : y = x % (n + 1)):
|
||||
@Nat.cast Int instNatCastInt (@Fin.val (n + 1) (OfNat.ofNat x)) = OfNat.ofNat y := by
|
||||
rw [h]
|
||||
rfl
|
||||
|
||||
end Fin
|
||||
|
||||
namespace Prod
|
||||
|
||||
theorem of_lex (w : Prod.Lex r s p q) : r p.fst q.fst ∨ p.fst = q.fst ∧ s p.snd q.snd :=
|
||||
(Prod.lex_def r s).mp w
|
||||
|
||||
theorem of_not_lex {α} {r : α → α → Prop} [DecidableEq α] {β} {s : β → β → Prop}
|
||||
{p q : α × β} (w : ¬ Prod.Lex r s p q) :
|
||||
¬ r p.fst q.fst ∧ (p.fst ≠ q.fst ∨ ¬ s p.snd q.snd) := by
|
||||
rw [Prod.lex_def, not_or, Decidable.not_and_iff_or_not_not] at w
|
||||
exact w
|
||||
|
||||
theorem fst_mk : (Prod.mk x y).fst = x := rfl
|
||||
theorem snd_mk : (Prod.mk x y).snd = y := rfl
|
||||
|
||||
end Prod
|
||||
|
||||
end Lean.Omega
|
||||
412
src/Init/Omega/IntList.lean
Normal file
412
src/Init/Omega/IntList.lean
Normal file
@@ -0,0 +1,412 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
/--
|
||||
A type synonym for `List Int`, used by `omega` for dense representation of coefficients.
|
||||
|
||||
We define algebraic operations,
|
||||
interpreting `List Int` as a finitely supported function `Nat → Int`.
|
||||
-/
|
||||
abbrev IntList := List Int
|
||||
|
||||
namespace IntList
|
||||
|
||||
/-- Get the `i`-th element (interpreted as `0` if the list is not long enough). -/
|
||||
def get (xs : IntList) (i : Nat) : Int := (xs.get? i).getD 0
|
||||
|
||||
@[simp] theorem get_nil : get ([] : IntList) i = 0 := rfl
|
||||
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := rfl
|
||||
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := rfl
|
||||
|
||||
theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) := by
|
||||
simp only [get, List.get?_map]
|
||||
cases xs.get? i <;> simp_all
|
||||
|
||||
theorem get_of_length_le {xs : IntList} (h : xs.length ≤ i) : xs.get i = 0 := by
|
||||
rw [get, List.get?_eq_none.mpr h]
|
||||
rfl
|
||||
|
||||
-- theorem lt_length_of_get_nonzero {xs : IntList} (h : xs.get i ≠ 0) : i < xs.length := by
|
||||
-- revert h
|
||||
-- simpa using mt get_of_length_le
|
||||
|
||||
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
|
||||
def set (xs : IntList) (i : Nat) (y : Int) : IntList :=
|
||||
match xs, i with
|
||||
| [], 0 => [y]
|
||||
| [], (i+1) => 0 :: set [] i y
|
||||
| _ :: xs, 0 => y :: xs
|
||||
| x :: xs, (i+1) => x :: set xs i y
|
||||
|
||||
@[simp] theorem set_nil_zero : set [] 0 y = [y] := rfl
|
||||
@[simp] theorem set_nil_succ : set [] (i+1) y = 0 :: set [] i y := rfl
|
||||
@[simp] theorem set_cons_zero : set (x :: xs) 0 y = y :: xs := rfl
|
||||
@[simp] theorem set_cons_succ : set (x :: xs) (i+1) y = x :: set xs i y := rfl
|
||||
|
||||
/-- Returns the leading coefficient, i.e. the first non-zero entry. -/
|
||||
def leading (xs : IntList) : Int := xs.find? (! · == 0) |>.getD 0
|
||||
|
||||
/-- Implementation of `+` on `IntList`. -/
|
||||
def add (xs ys : IntList) : IntList :=
|
||||
List.zipWithAll (fun x y => x.getD 0 + y.getD 0) xs ys
|
||||
|
||||
instance : Add IntList := ⟨add⟩
|
||||
|
||||
theorem add_def (xs ys : IntList) :
|
||||
xs + ys = List.zipWithAll (fun x y => x.getD 0 + y.getD 0) xs ys :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem add_get (xs ys : IntList) (i : Nat) : (xs + ys).get i = xs.get i + ys.get i := by
|
||||
simp only [add_def, get, List.zipWithAll_get?, List.get?_eq_none]
|
||||
cases xs.get? i <;> cases ys.get? i <;> simp
|
||||
|
||||
@[simp] theorem add_nil (xs : IntList) : xs + [] = xs := by simp [add_def]
|
||||
@[simp] theorem nil_add (xs : IntList) : [] + xs = xs := by simp [add_def]
|
||||
@[simp] theorem cons_add_cons (x) (xs : IntList) (y) (ys : IntList) :
|
||||
(x :: xs) + (y :: ys) = (x + y) :: (xs + ys) := by simp [add_def]
|
||||
|
||||
/-- Implementation of `*` on `IntList`. -/
|
||||
def mul (xs ys : IntList) : IntList := List.zipWith (· * ·) xs ys
|
||||
|
||||
instance : Mul IntList := ⟨mul⟩
|
||||
|
||||
theorem mul_def (xs ys : IntList) : xs * ys = List.zipWith (· * ·) xs ys :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem mul_get (xs ys : IntList) (i : Nat) : (xs * ys).get i = xs.get i * ys.get i := by
|
||||
simp only [mul_def, get, List.zipWith_get?]
|
||||
cases xs.get? i <;> cases ys.get? i <;> simp
|
||||
|
||||
@[simp] theorem mul_nil_left : ([] : IntList) * ys = [] := rfl
|
||||
@[simp] theorem mul_nil_right : xs * ([] : IntList) = [] := List.zipWith_nil_right
|
||||
@[simp] theorem mul_cons₂ : (x::xs : IntList) * (y::ys) = (x * y) :: (xs * ys) := rfl
|
||||
|
||||
/-- Implementation of negation on `IntList`. -/
|
||||
def neg (xs : IntList) : IntList := xs.map fun x => -x
|
||||
|
||||
instance : Neg IntList := ⟨neg⟩
|
||||
|
||||
theorem neg_def (xs : IntList) : - xs = xs.map fun x => -x := rfl
|
||||
|
||||
@[simp] theorem neg_get (xs : IntList) (i : Nat) : (- xs).get i = - xs.get i := by
|
||||
simp only [neg_def, get, List.get?_map]
|
||||
cases xs.get? i <;> simp
|
||||
|
||||
@[simp] theorem neg_nil : (- ([] : IntList)) = [] := rfl
|
||||
@[simp] theorem neg_cons : (- (x::xs : IntList)) = -x :: -xs := rfl
|
||||
|
||||
/-- Implementation of subtraction on `IntList`. -/
|
||||
def sub (xs ys : IntList) : IntList :=
|
||||
List.zipWithAll (fun x y => x.getD 0 - y.getD 0) xs ys
|
||||
|
||||
instance : Sub IntList := ⟨sub⟩
|
||||
|
||||
theorem sub_def (xs ys : IntList) :
|
||||
xs - ys = List.zipWithAll (fun x y => x.getD 0 - y.getD 0) xs ys :=
|
||||
rfl
|
||||
|
||||
/-- Implementation of scalar multiplication by an integer on `IntList`. -/
|
||||
def smul (xs : IntList) (i : Int) : IntList :=
|
||||
xs.map fun x => i * x
|
||||
|
||||
instance : HMul Int IntList IntList where
|
||||
hMul i xs := xs.smul i
|
||||
|
||||
theorem smul_def (xs : IntList) (i : Int) : i * xs = xs.map fun x => i * x := rfl
|
||||
|
||||
@[simp] theorem smul_get (xs : IntList) (a : Int) (i : Nat) : (a * xs).get i = a * xs.get i := by
|
||||
simp only [smul_def, get, List.get?_map]
|
||||
cases xs.get? i <;> simp
|
||||
|
||||
@[simp] theorem smul_nil {i : Int} : i * ([] : IntList) = [] := rfl
|
||||
@[simp] theorem smul_cons {i : Int} : i * (x::xs : IntList) = i * x :: i * xs := rfl
|
||||
|
||||
/-- A linear combination of two `IntList`s. -/
|
||||
def combo (a : Int) (xs : IntList) (b : Int) (ys : IntList) : IntList :=
|
||||
List.zipWithAll (fun x y => a * x.getD 0 + b * y.getD 0) xs ys
|
||||
|
||||
theorem combo_eq_smul_add_smul (a : Int) (xs : IntList) (b : Int) (ys : IntList) :
|
||||
combo a xs b ys = a * xs + b * ys := by
|
||||
dsimp [combo]
|
||||
induction xs generalizing ys with
|
||||
| nil => simp; rfl
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp; rfl
|
||||
| cons y ys => simp_all
|
||||
|
||||
attribute [local simp] add_def mul_def in
|
||||
theorem mul_distrib_left (xs ys zs : IntList) : (xs + ys) * zs = xs * zs + ys * zs := by
|
||||
induction xs generalizing ys zs with
|
||||
| nil =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons _ _ =>
|
||||
cases zs with
|
||||
| nil => simp
|
||||
| cons _ _ => simp_all [Int.add_mul]
|
||||
| cons x xs ih₁ =>
|
||||
cases ys with
|
||||
| nil => simp_all
|
||||
| cons _ _ =>
|
||||
cases zs with
|
||||
| nil => simp
|
||||
| cons _ _ => simp_all [Int.add_mul]
|
||||
|
||||
theorem mul_neg_left (xs ys : IntList) : (-xs) * ys = -(xs * ys) := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys => simp_all [Int.neg_mul]
|
||||
|
||||
attribute [local simp] add_def neg_def sub_def in
|
||||
theorem sub_eq_add_neg (xs ys : IntList) : xs - ys = xs + (-ys) := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp; rfl
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys => simp_all [Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem mul_smul_left {i : Int} {xs ys : IntList} : (i * xs) * ys = i * (xs * ys) := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys => simp_all [Int.mul_assoc]
|
||||
|
||||
/-- The sum of the entries of an `IntList`. -/
|
||||
def sum (xs : IntList) : Int := xs.foldr (· + ·) 0
|
||||
|
||||
@[simp] theorem sum_nil : sum ([] : IntList) = 0 := rfl
|
||||
@[simp] theorem sum_cons : sum (x::xs : IntList) = x + sum xs := rfl
|
||||
|
||||
attribute [local simp] sum add_def in
|
||||
theorem sum_add (xs ys : IntList) : (xs + ys).sum = xs.sum + ys.sum := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys => simp_all [Int.add_assoc, Int.add_left_comm]
|
||||
|
||||
@[simp]
|
||||
theorem sum_neg (xs : IntList) : (-xs).sum = -(xs.sum) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih => simp_all [Int.neg_add]
|
||||
|
||||
@[simp]
|
||||
theorem sum_smul (i : Int) (xs : IntList) : (i * xs).sum = i * (xs.sum) := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih => simp_all [Int.mul_add]
|
||||
|
||||
/-- The dot product of two `IntList`s. -/
|
||||
def dot (xs ys : IntList) : Int := (xs * ys).sum
|
||||
|
||||
example : IntList.dot [a, b, c] [x, y, z] = IntList.dot [a, b, c, d] [x, y, z] := rfl
|
||||
example : IntList.dot [a, b, c] [x, y, z] = IntList.dot [a, b, c] [x, y, z, w] := rfl
|
||||
|
||||
@[local simp] theorem dot_nil_left : dot ([] : IntList) ys = 0 := rfl
|
||||
@[simp] theorem dot_nil_right : dot xs ([] : IntList) = 0 := by simp [dot]
|
||||
@[simp] theorem dot_cons₂ : dot (x::xs) (y::ys) = x * y + dot xs ys := rfl
|
||||
|
||||
-- theorem dot_comm (xs ys : IntList) : dot xs ys = dot ys xs := by
|
||||
-- rw [dot, dot, mul_comm]
|
||||
|
||||
@[simp] theorem dot_set_left (xs ys : IntList) (i : Nat) (z : Int) :
|
||||
dot (xs.set i z) ys = dot xs ys + (z - xs.get i) * ys.get i := by
|
||||
induction xs generalizing i ys with
|
||||
| nil =>
|
||||
induction i generalizing ys with
|
||||
| zero => cases ys <;> simp
|
||||
| succ i => cases ys <;> simp_all
|
||||
| cons x xs ih =>
|
||||
induction i generalizing ys with
|
||||
| zero =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys =>
|
||||
simp only [Nat.zero_eq, set_cons_zero, dot_cons₂, get_cons_zero, Int.sub_mul]
|
||||
rw [Int.add_right_comm, Int.add_comm (x * y), Int.sub_add_cancel]
|
||||
| succ i =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys => simp_all [Int.add_assoc]
|
||||
|
||||
theorem dot_distrib_left (xs ys zs : IntList) : (xs + ys).dot zs = xs.dot zs + ys.dot zs := by
|
||||
simp [dot, mul_distrib_left, sum_add]
|
||||
|
||||
@[simp] theorem dot_neg_left (xs ys : IntList) : (-xs).dot ys = -(xs.dot ys) := by
|
||||
simp [dot, mul_neg_left]
|
||||
|
||||
@[simp] theorem dot_smul_left (xs ys : IntList) (i : Int) : (i * xs).dot ys = i * xs.dot ys := by
|
||||
simp [dot]
|
||||
|
||||
theorem dot_of_left_zero (w : ∀ x, x ∈ xs → x = 0) : dot xs ys = 0 := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys =>
|
||||
rw [dot_cons₂, w x (by simp), ih]
|
||||
· simp
|
||||
· intro x m
|
||||
apply w
|
||||
exact List.mem_cons_of_mem _ m
|
||||
|
||||
/-- Division of an `IntList` by a integer. -/
|
||||
def sdiv (xs : IntList) (g : Int) : IntList := xs.map fun x => x / g
|
||||
|
||||
@[simp] theorem sdiv_nil : sdiv [] g = [] := rfl
|
||||
@[simp] theorem sdiv_cons : sdiv (x::xs) g = (x / g) :: sdiv xs g := rfl
|
||||
|
||||
/-- The gcd of the absolute values of the entries of an `IntList`. -/
|
||||
def gcd (xs : IntList) : Nat := xs.foldr (fun x g => Nat.gcd x.natAbs g) 0
|
||||
|
||||
@[simp] theorem gcd_nil : gcd [] = 0 := rfl
|
||||
@[simp] theorem gcd_cons : gcd (x :: xs) = Nat.gcd x.natAbs (gcd xs) := rfl
|
||||
|
||||
theorem gcd_cons_div_left : (gcd (x::xs) : Int) ∣ x := by
|
||||
simp only [gcd, List.foldr_cons, Int.ofNat_dvd_left]
|
||||
apply Nat.gcd_dvd_left
|
||||
|
||||
theorem gcd_cons_div_right : gcd (x::xs) ∣ gcd xs := by
|
||||
simp only [gcd, List.foldr_cons]
|
||||
apply Nat.gcd_dvd_right
|
||||
|
||||
theorem gcd_cons_div_right' : (gcd (x::xs) : Int) ∣ (gcd xs : Int) := by
|
||||
rw [Int.ofNat_dvd_left, Int.natAbs_ofNat]
|
||||
exact gcd_cons_div_right
|
||||
|
||||
theorem gcd_dvd (xs : IntList) {a : Int} (m : a ∈ xs) : (xs.gcd : Int) ∣ a := by
|
||||
rw [Int.ofNat_dvd_left]
|
||||
induction m with
|
||||
| head =>
|
||||
simp only [gcd_cons]
|
||||
apply Nat.gcd_dvd_left
|
||||
| tail b m ih => -- FIXME: why is the argument of tail implicit?
|
||||
simp only [gcd_cons]
|
||||
exact Nat.dvd_trans (Nat.gcd_dvd_right _ _) ih
|
||||
|
||||
theorem dvd_gcd (xs : IntList) (c : Nat) (w : ∀ {a : Int}, a ∈ xs → (c : Int) ∣ a) :
|
||||
c ∣ xs.gcd := by
|
||||
simp only [Int.ofNat_dvd_left] at w
|
||||
induction xs with
|
||||
| nil => have := Nat.dvd_zero c; simp at this; exact this
|
||||
| cons x xs ih =>
|
||||
simp
|
||||
apply Nat.dvd_gcd
|
||||
· apply w
|
||||
simp
|
||||
· apply ih
|
||||
intro b m
|
||||
apply w
|
||||
exact List.mem_cons_of_mem x m
|
||||
|
||||
theorem gcd_eq_iff (xs : IntList) (g : Nat) :
|
||||
xs.gcd = g ↔
|
||||
(∀ {a : Int}, a ∈ xs → (g : Int) ∣ a) ∧
|
||||
(∀ (c : Nat), (∀ {a : Int}, a ∈ xs → (c : Int) ∣ a) → c ∣ g) := by
|
||||
constructor
|
||||
· rintro rfl
|
||||
exact ⟨gcd_dvd _, dvd_gcd _⟩
|
||||
· rintro ⟨hi, hg⟩
|
||||
apply Nat.dvd_antisymm
|
||||
· apply hg
|
||||
intro i m
|
||||
exact gcd_dvd xs m
|
||||
· exact dvd_gcd xs g hi
|
||||
|
||||
attribute [simp] Int.zero_dvd
|
||||
|
||||
@[simp] theorem gcd_eq_zero (xs : IntList) : xs.gcd = 0 ↔ ∀ x, x ∈ xs → x = 0 := by
|
||||
simp [gcd_eq_iff, Nat.dvd_zero]
|
||||
|
||||
@[simp] theorem dot_mod_gcd_left (xs ys : IntList) : dot xs ys % xs.gcd = 0 := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys =>
|
||||
rw [dot_cons₂, Int.add_emod,
|
||||
← Int.emod_emod_of_dvd (x * y) (gcd_cons_div_left),
|
||||
← Int.emod_emod_of_dvd (dot xs ys) (Int.ofNat_dvd.mpr gcd_cons_div_right)]
|
||||
simp_all
|
||||
|
||||
theorem gcd_dvd_dot_left (xs ys : IntList) : (xs.gcd : Int) ∣ dot xs ys :=
|
||||
Int.dvd_of_emod_eq_zero (dot_mod_gcd_left xs ys)
|
||||
|
||||
@[simp]
|
||||
theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : ∀ x, x ∈ xs → x = 0) : dot xs ys = 0 := by
|
||||
induction xs generalizing ys with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => rfl
|
||||
| cons y ys =>
|
||||
rw [dot_cons₂, h x (List.mem_cons_self _ _), ih (fun x m => h x (List.mem_cons_of_mem _ m)),
|
||||
Int.zero_mul, Int.add_zero]
|
||||
|
||||
theorem dot_sdiv_left (xs ys : IntList) {d : Int} (h : d ∣ xs.gcd) :
|
||||
dot (xs.sdiv d) ys = (dot xs ys) / d := by
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys =>
|
||||
have wx : d ∣ x := Int.dvd_trans h (gcd_cons_div_left)
|
||||
have wxy : d ∣ x * y := Int.dvd_trans wx (Int.dvd_mul_right x y)
|
||||
have w : d ∣ (IntList.gcd xs : Int) := Int.dvd_trans h (gcd_cons_div_right')
|
||||
simp_all [Int.add_ediv_of_dvd_left, Int.mul_ediv_assoc']
|
||||
|
||||
/-- Apply "balanced mod" to each entry in an `IntList`. -/
|
||||
abbrev bmod (x : IntList) (m : Nat) : IntList := x.map (Int.bmod · m)
|
||||
|
||||
theorem bmod_length (x : IntList) (m) : (bmod x m).length ≤ x.length :=
|
||||
Nat.le_of_eq (List.length_map _ _)
|
||||
|
||||
/--
|
||||
The difference between the balanced mod of a dot product,
|
||||
and the dot product with balanced mod applied to each entry of the left factor.
|
||||
-/
|
||||
abbrev bmod_dot_sub_dot_bmod (m : Nat) (a b : IntList) : Int :=
|
||||
(Int.bmod (dot a b) m) - dot (bmod a m) b
|
||||
|
||||
theorem dvd_bmod_dot_sub_dot_bmod (m : Nat) (xs ys : IntList) :
|
||||
(m : Int) ∣ bmod_dot_sub_dot_bmod m xs ys := by
|
||||
dsimp [bmod_dot_sub_dot_bmod]
|
||||
rw [Int.dvd_iff_emod_eq_zero]
|
||||
induction xs generalizing ys with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
cases ys with
|
||||
| nil => simp
|
||||
| cons y ys =>
|
||||
simp only [IntList.dot_cons₂, List.map_cons]
|
||||
specialize ih ys
|
||||
rw [Int.sub_emod, Int.bmod_emod] at ih
|
||||
rw [Int.sub_emod, Int.bmod_emod, Int.add_emod, Int.add_emod (Int.bmod x m * y),
|
||||
← Int.sub_emod, ← Int.sub_sub, Int.sub_eq_add_neg, Int.sub_eq_add_neg,
|
||||
Int.add_assoc (x * y % m), Int.add_comm (IntList.dot _ _ % m), ← Int.add_assoc,
|
||||
Int.add_assoc, ← Int.sub_eq_add_neg, ← Int.sub_eq_add_neg, Int.add_emod, ih, Int.add_zero,
|
||||
Int.emod_emod, Int.mul_emod, Int.mul_emod (Int.bmod x m), Int.bmod_emod, Int.sub_self,
|
||||
Int.zero_emod]
|
||||
|
||||
end IntList
|
||||
|
||||
end Lean.Omega
|
||||
178
src/Init/Omega/LinearCombo.lean
Normal file
178
src/Init/Omega/LinearCombo.lean
Normal file
@@ -0,0 +1,178 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Omega.Coeffs
|
||||
|
||||
/-!
|
||||
# Linear combinations
|
||||
|
||||
We use this data structure while processing hypotheses.
|
||||
|
||||
-/
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
/-- Internal representation of a linear combination of atoms, and a constant term. -/
|
||||
structure LinearCombo where
|
||||
/-- Constant term. -/
|
||||
const : Int := 0
|
||||
/-- Coefficients of the atoms. -/
|
||||
coeffs : Coeffs := []
|
||||
deriving DecidableEq, Repr
|
||||
|
||||
namespace LinearCombo
|
||||
|
||||
instance : ToString LinearCombo where
|
||||
toString lc :=
|
||||
s!"{lc.const}{String.join <| lc.coeffs.toList.enum.map fun ⟨i, c⟩ => s!" + {c} * x{i+1}"}"
|
||||
|
||||
instance : Inhabited LinearCombo := ⟨{const := 1}⟩
|
||||
|
||||
theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.coeffs) :
|
||||
a = b := by
|
||||
cases a; cases b
|
||||
subst w₁; subst w₂
|
||||
congr
|
||||
|
||||
/--
|
||||
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
|
||||
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.
|
||||
-/
|
||||
def eval (lc : LinearCombo) (values : Coeffs) : Int :=
|
||||
lc.const + lc.coeffs.dot values
|
||||
|
||||
@[simp] theorem eval_nil : (lc : LinearCombo).eval .nil = lc.const := by
|
||||
simp [eval]
|
||||
|
||||
/-- The `i`-th coordinate function. -/
|
||||
def coordinate (i : Nat) : LinearCombo where
|
||||
const := 0
|
||||
coeffs := Coeffs.set .nil i 1
|
||||
|
||||
@[simp] theorem coordinate_eval (i : Nat) (v : Coeffs) :
|
||||
(coordinate i).eval v = v.get i := by
|
||||
simp [eval, coordinate]
|
||||
|
||||
theorem coordinate_eval_0 : (coordinate 0).eval (.ofList (a0 :: t)) = a0 := by simp
|
||||
theorem coordinate_eval_1 : (coordinate 1).eval (.ofList (a0 :: a1 :: t)) = a1 := by simp
|
||||
theorem coordinate_eval_2 : (coordinate 2).eval (.ofList (a0 :: a1 :: a2 :: t)) = a2 := by simp
|
||||
theorem coordinate_eval_3 :
|
||||
(coordinate 3).eval (.ofList (a0 :: a1 :: a2 :: a3 :: t)) = a3 := by simp
|
||||
theorem coordinate_eval_4 :
|
||||
(coordinate 4).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: t)) = a4 := by simp
|
||||
theorem coordinate_eval_5 :
|
||||
(coordinate 5).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: t)) = a5 := by simp
|
||||
theorem coordinate_eval_6 :
|
||||
(coordinate 6).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: t)) = a6 := by simp
|
||||
theorem coordinate_eval_7 :
|
||||
(coordinate 7).eval
|
||||
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: t)) = a7 := by simp
|
||||
theorem coordinate_eval_8 :
|
||||
(coordinate 8).eval
|
||||
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: a8 :: t)) = a8 := by simp
|
||||
theorem coordinate_eval_9 :
|
||||
(coordinate 9).eval
|
||||
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: a8 :: a9 :: t)) = a9 := by simp
|
||||
|
||||
/-- Implementation of addition on `LinearCombo`. -/
|
||||
def add (l₁ l₂ : LinearCombo) : LinearCombo where
|
||||
const := l₁.const + l₂.const
|
||||
coeffs := l₁.coeffs + l₂.coeffs
|
||||
|
||||
instance : Add LinearCombo := ⟨add⟩
|
||||
|
||||
@[simp] theorem add_const {l₁ l₂ : LinearCombo} : (l₁ + l₂).const = l₁.const + l₂.const := rfl
|
||||
@[simp] theorem add_coeffs {l₁ l₂ : LinearCombo} : (l₁ + l₂).coeffs = l₁.coeffs + l₂.coeffs := rfl
|
||||
|
||||
/-- Implementation of subtraction on `LinearCombo`. -/
|
||||
def sub (l₁ l₂ : LinearCombo) : LinearCombo where
|
||||
const := l₁.const - l₂.const
|
||||
coeffs := l₁.coeffs - l₂.coeffs
|
||||
|
||||
instance : Sub LinearCombo := ⟨sub⟩
|
||||
|
||||
@[simp] theorem sub_const {l₁ l₂ : LinearCombo} : (l₁ - l₂).const = l₁.const - l₂.const := rfl
|
||||
@[simp] theorem sub_coeffs {l₁ l₂ : LinearCombo} : (l₁ - l₂).coeffs = l₁.coeffs - l₂.coeffs := rfl
|
||||
|
||||
/-- Implementation of negation on `LinearCombo`. -/
|
||||
def neg (lc : LinearCombo) : LinearCombo where
|
||||
const := -lc.const
|
||||
coeffs := -lc.coeffs
|
||||
|
||||
instance : Neg LinearCombo := ⟨neg⟩
|
||||
|
||||
@[simp] theorem neg_const {l : LinearCombo} : (-l).const = -l.const := rfl
|
||||
@[simp] theorem neg_coeffs {l : LinearCombo} : (-l).coeffs = -l.coeffs := rfl
|
||||
|
||||
theorem sub_eq_add_neg (l₁ l₂ : LinearCombo) : l₁ - l₂ = l₁ + -l₂ := by
|
||||
rcases l₁ with ⟨a₁, c₁⟩; rcases l₂ with ⟨a₂, c₂⟩
|
||||
apply ext
|
||||
· simp [Int.sub_eq_add_neg]
|
||||
· simp [Coeffs.sub_eq_add_neg]
|
||||
|
||||
/-- Implementation of scalar multiplication of a `LinearCombo` by an `Int`. -/
|
||||
def smul (lc : LinearCombo) (i : Int) : LinearCombo where
|
||||
const := i * lc.const
|
||||
coeffs := lc.coeffs.smul i
|
||||
|
||||
instance : HMul Int LinearCombo LinearCombo := ⟨fun i lc => lc.smul i⟩
|
||||
|
||||
@[simp] theorem smul_const {lc : LinearCombo} {i : Int} : (i * lc).const = i * lc.const := rfl
|
||||
@[simp] theorem smul_coeffs {lc : LinearCombo} {i : Int} : (i * lc).coeffs = i * lc.coeffs := rfl
|
||||
|
||||
@[simp] theorem add_eval (l₁ l₂ : LinearCombo) (v : Coeffs) :
|
||||
(l₁ + l₂).eval v = l₁.eval v + l₂.eval v := by
|
||||
rcases l₁ with ⟨r₁, c₁⟩; rcases l₂ with ⟨r₂, c₂⟩
|
||||
simp only [eval, add_const, add_coeffs, Int.add_assoc, Int.add_left_comm]
|
||||
congr
|
||||
exact Coeffs.dot_distrib_left c₁ c₂ v
|
||||
|
||||
@[simp] theorem neg_eval (lc : LinearCombo) (v : Coeffs) : (-lc).eval v = - lc.eval v := by
|
||||
rcases lc with ⟨a, coeffs⟩
|
||||
simp [eval, Int.neg_add]
|
||||
|
||||
@[simp] theorem sub_eval (l₁ l₂ : LinearCombo) (v : Coeffs) :
|
||||
(l₁ - l₂).eval v = l₁.eval v - l₂.eval v := by
|
||||
simp [sub_eq_add_neg, Int.sub_eq_add_neg]
|
||||
|
||||
@[simp] theorem smul_eval (lc : LinearCombo) (i : Int) (v : Coeffs) :
|
||||
(i * lc).eval v = i * lc.eval v := by
|
||||
rcases lc with ⟨a, coeffs⟩
|
||||
simp [eval, Int.mul_add]
|
||||
|
||||
theorem smul_eval_comm (lc : LinearCombo) (i : Int) (v : Coeffs) :
|
||||
(i * lc).eval v = lc.eval v * i := by
|
||||
simp [Int.mul_comm]
|
||||
|
||||
/--
|
||||
Multiplication of two linear combinations.
|
||||
This is useful only if at least one of the linear combinations is constant,
|
||||
and otherwise should be considered as a junk value.
|
||||
-/
|
||||
def mul (l₁ l₂ : LinearCombo) : LinearCombo :=
|
||||
l₂.const * l₁ + l₁.const * l₂ - { const := l₁.const * l₂.const }
|
||||
|
||||
theorem mul_eval_of_const_left (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₁.coeffs.isZero) :
|
||||
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
|
||||
have : Coeffs.dot l₁.coeffs v = 0 := IntList.dot_of_left_zero w
|
||||
simp [mul, eval, this, Coeffs.sub_eq_add_neg, Coeffs.dot_distrib_left, Int.add_mul, Int.mul_add,
|
||||
Int.mul_comm]
|
||||
|
||||
theorem mul_eval_of_const_right (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₂.coeffs.isZero) :
|
||||
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
|
||||
have : Coeffs.dot l₂.coeffs v = 0 := IntList.dot_of_left_zero w
|
||||
simp [mul, eval, this, Coeffs.sub_eq_add_neg, Coeffs.dot_distrib_left, Int.add_mul, Int.mul_add,
|
||||
Int.mul_comm]
|
||||
|
||||
theorem mul_eval (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₁.coeffs.isZero ∨ l₂.coeffs.isZero) :
|
||||
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
|
||||
rcases w with w | w
|
||||
· rw [mul_eval_of_const_left _ _ _ w]
|
||||
· rw [mul_eval_of_const_right _ _ _ w]
|
||||
|
||||
end LinearCombo
|
||||
|
||||
end Lean.Omega
|
||||
50
src/Init/Omega/Logic.lean
Normal file
50
src/Init/Omega/Logic.lean
Normal file
@@ -0,0 +1,50 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.PropLemmas
|
||||
/-!
|
||||
# Specializations of basic logic lemmas
|
||||
|
||||
These are useful for `omega` while constructing proofs, but not considered generally useful
|
||||
so are hidden in the `Lean.Omega` namespace.
|
||||
|
||||
If you find yourself needing them elsewhere, please move them first to another file.
|
||||
-/
|
||||
|
||||
namespace Lean.Omega
|
||||
|
||||
theorem and_not_not_of_not_or (h : ¬ (p ∨ q)) : ¬ p ∧ ¬ q := not_or.mp h
|
||||
|
||||
theorem Decidable.or_not_not_of_not_and [Decidable p] [Decidable q]
|
||||
(h : ¬ (p ∧ q)) : ¬ p ∨ ¬ q :=
|
||||
(Decidable.not_and_iff_or_not _ _).mp h
|
||||
|
||||
theorem Decidable.and_or_not_and_not_of_iff {p q : Prop} [Decidable q] (h : p ↔ q) :
|
||||
(p ∧ q) ∨ (¬p ∧ ¬q) := Decidable.iff_iff_and_or_not_and_not.mp h
|
||||
|
||||
theorem Decidable.not_iff_iff_and_not_or_not_and [Decidable a] [Decidable b] :
|
||||
(¬ (a ↔ b)) ↔ (a ∧ ¬ b) ∨ ((¬ a) ∧ b) :=
|
||||
⟨fun e => if hb : b then
|
||||
.inr ⟨fun ha => e ⟨fun _ => hb, fun _ => ha⟩, hb⟩
|
||||
else
|
||||
.inl ⟨if ha : a then ha else False.elim (e ⟨fun ha' => absurd ha' ha, fun hb' => absurd hb' hb⟩), hb⟩,
|
||||
Or.rec (And.rec fun ha nb w => nb (w.mp ha)) (And.rec fun na hb w => na (w.mpr hb))⟩
|
||||
|
||||
theorem Decidable.and_not_or_not_and_of_not_iff [Decidable a] [Decidable b]
|
||||
(h : ¬ (a ↔ b)) : a ∧ ¬b ∨ ¬a ∧ b :=
|
||||
Decidable.not_iff_iff_and_not_or_not_and.mp h
|
||||
|
||||
theorem Decidable.and_not_of_not_imp [Decidable a] (h : ¬(a → b)) : a ∧ ¬b :=
|
||||
Decidable.not_imp_iff_and_not.mp h
|
||||
|
||||
theorem ite_disjunction {α : Type u} {P : Prop} [Decidable P] {a b : α} :
|
||||
(P ∧ (if P then a else b) = a) ∨ (¬ P ∧ (if P then a else b) = b) :=
|
||||
if h : P then
|
||||
.inl ⟨h, if_pos h⟩
|
||||
else
|
||||
.inr ⟨h, if_neg h⟩
|
||||
|
||||
end Lean.Omega
|
||||
@@ -548,6 +548,11 @@ theorem Or.elim {c : Prop} (h : Or a b) (left : a → c) (right : b → c) : c :
|
||||
| Or.inl h => left h
|
||||
| Or.inr h => right h
|
||||
|
||||
theorem Or.resolve_left (h: Or a b) (na : Not a) : b := h.elim (absurd · na) id
|
||||
theorem Or.resolve_right (h: Or a b) (nb : Not b) : a := h.elim id (absurd · nb)
|
||||
theorem Or.neg_resolve_left (h : Or (Not a) b) (ha : a) : b := h.elim (absurd ha) id
|
||||
theorem Or.neg_resolve_right (h : Or a (Not b)) (nb : b) : a := h.elim id (absurd nb)
|
||||
|
||||
/--
|
||||
`Bool` is the type of boolean values, `true` and `false`. Classically,
|
||||
this is equivalent to `Prop` (the type of propositions), but the distinction
|
||||
@@ -942,7 +947,8 @@ return `t` or `e` depending on whether `c` is true or false. The explicit argume
|
||||
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
|
||||
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
|
||||
that `c` is true/false.
|
||||
|
||||
-/
|
||||
/-
|
||||
Because Lean uses a strict (call-by-value) evaluation strategy, the signature of this
|
||||
function is problematic in that it would require `t` and `e` to be evaluated before
|
||||
calling the `ite` function, which would cause both sides of the `if` to be evaluated.
|
||||
@@ -1629,8 +1635,8 @@ instance : LT Nat where
|
||||
lt := Nat.lt
|
||||
|
||||
theorem Nat.not_succ_le_zero : ∀ (n : Nat), LE.le (succ n) 0 → False
|
||||
| 0, h => nomatch h
|
||||
| succ _, h => nomatch h
|
||||
| 0 => nofun
|
||||
| succ _ => nofun
|
||||
|
||||
theorem Nat.not_lt_zero (n : Nat) : Not (LT.lt n 0) :=
|
||||
not_succ_le_zero n
|
||||
@@ -1809,6 +1815,8 @@ structure Fin (n : Nat) where
|
||||
/-- If `i : Fin n`, then `i.2` is a proof that `i.1 < n`. -/
|
||||
isLt : LT.lt val n
|
||||
|
||||
attribute [coe] Fin.val
|
||||
|
||||
theorem Fin.eq_of_val_eq {n} : ∀ {i j : Fin n}, Eq i.val j.val → Eq i j
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
@@ -2373,6 +2381,9 @@ Codepoint positions (counting the Unicode codepoints rather than bytes)
|
||||
are represented by plain `Nat`s instead.
|
||||
Indexing a `String` by a byte position is constant-time, while codepoint
|
||||
positions need to be translated internally to byte positions in linear-time.
|
||||
|
||||
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.endPos` and `p`
|
||||
lies on a UTF8 byte boundary.
|
||||
-/
|
||||
structure String.Pos where
|
||||
/-- Get the underlying byte index of a `String.Pos` -/
|
||||
|
||||
562
src/Init/PropLemmas.lean
Normal file
562
src/Init/PropLemmas.lean
Normal file
@@ -0,0 +1,562 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Jeremy Avigad, Floris van Doorn, Mario Carneiro
|
||||
|
||||
This provides additional lemmas about propositional types beyond what is
|
||||
needed for Core and SimpLemmas.
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
import Init.NotationExtra
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
/-! ## cast and equality -/
|
||||
|
||||
@[simp] theorem eq_mp_eq_cast (h : α = β) : Eq.mp h = cast h := rfl
|
||||
@[simp] theorem eq_mpr_eq_cast (h : α = β) : Eq.mpr h = cast h.symm := rfl
|
||||
|
||||
@[simp] theorem cast_cast : ∀ (ha : α = β) (hb : β = γ) (a : α),
|
||||
cast hb (cast ha a) = cast (ha.trans hb) a
|
||||
| rfl, rfl, _ => rfl
|
||||
|
||||
@[simp] theorem eq_true_eq_id : Eq True = id := by
|
||||
funext _; simp only [true_iff, id.def, eq_iff_iff]
|
||||
|
||||
/-! ## not -/
|
||||
|
||||
theorem not_not_em (a : Prop) : ¬¬(a ∨ ¬a) := fun h => h (.inr (h ∘ .inl))
|
||||
|
||||
/-! ## and -/
|
||||
|
||||
theorem and_self_iff : a ∧ a ↔ a := Iff.of_eq (and_self a)
|
||||
theorem and_not_self_iff (a : Prop) : a ∧ ¬a ↔ False := iff_false_intro and_not_self
|
||||
theorem not_and_self_iff (a : Prop) : ¬a ∧ a ↔ False := iff_false_intro not_and_self
|
||||
|
||||
theorem And.imp (f : a → c) (g : b → d) (h : a ∧ b) : c ∧ d := And.intro (f h.left) (g h.right)
|
||||
theorem And.imp_left (h : a → b) : a ∧ c → b ∧ c := .imp h id
|
||||
theorem And.imp_right (h : a → b) : c ∧ a → c ∧ b := .imp id h
|
||||
|
||||
theorem and_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : a ∧ b ↔ c ∧ d :=
|
||||
Iff.intro (And.imp h₁.mp h₂.mp) (And.imp h₁.mpr h₂.mpr)
|
||||
theorem and_congr_left' (h : a ↔ b) : a ∧ c ↔ b ∧ c := and_congr h .rfl
|
||||
theorem and_congr_right' (h : b ↔ c) : a ∧ b ↔ a ∧ c := and_congr .rfl h
|
||||
|
||||
theorem not_and_of_not_left (b : Prop) : ¬a → ¬(a ∧ b) := mt And.left
|
||||
theorem not_and_of_not_right (a : Prop) {b : Prop} : ¬b → ¬(a ∧ b) := mt And.right
|
||||
|
||||
theorem and_congr_right_eq (h : a → b = c) : (a ∧ b) = (a ∧ c) :=
|
||||
propext (and_congr_right (Iff.of_eq ∘ h))
|
||||
theorem and_congr_left_eq (h : c → a = b) : (a ∧ c) = (b ∧ c) :=
|
||||
propext (and_congr_left (Iff.of_eq ∘ h))
|
||||
|
||||
theorem and_left_comm : a ∧ b ∧ c ↔ b ∧ a ∧ c :=
|
||||
Iff.intro (fun ⟨ha, hb, hc⟩ => ⟨hb, ha, hc⟩)
|
||||
(fun ⟨hb, ha, hc⟩ => ⟨ha, hb, hc⟩)
|
||||
|
||||
theorem and_right_comm : (a ∧ b) ∧ c ↔ (a ∧ c) ∧ b :=
|
||||
Iff.intro (fun ⟨⟨ha, hb⟩, hc⟩ => ⟨⟨ha, hc⟩, hb⟩)
|
||||
(fun ⟨⟨ha, hc⟩, hb⟩ => ⟨⟨ha, hb⟩, hc⟩)
|
||||
|
||||
theorem and_rotate : a ∧ b ∧ c ↔ b ∧ c ∧ a := by rw [and_left_comm, @and_comm a c]
|
||||
theorem and_and_and_comm : (a ∧ b) ∧ c ∧ d ↔ (a ∧ c) ∧ b ∧ d := by rw [← and_assoc, @and_right_comm a, and_assoc]
|
||||
theorem and_and_left : a ∧ (b ∧ c) ↔ (a ∧ b) ∧ a ∧ c := by rw [and_and_and_comm, and_self]
|
||||
theorem and_and_right : (a ∧ b) ∧ c ↔ (a ∧ c) ∧ b ∧ c := by rw [and_and_and_comm, and_self]
|
||||
|
||||
theorem and_iff_left (hb : b) : a ∧ b ↔ a := Iff.intro And.left (And.intro · hb)
|
||||
theorem and_iff_right (ha : a) : a ∧ b ↔ b := Iff.intro And.right (And.intro ha ·)
|
||||
|
||||
/-! ## or -/
|
||||
|
||||
theorem or_self_iff : a ∨ a ↔ a := or_self _ ▸ .rfl
|
||||
theorem not_or_intro {a b : Prop} (ha : ¬a) (hb : ¬b) : ¬(a ∨ b) := (·.elim ha hb)
|
||||
|
||||
theorem or_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : (a ∨ b) ↔ (c ∨ d) := ⟨.imp h₁.mp h₂.mp, .imp h₁.mpr h₂.mpr⟩
|
||||
theorem or_congr_left (h : a ↔ b) : a ∨ c ↔ b ∨ c := or_congr h .rfl
|
||||
theorem or_congr_right (h : b ↔ c) : a ∨ b ↔ a ∨ c := or_congr .rfl h
|
||||
|
||||
theorem or_left_comm : a ∨ (b ∨ c) ↔ b ∨ (a ∨ c) := by rw [← or_assoc, ← or_assoc, @or_comm a b]
|
||||
theorem or_right_comm : (a ∨ b) ∨ c ↔ (a ∨ c) ∨ b := by rw [or_assoc, or_assoc, @or_comm b]
|
||||
|
||||
theorem or_or_or_comm : (a ∨ b) ∨ c ∨ d ↔ (a ∨ c) ∨ b ∨ d := by rw [← or_assoc, @or_right_comm a, or_assoc]
|
||||
|
||||
theorem or_or_distrib_left : a ∨ b ∨ c ↔ (a ∨ b) ∨ a ∨ c := by rw [or_or_or_comm, or_self]
|
||||
theorem or_or_distrib_right : (a ∨ b) ∨ c ↔ (a ∨ c) ∨ b ∨ c := by rw [or_or_or_comm, or_self]
|
||||
|
||||
theorem or_rotate : a ∨ b ∨ c ↔ b ∨ c ∨ a := by simp only [or_left_comm, Or.comm]
|
||||
|
||||
theorem or_iff_left (hb : ¬b) : a ∨ b ↔ a := or_iff_left_iff_imp.mpr hb.elim
|
||||
theorem or_iff_right (ha : ¬a) : a ∨ b ↔ b := or_iff_right_iff_imp.mpr ha.elim
|
||||
|
||||
/-! ## distributivity -/
|
||||
|
||||
theorem not_imp_of_and_not : a ∧ ¬b → ¬(a → b)
|
||||
| ⟨ha, hb⟩, h => hb <| h ha
|
||||
|
||||
theorem imp_and {α} : (α → b ∧ c) ↔ (α → b) ∧ (α → c) :=
|
||||
⟨fun h => ⟨fun ha => (h ha).1, fun ha => (h ha).2⟩, fun h ha => ⟨h.1 ha, h.2 ha⟩⟩
|
||||
|
||||
theorem not_and' : ¬(a ∧ b) ↔ b → ¬a := Iff.trans not_and imp_not_comm
|
||||
|
||||
/-- `∧` distributes over `∨` (on the left). -/
|
||||
theorem and_or_left : a ∧ (b ∨ c) ↔ (a ∧ b) ∨ (a ∧ c) :=
|
||||
Iff.intro (fun ⟨ha, hbc⟩ => hbc.imp (.intro ha) (.intro ha))
|
||||
(Or.rec (.imp_right .inl) (.imp_right .inr))
|
||||
|
||||
/-- `∧` distributes over `∨` (on the right). -/
|
||||
theorem or_and_right : (a ∨ b) ∧ c ↔ (a ∧ c) ∨ (b ∧ c) := by rw [@and_comm (a ∨ b), and_or_left, @and_comm c, @and_comm c]
|
||||
|
||||
/-- `∨` distributes over `∧` (on the left). -/
|
||||
theorem or_and_left : a ∨ (b ∧ c) ↔ (a ∨ b) ∧ (a ∨ c) :=
|
||||
Iff.intro (Or.rec (fun ha => ⟨.inl ha, .inl ha⟩) (.imp .inr .inr))
|
||||
(And.rec <| .rec (fun _ => .inl ·) (.imp_right ∘ .intro))
|
||||
|
||||
/-- `∨` distributes over `∧` (on the right). -/
|
||||
theorem and_or_right : (a ∧ b) ∨ c ↔ (a ∨ c) ∧ (b ∨ c) := by rw [@or_comm (a ∧ b), or_and_left, @or_comm c, @or_comm c]
|
||||
|
||||
theorem or_imp : (a ∨ b → c) ↔ (a → c) ∧ (b → c) :=
|
||||
Iff.intro (fun h => ⟨h ∘ .inl, h ∘ .inr⟩) (fun ⟨ha, hb⟩ => Or.rec ha hb)
|
||||
|
||||
/-
|
||||
`not_or` is made simp for confluence with `¬((b || c) = true)`:
|
||||
|
||||
Critical pair:
|
||||
1. `¬(b = true ∨ c = true)` via `Bool.or_eq_true`.
|
||||
2. `(b || c = false)` via `Bool.not_eq_true` which then
|
||||
reduces to `b = false ∧ c = false` via Mathlib simp lemma
|
||||
`Bool.or_eq_false_eq_eq_false_and_eq_false`.
|
||||
|
||||
Both reduce to `b = false ∧ c = false` via `not_or`.
|
||||
-/
|
||||
@[simp] theorem not_or : ¬(p ∨ q) ↔ ¬p ∧ ¬q := or_imp
|
||||
|
||||
theorem not_and_of_not_or_not (h : ¬a ∨ ¬b) : ¬(a ∧ b) := h.elim (mt (·.1)) (mt (·.2))
|
||||
|
||||
|
||||
/-! ## Ite -/
|
||||
|
||||
@[simp]
|
||||
theorem if_false_left [h : Decidable p] :
|
||||
ite p False q ↔ ¬p ∧ q := by cases h <;> (rename_i g; simp [g])
|
||||
|
||||
@[simp]
|
||||
theorem if_false_right [h : Decidable p] :
|
||||
ite p q False ↔ p ∧ q := by cases h <;> (rename_i g; simp [g])
|
||||
|
||||
/-
|
||||
`if_true_left` and `if_true_right` are lower priority because
|
||||
they introduce disjunctions and we prefer `if_false_left` and
|
||||
`if_false_right` if they overlap.
|
||||
-/
|
||||
|
||||
@[simp low]
|
||||
theorem if_true_left [h : Decidable p] :
|
||||
ite p True q ↔ ¬p → q := by cases h <;> (rename_i g; simp [g])
|
||||
|
||||
@[simp low]
|
||||
theorem if_true_right [h : Decidable p] :
|
||||
ite p q True ↔ p → q := by cases h <;> (rename_i g; simp [g])
|
||||
|
||||
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
|
||||
@[simp] theorem dite_not [hn : Decidable (¬p)] [h : Decidable p] (x : ¬p → α) (y : ¬¬p → α) :
|
||||
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
|
||||
cases h <;> (rename_i g; simp [g])
|
||||
|
||||
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
|
||||
@[simp] theorem ite_not (p : Prop) [Decidable p] (x y : α) : ite (¬p) x y = ite p y x :=
|
||||
dite_not (fun _ => x) (fun _ => y)
|
||||
|
||||
@[simp] theorem ite_true_same (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p → q) := by
|
||||
cases h <;> (rename_i g; simp [g])
|
||||
|
||||
@[simp] theorem ite_false_same (p q : Prop) [h : Decidable p] : (if p then q else p) = (p ∧ q) := by
|
||||
cases h <;> (rename_i g; simp [g])
|
||||
|
||||
/-! ## exists and forall -/
|
||||
|
||||
section quantifiers
|
||||
variable {p q : α → Prop} {b : Prop}
|
||||
|
||||
theorem forall_imp (h : ∀ a, p a → q a) : (∀ a, p a) → ∀ a, q a := fun h' a => h a (h' a)
|
||||
|
||||
/--
|
||||
As `simp` does not index foralls, this `@[simp]` lemma is tried on every `forall` expression.
|
||||
This is not ideal, and likely a performance issue, but it is difficult to remove this attribute at this time.
|
||||
-/
|
||||
@[simp] theorem forall_exists_index {q : (∃ x, p x) → Prop} :
|
||||
(∀ h, q h) ↔ ∀ x (h : p x), q ⟨x, h⟩ :=
|
||||
⟨fun h x hpx => h ⟨x, hpx⟩, fun h ⟨x, hpx⟩ => h x hpx⟩
|
||||
|
||||
theorem Exists.imp (h : ∀ a, p a → q a) : (∃ a, p a) → ∃ a, q a
|
||||
| ⟨a, hp⟩ => ⟨a, h a hp⟩
|
||||
|
||||
theorem Exists.imp' {β} {q : β → Prop} (f : α → β) (hpq : ∀ a, p a → q (f a)) :
|
||||
(∃ a, p a) → ∃ b, q b
|
||||
| ⟨_, hp⟩ => ⟨_, hpq _ hp⟩
|
||||
|
||||
theorem exists_imp : ((∃ x, p x) → b) ↔ ∀ x, p x → b := forall_exists_index
|
||||
|
||||
@[simp] theorem exists_const (α) [i : Nonempty α] : (∃ _ : α, b) ↔ b :=
|
||||
⟨fun ⟨_, h⟩ => h, i.elim Exists.intro⟩
|
||||
|
||||
section forall_congr
|
||||
|
||||
theorem forall_congr' (h : ∀ a, p a ↔ q a) : (∀ a, p a) ↔ ∀ a, q a :=
|
||||
⟨fun H a => (h a).1 (H a), fun H a => (h a).2 (H a)⟩
|
||||
|
||||
theorem exists_congr (h : ∀ a, p a ↔ q a) : (∃ a, p a) ↔ ∃ a, q a :=
|
||||
⟨Exists.imp fun x => (h x).1, Exists.imp fun x => (h x).2⟩
|
||||
|
||||
variable {β : α → Sort _}
|
||||
|
||||
theorem forall₂_congr {p q : ∀ a, β a → Prop} (h : ∀ a b, p a b ↔ q a b) :
|
||||
(∀ a b, p a b) ↔ ∀ a b, q a b :=
|
||||
forall_congr' fun a => forall_congr' <| h a
|
||||
|
||||
theorem exists₂_congr {p q : ∀ a, β a → Prop} (h : ∀ a b, p a b ↔ q a b) :
|
||||
(∃ a b, p a b) ↔ ∃ a b, q a b :=
|
||||
exists_congr fun a => exists_congr <| h a
|
||||
|
||||
variable {γ : ∀ a, β a → Sort _}
|
||||
theorem forall₃_congr {p q : ∀ a b, γ a b → Prop} (h : ∀ a b c, p a b c ↔ q a b c) :
|
||||
(∀ a b c, p a b c) ↔ ∀ a b c, q a b c :=
|
||||
forall_congr' fun a => forall₂_congr <| h a
|
||||
|
||||
theorem exists₃_congr {p q : ∀ a b, γ a b → Prop} (h : ∀ a b c, p a b c ↔ q a b c) :
|
||||
(∃ a b c, p a b c) ↔ ∃ a b c, q a b c :=
|
||||
exists_congr fun a => exists₂_congr <| h a
|
||||
|
||||
variable {δ : ∀ a b, γ a b → Sort _}
|
||||
theorem forall₄_congr {p q : ∀ a b c, δ a b c → Prop} (h : ∀ a b c d, p a b c d ↔ q a b c d) :
|
||||
(∀ a b c d, p a b c d) ↔ ∀ a b c d, q a b c d :=
|
||||
forall_congr' fun a => forall₃_congr <| h a
|
||||
|
||||
theorem exists₄_congr {p q : ∀ a b c, δ a b c → Prop} (h : ∀ a b c d, p a b c d ↔ q a b c d) :
|
||||
(∃ a b c d, p a b c d) ↔ ∃ a b c d, q a b c d :=
|
||||
exists_congr fun a => exists₃_congr <| h a
|
||||
|
||||
variable {ε : ∀ a b c, δ a b c → Sort _}
|
||||
theorem forall₅_congr {p q : ∀ a b c d, ε a b c d → Prop}
|
||||
(h : ∀ a b c d e, p a b c d e ↔ q a b c d e) :
|
||||
(∀ a b c d e, p a b c d e) ↔ ∀ a b c d e, q a b c d e :=
|
||||
forall_congr' fun a => forall₄_congr <| h a
|
||||
|
||||
theorem exists₅_congr {p q : ∀ a b c d, ε a b c d → Prop}
|
||||
(h : ∀ a b c d e, p a b c d e ↔ q a b c d e) :
|
||||
(∃ a b c d e, p a b c d e) ↔ ∃ a b c d e, q a b c d e :=
|
||||
exists_congr fun a => exists₄_congr <| h a
|
||||
|
||||
end forall_congr
|
||||
|
||||
@[simp] theorem not_exists : (¬∃ x, p x) ↔ ∀ x, ¬p x := exists_imp
|
||||
|
||||
theorem forall_and : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
|
||||
⟨fun h => ⟨fun x => (h x).1, fun x => (h x).2⟩, fun ⟨h₁, h₂⟩ x => ⟨h₁ x, h₂ x⟩⟩
|
||||
|
||||
theorem exists_or : (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ ∃ x, q x :=
|
||||
⟨fun | ⟨x, .inl h⟩ => .inl ⟨x, h⟩ | ⟨x, .inr h⟩ => .inr ⟨x, h⟩,
|
||||
fun | .inl ⟨x, h⟩ => ⟨x, .inl h⟩ | .inr ⟨x, h⟩ => ⟨x, .inr h⟩⟩
|
||||
|
||||
@[simp] theorem exists_false : ¬(∃ _a : α, False) := fun ⟨_, h⟩ => h
|
||||
|
||||
@[simp] theorem forall_const (α : Sort _) [i : Nonempty α] : (α → b) ↔ b :=
|
||||
⟨i.elim, fun hb _ => hb⟩
|
||||
|
||||
theorem Exists.nonempty : (∃ x, p x) → Nonempty α | ⟨x, _⟩ => ⟨x⟩
|
||||
|
||||
theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x, p x
|
||||
| ⟨x, hn⟩, h => hn (h x)
|
||||
|
||||
@[simp] theorem forall_eq {p : α → Prop} {a' : α} : (∀ a, a = a' → p a) ↔ p a' :=
|
||||
⟨fun h => h a' rfl, fun h _ e => e.symm ▸ h⟩
|
||||
|
||||
@[simp] theorem forall_eq' {a' : α} : (∀ a, a' = a → p a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem exists_eq : ∃ a, a = a' := ⟨_, rfl⟩
|
||||
|
||||
@[simp] theorem exists_eq' : ∃ a, a' = a := ⟨_, rfl⟩
|
||||
|
||||
@[simp] theorem exists_eq_left : (∃ a, a = a' ∧ p a) ↔ p a' :=
|
||||
⟨fun ⟨_, e, h⟩ => e ▸ h, fun h => ⟨_, rfl, h⟩⟩
|
||||
|
||||
@[simp] theorem exists_eq_right : (∃ a, p a ∧ a = a') ↔ p a' :=
|
||||
(exists_congr <| by exact fun a => And.comm).trans exists_eq_left
|
||||
|
||||
@[simp] theorem exists_and_left : (∃ x, b ∧ p x) ↔ b ∧ (∃ x, p x) :=
|
||||
⟨fun ⟨x, h, hp⟩ => ⟨h, x, hp⟩, fun ⟨h, x, hp⟩ => ⟨x, h, hp⟩⟩
|
||||
|
||||
@[simp] theorem exists_and_right : (∃ x, p x ∧ b) ↔ (∃ x, p x) ∧ b := by simp [And.comm]
|
||||
|
||||
@[simp] theorem exists_eq_left' : (∃ a, a' = a ∧ p a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem forall_eq_or_imp : (∀ a, a = a' ∨ q a → p a) ↔ p a' ∧ ∀ a, q a → p a := by
|
||||
simp only [or_imp, forall_and, forall_eq]
|
||||
|
||||
@[simp] theorem exists_eq_or_imp : (∃ a, (a = a' ∨ q a) ∧ p a) ↔ p a' ∨ ∃ a, q a ∧ p a := by
|
||||
simp only [or_and_right, exists_or, exists_eq_left]
|
||||
|
||||
@[simp] theorem exists_eq_right_right : (∃ (a : α), p a ∧ q a ∧ a = a') ↔ p a' ∧ q a' := by
|
||||
simp [← and_assoc]
|
||||
|
||||
@[simp] theorem exists_eq_right_right' : (∃ (a : α), p a ∧ q a ∧ a' = a) ↔ p a' ∧ q a' := by
|
||||
simp [@eq_comm _ a']
|
||||
|
||||
@[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⟩
|
||||
|
||||
theorem forall_prop_of_true {p : Prop} {q : p → Prop} (h : p) : (∀ h' : p, q h') ↔ q h :=
|
||||
@forall_const (q h) p ⟨h⟩
|
||||
|
||||
theorem forall_comm {p : α → β → Prop} : (∀ a b, p a b) ↔ (∀ b a, p a b) :=
|
||||
⟨fun h b a => h a b, fun h a b => h b a⟩
|
||||
|
||||
theorem exists_comm {p : α → β → Prop} : (∃ a b, p a b) ↔ (∃ b a, p a b) :=
|
||||
⟨fun ⟨a, b, h⟩ => ⟨b, a, h⟩, fun ⟨b, a, h⟩ => ⟨a, b, h⟩⟩
|
||||
|
||||
@[simp] theorem forall_apply_eq_imp_iff {f : α → β} {p : β → Prop} :
|
||||
(∀ b a, f a = b → p b) ↔ ∀ a, p (f a) := by simp [forall_comm]
|
||||
|
||||
@[simp] theorem forall_eq_apply_imp_iff {f : α → β} {p : β → Prop} :
|
||||
(∀ b a, b = f a → p b) ↔ ∀ a, p (f a) := by simp [forall_comm]
|
||||
|
||||
@[simp] theorem forall_apply_eq_imp_iff₂ {f : α → β} {p : α → Prop} {q : β → Prop} :
|
||||
(∀ b a, p a → f a = b → q b) ↔ ∀ a, p a → q (f a) :=
|
||||
⟨fun h a ha => h (f a) a ha rfl, fun h _ a ha hb => hb ▸ h a ha⟩
|
||||
|
||||
theorem forall_prop_of_false {p : Prop} {q : p → Prop} (hn : ¬p) : (∀ h' : p, q h') ↔ True :=
|
||||
iff_true_intro fun h => hn.elim h
|
||||
|
||||
end quantifiers
|
||||
|
||||
/-! ## decidable -/
|
||||
|
||||
@[simp] theorem Decidable.not_not [Decidable p] : ¬¬p ↔ p := ⟨of_not_not, not_not_intro⟩
|
||||
|
||||
/-- Excluded middle. Added as alias for Decidable.em -/
|
||||
abbrev Decidable.or_not_self := em
|
||||
|
||||
/-- Excluded middle commuted. Added as alias for Decidable.em -/
|
||||
theorem Decidable.not_or_self (p : Prop) [h : Decidable p] : ¬p ∨ p := by
|
||||
cases h <;> simp [*]
|
||||
|
||||
theorem Decidable.by_contra [Decidable p] : (¬p → False) → p := of_not_not
|
||||
|
||||
/-- Construct a non-Prop by cases on an `Or`, when the left conjunct is decidable. -/
|
||||
protected def Or.by_cases [Decidable p] {α : Sort u} (h : p ∨ q) (h₁ : p → α) (h₂ : q → α) : α :=
|
||||
if hp : p then h₁ hp else h₂ (h.resolve_left hp)
|
||||
|
||||
/-- Construct a non-Prop by cases on an `Or`, when the right conjunct is decidable. -/
|
||||
protected def Or.by_cases' [Decidable q] {α : Sort u} (h : p ∨ q) (h₁ : p → α) (h₂ : q → α) : α :=
|
||||
if hq : q then h₂ hq else h₁ (h.resolve_right hq)
|
||||
|
||||
instance exists_prop_decidable {p} (P : p → Prop)
|
||||
[Decidable p] [∀ h, Decidable (P h)] : Decidable (∃ h, P h) :=
|
||||
if h : p then
|
||||
decidable_of_decidable_of_iff ⟨fun h2 => ⟨h, h2⟩, fun ⟨_, h2⟩ => h2⟩
|
||||
else isFalse fun ⟨h', _⟩ => h h'
|
||||
|
||||
instance forall_prop_decidable {p} (P : p → Prop)
|
||||
[Decidable p] [∀ h, Decidable (P h)] : Decidable (∀ h, P h) :=
|
||||
if h : p then
|
||||
decidable_of_decidable_of_iff ⟨fun h2 _ => h2, fun al => al h⟩
|
||||
else isTrue fun h2 => absurd h2 h
|
||||
|
||||
theorem decide_eq_true_iff (p : Prop) [Decidable p] : (decide p = true) ↔ p := by simp
|
||||
|
||||
@[simp] theorem decide_eq_false_iff_not (p : Prop) {_ : Decidable p} : (decide p = false) ↔ ¬p :=
|
||||
⟨of_decide_eq_false, decide_eq_false⟩
|
||||
|
||||
@[simp] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
|
||||
decide p = decide q ↔ (p ↔ q) :=
|
||||
⟨fun h => by rw [← decide_eq_true_iff p, h, decide_eq_true_iff], fun h => by simp [h]⟩
|
||||
|
||||
theorem Decidable.of_not_imp [Decidable a] (h : ¬(a → b)) : a :=
|
||||
byContradiction (not_not_of_not_imp h)
|
||||
|
||||
theorem Decidable.not_imp_symm [Decidable a] (h : ¬a → b) (hb : ¬b) : a :=
|
||||
byContradiction <| hb ∘ h
|
||||
|
||||
theorem Decidable.not_imp_comm [Decidable a] [Decidable b] : (¬a → b) ↔ (¬b → a) :=
|
||||
⟨not_imp_symm, not_imp_symm⟩
|
||||
|
||||
@[simp] theorem Decidable.not_imp_self [Decidable a] : (¬a → a) ↔ a := by
|
||||
have := @imp_not_self (¬a); rwa [not_not] at this
|
||||
|
||||
theorem Decidable.or_iff_not_imp_left [Decidable a] : a ∨ b ↔ (¬a → b) :=
|
||||
⟨Or.resolve_left, fun h => dite _ .inl (.inr ∘ h)⟩
|
||||
|
||||
theorem Decidable.or_iff_not_imp_right [Decidable b] : a ∨ b ↔ (¬b → a) :=
|
||||
or_comm.trans or_iff_not_imp_left
|
||||
|
||||
theorem Decidable.not_imp_not [Decidable a] : (¬a → ¬b) ↔ (b → a) :=
|
||||
⟨fun h hb => byContradiction (h · hb), mt⟩
|
||||
|
||||
theorem Decidable.not_or_of_imp [Decidable a] (h : a → b) : ¬a ∨ b :=
|
||||
if ha : a then .inr (h ha) else .inl ha
|
||||
|
||||
theorem Decidable.imp_iff_not_or [Decidable a] : (a → b) ↔ (¬a ∨ b) :=
|
||||
⟨not_or_of_imp, Or.neg_resolve_left⟩
|
||||
|
||||
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) :=
|
||||
if h : a then by
|
||||
rw [imp_iff_right h, imp_iff_right h, imp_iff_right h]
|
||||
else by
|
||||
rw [iff_false_intro h, false_imp_iff, false_imp_iff, true_or]
|
||||
|
||||
theorem Decidable.imp_or' [Decidable b] : (a → b ∨ c) ↔ (a → b) ∨ (a → c) :=
|
||||
if h : b then by simp [h] else by
|
||||
rw [eq_false h, false_or]; exact (or_iff_right_of_imp fun hx x => (hx x).elim).symm
|
||||
|
||||
theorem Decidable.not_imp_iff_and_not [Decidable a] : ¬(a → b) ↔ a ∧ ¬b :=
|
||||
⟨fun h => ⟨of_not_imp h, not_of_not_imp h⟩, not_imp_of_and_not⟩
|
||||
|
||||
theorem Decidable.peirce (a b : Prop) [Decidable a] : ((a → b) → a) → a :=
|
||||
if ha : a then fun _ => ha else fun h => h ha.elim
|
||||
|
||||
theorem peirce' {a : Prop} (H : ∀ b : Prop, (a → b) → a) : a := H _ id
|
||||
|
||||
theorem Decidable.not_iff_not [Decidable a] [Decidable b] : (¬a ↔ ¬b) ↔ (a ↔ b) := by
|
||||
rw [@iff_def (¬a), @iff_def' a]; exact and_congr not_imp_not not_imp_not
|
||||
|
||||
theorem Decidable.not_iff_comm [Decidable a] [Decidable b] : (¬a ↔ b) ↔ (¬b ↔ a) := by
|
||||
rw [@iff_def (¬a), @iff_def (¬b)]; exact and_congr not_imp_comm imp_not_comm
|
||||
|
||||
theorem Decidable.not_iff [Decidable b] : ¬(a ↔ b) ↔ (¬a ↔ b) :=
|
||||
if h : b then by
|
||||
rw [iff_true_right h, iff_true_right h]
|
||||
else by
|
||||
rw [iff_false_right h, iff_false_right h]
|
||||
|
||||
theorem Decidable.iff_not_comm [Decidable a] [Decidable b] : (a ↔ ¬b) ↔ (b ↔ ¬a) := by
|
||||
rw [@iff_def a, @iff_def b]; exact and_congr imp_not_comm not_imp_comm
|
||||
|
||||
theorem Decidable.iff_iff_and_or_not_and_not {a b : Prop} [Decidable b] :
|
||||
(a ↔ b) ↔ (a ∧ b) ∨ (¬a ∧ ¬b) :=
|
||||
⟨fun e => if h : b then .inl ⟨e.2 h, h⟩ else .inr ⟨mt e.1 h, h⟩,
|
||||
Or.rec (And.rec iff_of_true) (And.rec iff_of_false)⟩
|
||||
|
||||
theorem Decidable.iff_iff_not_or_and_or_not [Decidable a] [Decidable b] :
|
||||
(a ↔ b) ↔ (¬a ∨ b) ∧ (a ∨ ¬b) := by
|
||||
rw [iff_iff_implies_and_implies a b]; simp only [imp_iff_not_or, Or.comm]
|
||||
|
||||
theorem Decidable.not_and_not_right [Decidable b] : ¬(a ∧ ¬b) ↔ (a → b) :=
|
||||
⟨fun h ha => not_imp_symm (And.intro ha) h, fun h ⟨ha, hb⟩ => hb <| h ha⟩
|
||||
|
||||
theorem Decidable.not_and_iff_or_not_not [Decidable a] : ¬(a ∧ b) ↔ ¬a ∨ ¬b :=
|
||||
⟨fun h => if ha : a then .inr (h ⟨ha, ·⟩) else .inl ha, not_and_of_not_or_not⟩
|
||||
|
||||
theorem Decidable.not_and_iff_or_not_not' [Decidable b] : ¬(a ∧ b) ↔ ¬a ∨ ¬b :=
|
||||
⟨fun h => if hb : b then .inl (h ⟨·, hb⟩) else .inr hb, not_and_of_not_or_not⟩
|
||||
|
||||
theorem Decidable.or_iff_not_and_not [Decidable a] [Decidable b] : a ∨ b ↔ ¬(¬a ∧ ¬b) := by
|
||||
rw [← not_or, not_not]
|
||||
|
||||
theorem Decidable.and_iff_not_or_not [Decidable a] [Decidable b] : a ∧ b ↔ ¬(¬a ∨ ¬b) := by
|
||||
rw [← not_and_iff_or_not_not, not_not]
|
||||
|
||||
theorem Decidable.imp_iff_right_iff [Decidable a] : (a → b ↔ b) ↔ a ∨ b :=
|
||||
Iff.intro
|
||||
(fun h => (Decidable.em a).imp_right fun ha' => h.mp fun ha => (ha' ha).elim)
|
||||
(fun ab => ab.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb)
|
||||
|
||||
theorem Decidable.imp_iff_left_iff [Decidable a] : (b ↔ a → b) ↔ a ∨ b :=
|
||||
propext (@Iff.comm (a → b) b) ▸ (@Decidable.imp_iff_right_iff a b _)
|
||||
|
||||
theorem Decidable.and_or_imp [Decidable a] : a ∧ b ∨ (a → c) ↔ a → b ∨ c :=
|
||||
if ha : a then by simp only [ha, true_and, true_imp_iff]
|
||||
else by simp only [ha, false_or, false_and, false_imp_iff]
|
||||
|
||||
theorem Decidable.or_congr_left' [Decidable c] (h : ¬c → (a ↔ b)) : a ∨ c ↔ b ∨ c := by
|
||||
rw [or_iff_not_imp_right, or_iff_not_imp_right]; exact imp_congr_right h
|
||||
|
||||
theorem Decidable.or_congr_right' [Decidable a] (h : ¬a → (b ↔ c)) : a ∨ b ↔ a ∨ c := by
|
||||
rw [or_iff_not_imp_left, or_iff_not_imp_left]; exact imp_congr_right h
|
||||
|
||||
/-- Transfer decidability of `a` to decidability of `b`, if the propositions are equivalent.
|
||||
**Important**: this function should be used instead of `rw` on `Decidable b`, because the
|
||||
kernel will get stuck reducing the usage of `propext` otherwise,
|
||||
and `decide` will not work. -/
|
||||
@[inline] def decidable_of_iff (a : Prop) (h : a ↔ b) [Decidable a] : Decidable b :=
|
||||
decidable_of_decidable_of_iff h
|
||||
|
||||
/-- Transfer decidability of `b` to decidability of `a`, if the propositions are equivalent.
|
||||
This is the same as `decidable_of_iff` but the iff is flipped. -/
|
||||
@[inline] def decidable_of_iff' (b : Prop) (h : a ↔ b) [Decidable b] : Decidable a :=
|
||||
decidable_of_decidable_of_iff h.symm
|
||||
|
||||
instance Decidable.predToBool (p : α → Prop) [DecidablePred p] :
|
||||
CoeDep (α → Prop) p (α → Bool) := ⟨fun b => decide <| p b⟩
|
||||
|
||||
/-- Prove that `a` is decidable by constructing a boolean `b` and a proof that `b ↔ a`.
|
||||
(This is sometimes taken as an alternate definition of decidability.) -/
|
||||
def decidable_of_bool : ∀ (b : Bool), (b ↔ a) → Decidable a
|
||||
| true, h => isTrue (h.1 rfl)
|
||||
| false, h => isFalse (mt h.2 Bool.noConfusion)
|
||||
|
||||
protected theorem Decidable.not_forall {p : α → Prop} [Decidable (∃ x, ¬p x)]
|
||||
[∀ x, Decidable (p x)] : (¬∀ x, p x) ↔ ∃ x, ¬p x :=
|
||||
⟨Decidable.not_imp_symm fun nx x => Decidable.not_imp_symm (fun h => ⟨x, h⟩) nx,
|
||||
not_forall_of_exists_not⟩
|
||||
|
||||
protected theorem Decidable.not_forall_not {p : α → Prop} [Decidable (∃ x, p x)] :
|
||||
(¬∀ x, ¬p x) ↔ ∃ x, p x :=
|
||||
(@Decidable.not_iff_comm _ _ _ (decidable_of_iff (¬∃ x, p x) not_exists)).1 not_exists
|
||||
|
||||
protected theorem Decidable.not_exists_not {p : α → Prop} [∀ x, Decidable (p x)] :
|
||||
(¬∃ x, ¬p x) ↔ ∀ x, p x := by
|
||||
simp only [not_exists, Decidable.not_not]
|
||||
|
||||
export Decidable (not_imp_self)
|
||||
|
||||
/-
|
||||
`decide_implies` simp justification.
|
||||
|
||||
We have a critical pair from `decide (¬(p ∧ q))`:
|
||||
|
||||
1. `decide (p → ¬q)` via `not_and`
|
||||
2. `!decide (p ∧ q)` via `decide_not` This further refines to
|
||||
`!(decide p) || !(decide q)` via `Bool.decide_and` (in Mathlib) and
|
||||
`Bool.not_and` (made simp in Mathlib).
|
||||
|
||||
We introduce `decide_implies` below and then both normalize to
|
||||
`!(decide p) || !(decide q)`.
|
||||
-/
|
||||
@[simp]
|
||||
theorem decide_implies (u v : Prop)
|
||||
[duv : Decidable (u → v)] [du : Decidable u] {dv : u → Decidable v}
|
||||
: decide (u → v) = dite u (fun h => @decide v (dv h)) (fun _ => true) :=
|
||||
if h : u then by
|
||||
simp [h]
|
||||
else by
|
||||
simp [h]
|
||||
|
||||
/-
|
||||
`decide_ite` is needed to resolve critical pair with
|
||||
|
||||
We have a critical pair from `decide (ite p b c = true)`:
|
||||
|
||||
1. `ite p b c` via `decide_coe`
|
||||
2. `decide (ite p (b = true) (c = true))` via `Bool.ite_eq_true_distrib`.
|
||||
|
||||
We introduce `decide_ite` so both normalize to `ite p b c`.
|
||||
-/
|
||||
@[simp]
|
||||
theorem decide_ite (u : Prop) [du : Decidable u] (p q : Prop)
|
||||
[dpq : Decidable (ite u p q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
decide (ite u p q) = ite u (decide p) (decide q) := by
|
||||
cases du <;> simp [*]
|
||||
|
||||
/- Confluence for `ite_true_same` and `decide_ite`. -/
|
||||
@[simp] theorem ite_true_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
|
||||
(if p then decide p else b) = (decide p || b) := by
|
||||
cases h <;> (rename_i pt; simp [pt])
|
||||
|
||||
/- Confluence for `ite_false_same` and `decide_ite`. -/
|
||||
@[simp] theorem ite_false_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
|
||||
(if p then b else decide p) = (decide p && b) := by
|
||||
cases h <;> (rename_i pt; simp [pt])
|
||||
@@ -15,12 +15,15 @@ theorem of_eq_false (h : p = False) : ¬ p := fun hp => False.elim (h.mp hp)
|
||||
theorem eq_true (h : p) : p = True :=
|
||||
propext ⟨fun _ => trivial, fun _ => h⟩
|
||||
|
||||
-- Adding this attribute needs `eq_true`.
|
||||
attribute [simp] cast_heq
|
||||
|
||||
theorem eq_false (h : ¬ p) : p = False :=
|
||||
propext ⟨fun h' => absurd h' h, fun h' => False.elim h'⟩
|
||||
|
||||
theorem eq_false' (h : p → False) : p = False := eq_false h
|
||||
|
||||
theorem eq_true_of_decide {p : Prop} {_ : Decidable p} (h : decide p = true) : p = True :=
|
||||
theorem eq_true_of_decide {p : Prop} [Decidable p] (h : decide p = true) : p = True :=
|
||||
eq_true (of_decide_eq_true h)
|
||||
|
||||
theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) : p = False :=
|
||||
@@ -31,6 +34,9 @@ theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) :
|
||||
theorem implies_congr {p₁ p₂ : Sort u} {q₁ q₂ : Sort v} (h₁ : p₁ = p₂) (h₂ : q₁ = q₂) : (p₁ → q₁) = (p₂ → q₂) :=
|
||||
h₁ ▸ h₂ ▸ rfl
|
||||
|
||||
theorem iff_congr {p₁ p₂ q₁ q₂ : Prop} (h₁ : p₁ ↔ p₂) (h₂ : q₁ ↔ q₂) : (p₁ ↔ q₁) ↔ (p₂ ↔ q₂) :=
|
||||
Iff.of_eq (propext h₁ ▸ propext h₂ ▸ rfl)
|
||||
|
||||
theorem implies_dep_congr_ctx {p₁ p₂ q₁ : Prop} (h₁ : p₁ = p₂) {q₂ : p₂ → Prop} (h₂ : (h : p₂) → q₁ = q₂ h) : (p₁ → q₁) = ((h : p₂) → q₂ h) :=
|
||||
propext ⟨
|
||||
fun hl hp₂ => (h₂ hp₂).mp (hl (h₁.mpr hp₂)),
|
||||
@@ -81,6 +87,7 @@ theorem dite_congr {_ : Decidable b} [Decidable c]
|
||||
| inr h => rw [dif_neg h]; subst b; rw [dif_neg h]; exact h₃ h
|
||||
|
||||
@[simp] theorem ne_eq (a b : α) : (a ≠ b) = ¬(a = b) := rfl
|
||||
norm_cast_add_elim ne_eq
|
||||
@[simp] theorem ite_true (a b : α) : (if True then a else b) = a := rfl
|
||||
@[simp] theorem ite_false (a b : α) : (if False then a else b) = b := rfl
|
||||
@[simp] theorem dite_true {α : Sort u} {t : True → α} {e : ¬ True → α} : (dite True t e) = t True.intro := rfl
|
||||
@@ -93,11 +100,16 @@ theorem dite_cond_eq_true {α : Sort u} {c : Prop} {_ : Decidable c} {t : c →
|
||||
theorem dite_cond_eq_false {α : Sort u} {c : Prop} {_ : Decidable c} {t : c → α} {e : ¬ c → α} (h : c = False) : (dite c t e) = e (of_eq_false h) := by simp [h]
|
||||
end SimprocHelperLemmas
|
||||
@[simp] theorem ite_self {α : Sort u} {c : Prop} {d : Decidable c} (a : α) : ite c a a = a := by cases d <;> rfl
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext ⟨(·.1), fun h => ⟨h, h⟩⟩
|
||||
|
||||
@[simp] theorem and_true (p : Prop) : (p ∧ True) = p := propext ⟨(·.1), (⟨·, trivial⟩)⟩
|
||||
@[simp] theorem true_and (p : Prop) : (True ∧ p) = p := propext ⟨(·.2), (⟨trivial, ·⟩)⟩
|
||||
@[simp] theorem and_false (p : Prop) : (p ∧ False) = False := eq_false (·.2)
|
||||
@[simp] theorem false_and (p : Prop) : (False ∧ p) = False := eq_false (·.1)
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext ⟨(·.left), fun h => ⟨h, h⟩⟩
|
||||
@[simp] theorem and_not_self : ¬(a ∧ ¬a) | ⟨ha, hn⟩ => absurd ha hn
|
||||
@[simp] theorem not_and_self : ¬(¬a ∧ a) := and_not_self ∘ And.symm
|
||||
@[simp] theorem and_imp : (a ∧ b → c) ↔ (a → b → c) := ⟨fun h ha hb => h ⟨ha, hb⟩, fun h ⟨ha, hb⟩ => h ha hb⟩
|
||||
@[simp] theorem not_and : ¬(a ∧ b) ↔ (a → ¬b) := and_imp
|
||||
@[simp] theorem or_self (p : Prop) : (p ∨ p) = p := propext ⟨fun | .inl h | .inr h => h, .inl⟩
|
||||
@[simp] theorem or_true (p : Prop) : (p ∨ True) = True := eq_true (.inr trivial)
|
||||
@[simp] theorem true_or (p : Prop) : (True ∨ p) = True := eq_true (.inl trivial)
|
||||
@@ -114,6 +126,64 @@ end SimprocHelperLemmas
|
||||
@[simp] theorem not_false_eq_true : (¬ False) = True := eq_true False.elim
|
||||
@[simp] theorem not_true_eq_false : (¬ True) = False := by decide
|
||||
|
||||
@[simp] theorem not_iff_self : ¬(¬a ↔ a) | H => iff_not_self H.symm
|
||||
attribute [simp] iff_not_self
|
||||
|
||||
/-! ## and -/
|
||||
|
||||
theorem and_congr_right (h : a → (b ↔ c)) : a ∧ b ↔ a ∧ c :=
|
||||
Iff.intro (fun ⟨ha, hb⟩ => And.intro ha ((h ha).mp hb))
|
||||
(fun ⟨ha, hb⟩ => And.intro ha ((h ha).mpr hb))
|
||||
theorem and_congr_left (h : c → (a ↔ b)) : a ∧ c ↔ b ∧ c :=
|
||||
Iff.trans and_comm (Iff.trans (and_congr_right h) and_comm)
|
||||
|
||||
theorem and_assoc : (a ∧ b) ∧ c ↔ a ∧ (b ∧ c) :=
|
||||
Iff.intro (fun ⟨⟨ha, hb⟩, hc⟩ => ⟨ha, hb, hc⟩)
|
||||
(fun ⟨ha, hb, hc⟩ => ⟨⟨ha, hb⟩, hc⟩)
|
||||
|
||||
@[simp] theorem and_self_left : a ∧ (a ∧ b) ↔ a ∧ b := by rw [←propext and_assoc, and_self]
|
||||
@[simp] theorem and_self_right : (a ∧ b) ∧ b ↔ a ∧ b := by rw [ propext and_assoc, and_self]
|
||||
|
||||
@[simp] theorem and_congr_right_iff : (a ∧ b ↔ a ∧ c) ↔ (a → (b ↔ c)) :=
|
||||
Iff.intro (fun h ha => by simp [ha] at h; exact h) and_congr_right
|
||||
@[simp] theorem and_congr_left_iff : (a ∧ c ↔ b ∧ c) ↔ c → (a ↔ b) := by
|
||||
rw [@and_comm _ c, @and_comm _ c, ← and_congr_right_iff]
|
||||
|
||||
theorem and_iff_left_of_imp (h : a → b) : (a ∧ b) ↔ a := Iff.intro And.left (fun ha => And.intro ha (h ha))
|
||||
theorem and_iff_right_of_imp (h : b → a) : (a ∧ b) ↔ b := Iff.trans And.comm (and_iff_left_of_imp h)
|
||||
|
||||
@[simp] theorem and_iff_left_iff_imp : ((a ∧ b) ↔ a) ↔ (a → b) := Iff.intro (And.right ∘ ·.mpr) and_iff_left_of_imp
|
||||
@[simp] theorem and_iff_right_iff_imp : ((a ∧ b) ↔ b) ↔ (b → a) := Iff.intro (And.left ∘ ·.mpr) and_iff_right_of_imp
|
||||
|
||||
@[simp] theorem iff_self_and : (p ↔ p ∧ q) ↔ (p → q) := by rw [@Iff.comm p, and_iff_left_iff_imp]
|
||||
@[simp] theorem iff_and_self : (p ↔ q ∧ p) ↔ (p → q) := by rw [and_comm, iff_self_and]
|
||||
|
||||
/-! ## or -/
|
||||
|
||||
theorem Or.imp (f : a → c) (g : b → d) (h : a ∨ b) : c ∨ d := h.elim (inl ∘ f) (inr ∘ g)
|
||||
theorem Or.imp_left (f : a → b) : a ∨ c → b ∨ c := .imp f id
|
||||
theorem Or.imp_right (f : b → c) : a ∨ b → a ∨ c := .imp id f
|
||||
|
||||
theorem or_assoc : (a ∨ b) ∨ c ↔ a ∨ (b ∨ c) :=
|
||||
Iff.intro (.rec (.imp_right .inl) (.inr ∘ .inr))
|
||||
(.rec (.inl ∘ .inl) (.imp_left .inr))
|
||||
|
||||
@[simp] theorem or_self_left : a ∨ (a ∨ b) ↔ a ∨ b := by rw [←propext or_assoc, or_self]
|
||||
@[simp] theorem or_self_right : (a ∨ b) ∨ b ↔ a ∨ b := by rw [ propext or_assoc, or_self]
|
||||
|
||||
theorem or_iff_right_of_imp (ha : a → b) : (a ∨ b) ↔ b := Iff.intro (Or.rec ha id) .inr
|
||||
theorem or_iff_left_of_imp (hb : b → a) : (a ∨ b) ↔ a := Iff.intro (Or.rec id hb) .inl
|
||||
|
||||
@[simp] theorem or_iff_left_iff_imp : (a ∨ b ↔ a) ↔ (b → a) := Iff.intro (·.mp ∘ Or.inr) or_iff_left_of_imp
|
||||
@[simp] theorem or_iff_right_iff_imp : (a ∨ b ↔ b) ↔ (a → b) := by rw [or_comm, or_iff_left_iff_imp]
|
||||
|
||||
@[simp] theorem iff_self_or (a b : Prop) : (a ↔ a ∨ b) ↔ (b → a) :=
|
||||
propext (@Iff.comm _ a) ▸ @or_iff_left_iff_imp a b
|
||||
@[simp] theorem iff_or_self (a b : Prop) : (b ↔ a ∨ b) ↔ (a → b) :=
|
||||
propext (@Iff.comm _ b) ▸ @or_iff_right_iff_imp a b
|
||||
|
||||
/-# Bool -/
|
||||
|
||||
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_true (b : Bool) : (b || true) = true := by cases b <;> rfl
|
||||
@[simp] theorem Bool.false_or (b : Bool) : (false || b) = b := by cases b <;> rfl
|
||||
@@ -138,9 +208,9 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_true : (!true) = false := by decide
|
||||
@[simp] theorem Bool.not_false : (!false) = true := by decide
|
||||
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
|
||||
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
|
||||
@[simp] theorem Bool.not_eq_false' (b : Bool) : ((!b) = false) = (b = true) := by cases b <;> simp
|
||||
|
||||
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
|
||||
@@ -151,11 +221,14 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
|
||||
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
|
||||
|
||||
@[simp] theorem decide_eq_true_eq {_ : Decidable p} : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
|
||||
@[simp] theorem decide_not {h : Decidable p} : decide (¬ p) = !decide p := by cases h <;> rfl
|
||||
@[simp] theorem not_decide_eq_true {h : Decidable p} : ((!decide p) = true) = ¬ p := by cases h <;> simp [decide, *]
|
||||
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p :=
|
||||
propext <| Iff.intro of_decide_eq_true decide_eq_true
|
||||
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
|
||||
cases g <;> (rename_i gp; simp [gp]; rfl)
|
||||
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by
|
||||
cases h <;> (rename_i hp; simp [decide, hp])
|
||||
|
||||
@[simp] theorem heq_eq_eq {α : Sort u} (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
|
||||
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
|
||||
|
||||
@[simp] theorem cond_true (a b : α) : cond true a b = a := rfl
|
||||
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
|
||||
@@ -166,11 +239,31 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
@[simp] theorem bne_self_eq_false [BEq α] [LawfulBEq α] (a : α) : (a != a) = false := by simp [bne]
|
||||
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
|
||||
|
||||
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a ≤ 0) = (a = 0) :=
|
||||
propext ⟨fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by rw [h]; decide⟩
|
||||
|
||||
@[simp] theorem decide_False : decide False = false := rfl
|
||||
@[simp] theorem decide_True : decide True = true := rfl
|
||||
@[simp] theorem decide_True : decide True = true := rfl
|
||||
|
||||
@[simp] theorem bne_iff_ne [BEq α] [LawfulBEq α] (a b : α) : a != b ↔ a ≠ b := by
|
||||
simp [bne]; rw [← beq_iff_eq a b]; simp [-beq_iff_eq]
|
||||
|
||||
/-
|
||||
Added for critical pair for `¬((a != b) = true)`
|
||||
|
||||
1. `(a != b) = false` via `Bool.not_eq_true`
|
||||
2. `¬(a ≠ b)` via `bne_iff_ne`
|
||||
|
||||
These will both normalize to `a = b` with the first via `bne_eq_false_iff_eq`.
|
||||
-/
|
||||
@[simp] theorem beq_eq_false_iff_ne [BEq α] [LawfulBEq α]
|
||||
(a b : α) : (a == b) = false ↔ a ≠ b := by
|
||||
rw [ne_eq, ← beq_iff_eq a b]
|
||||
cases a == b <;> decide
|
||||
|
||||
@[simp] theorem bne_eq_false_iff_eq [BEq α] [LawfulBEq α] (a b : α) :
|
||||
(a != b) = false ↔ a = b := by
|
||||
rw [bne, ← beq_iff_eq a b]
|
||||
cases a == b <;> decide
|
||||
|
||||
/-# Nat -/
|
||||
|
||||
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a ≤ 0) = (a = 0) :=
|
||||
propext ⟨fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by rw [h]; decide⟩
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Chris Lovett
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.String.Extra
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.System.FilePath
|
||||
|
||||
namespace System
|
||||
|
||||
@@ -172,6 +172,19 @@ example (x : Nat) (h : x ≠ x) : p := by contradiction
|
||||
-/
|
||||
syntax (name := contradiction) "contradiction" : tactic
|
||||
|
||||
/--
|
||||
Changes the goal to `False`, retaining as much information as possible:
|
||||
|
||||
* If the goal is `False`, do nothing.
|
||||
* If the goal is an implication or a function type, introduce the argument and restart.
|
||||
(In particular, if the goal is `x ≠ y`, introduce `x = y`.)
|
||||
* Otherwise, for a propositional goal `P`, replace it with `¬ ¬ P`
|
||||
(attempting to find a `Decidable` instance, but otherwise falling back to working classically)
|
||||
and introduce `¬ P`.
|
||||
* For a non-propositional goal use `False.elim`.
|
||||
-/
|
||||
syntax (name := falseOrByContra) "false_or_by_contra" : tactic
|
||||
|
||||
/--
|
||||
`apply e` tries to match the current goal against the conclusion of `e`'s type.
|
||||
If it succeeds, then the tactic returns as many subgoals as the number of premises that
|
||||
@@ -201,12 +214,37 @@ and implicit parameters are also converted into new goals.
|
||||
-/
|
||||
syntax (name := refine') "refine' " term : tactic
|
||||
|
||||
/-- `exfalso` converts a goal `⊢ tgt` into `⊢ False` by applying `False.elim`. -/
|
||||
macro "exfalso" : tactic => `(tactic| refine False.elim ?_)
|
||||
|
||||
/--
|
||||
If the main goal's target type is an inductive type, `constructor` solves it with
|
||||
the first matching constructor, or else fails.
|
||||
-/
|
||||
syntax (name := constructor) "constructor" : tactic
|
||||
|
||||
/--
|
||||
Applies the second constructor when
|
||||
the goal is an inductive type with exactly two constructors, or fails otherwise.
|
||||
```
|
||||
example : True ∨ False := by
|
||||
left
|
||||
trivial
|
||||
```
|
||||
-/
|
||||
syntax (name := left) "left" : tactic
|
||||
|
||||
/--
|
||||
Applies the second constructor when
|
||||
the goal is an inductive type with exactly two constructors, or fails otherwise.
|
||||
```
|
||||
example {p q : Prop} (h : q) : p ∨ q := by
|
||||
right
|
||||
exact h
|
||||
```
|
||||
-/
|
||||
syntax (name := right) "right" : tactic
|
||||
|
||||
/--
|
||||
* `case tag => tac` focuses on the goal with case name `tag` and solves it using `tac`,
|
||||
or else fails.
|
||||
@@ -376,7 +414,7 @@ syntax locationWildcard := " *"
|
||||
A hypothesis location specification consists of 1 or more hypothesis references
|
||||
and optionally `⊢` denoting the goal.
|
||||
-/
|
||||
syntax locationHyp := (ppSpace colGt term:max)+ ppSpace patternIgnore( atomic("|" noWs "-") <|> "⊢")?
|
||||
syntax locationHyp := (ppSpace colGt term:max)+ patternIgnore(ppSpace (atomic("|" noWs "-") <|> "⊢"))?
|
||||
|
||||
/--
|
||||
Location specifications are used by many tactics that can operate on either the
|
||||
@@ -528,6 +566,89 @@ definitionally equal to the input.
|
||||
syntax (name := dsimp) "dsimp" (config)? (discharger)? (&" only")?
|
||||
(" [" withoutPosition((simpErase <|> simpLemma),*,?) "]")? (location)? : tactic
|
||||
|
||||
/--
|
||||
A `simpArg` is either a `*`, `-lemma` or a simp lemma specification
|
||||
(which includes the `↑` `↓` `←` specifications for pre, post, reverse rewriting).
|
||||
-/
|
||||
def simpArg := simpStar.binary `orelse (simpErase.binary `orelse simpLemma)
|
||||
|
||||
/-- A simp args list is a list of `simpArg`. This is the main argument to `simp`. -/
|
||||
syntax simpArgs := " [" simpArg,* "]"
|
||||
|
||||
/--
|
||||
A `dsimpArg` is similar to `simpArg`, but it does not have the `simpStar` form
|
||||
because it does not make sense to use hypotheses in `dsimp`.
|
||||
-/
|
||||
def dsimpArg := simpErase.binary `orelse simpLemma
|
||||
|
||||
/-- A dsimp args list is a list of `dsimpArg`. This is the main argument to `dsimp`. -/
|
||||
syntax dsimpArgs := " [" dsimpArg,* "]"
|
||||
|
||||
/-- The common arguments of `simp?` and `simp?!`. -/
|
||||
syntax simpTraceArgsRest := (config)? (discharger)? (&" only")? (simpArgs)? (ppSpace location)?
|
||||
|
||||
/--
|
||||
`simp?` takes the same arguments as `simp`, but reports an equivalent call to `simp only`
|
||||
that would be sufficient to close the goal. This is useful for reducing the size of the simp
|
||||
set in a local invocation to speed up processing.
|
||||
```
|
||||
example (x : Nat) : (if True then x + 2 else 3) = x + 2 := by
|
||||
simp? -- prints "Try this: simp only [ite_true]"
|
||||
```
|
||||
|
||||
This command can also be used in `simp_all` and `dsimp`.
|
||||
-/
|
||||
syntax (name := simpTrace) "simp?" "!"? simpTraceArgsRest : tactic
|
||||
|
||||
@[inherit_doc simpTrace]
|
||||
macro tk:"simp?!" rest:simpTraceArgsRest : tactic => `(tactic| simp?%$tk ! $rest)
|
||||
|
||||
/-- The common arguments of `simp_all?` and `simp_all?!`. -/
|
||||
syntax simpAllTraceArgsRest := (config)? (discharger)? (&" only")? (dsimpArgs)?
|
||||
|
||||
@[inherit_doc simpTrace]
|
||||
syntax (name := simpAllTrace) "simp_all?" "!"? simpAllTraceArgsRest : tactic
|
||||
|
||||
@[inherit_doc simpTrace]
|
||||
macro tk:"simp_all?!" rest:simpAllTraceArgsRest : tactic => `(tactic| simp_all?%$tk ! $rest)
|
||||
|
||||
/-- The common arguments of `dsimp?` and `dsimp?!`. -/
|
||||
syntax dsimpTraceArgsRest := (config)? (&" only")? (dsimpArgs)? (ppSpace location)?
|
||||
|
||||
@[inherit_doc simpTrace]
|
||||
syntax (name := dsimpTrace) "dsimp?" "!"? dsimpTraceArgsRest : tactic
|
||||
|
||||
@[inherit_doc simpTrace]
|
||||
macro tk:"dsimp?!" rest:dsimpTraceArgsRest : tactic => `(tactic| dsimp?%$tk ! $rest)
|
||||
|
||||
/-- The arguments to the `simpa` family tactics. -/
|
||||
syntax simpaArgsRest := (config)? (discharger)? &" only "? (simpArgs)? (" using " term)?
|
||||
|
||||
/--
|
||||
This is a "finishing" tactic modification of `simp`. It has two forms.
|
||||
|
||||
* `simpa [rules, ⋯] using e` will simplify the goal and the type of
|
||||
`e` using `rules`, then try to close the goal using `e`.
|
||||
|
||||
Simplifying the type of `e` makes it more likely to match the goal
|
||||
(which has also been simplified). This construction also tends to be
|
||||
more robust under changes to the simp lemma set.
|
||||
|
||||
* `simpa [rules, ⋯]` will simplify the goal and the type of a
|
||||
hypothesis `this` if present in the context, then try to close the goal using
|
||||
the `assumption` tactic.
|
||||
-/
|
||||
syntax (name := simpa) "simpa" "?"? "!"? simpaArgsRest : tactic
|
||||
|
||||
@[inherit_doc simpa] macro "simpa!" rest:simpaArgsRest : tactic =>
|
||||
`(tactic| simpa ! $rest:simpaArgsRest)
|
||||
|
||||
@[inherit_doc simpa] macro "simpa?" rest:simpaArgsRest : tactic =>
|
||||
`(tactic| simpa ? $rest:simpaArgsRest)
|
||||
|
||||
@[inherit_doc simpa] macro "simpa?!" rest:simpaArgsRest : tactic =>
|
||||
`(tactic| simpa ?! $rest:simpaArgsRest)
|
||||
|
||||
/--
|
||||
`delta id1 id2 ...` delta-expands the definitions `id1`, `id2`, ....
|
||||
This is a low-level tactic, it will expose how recursive definitions have been
|
||||
@@ -552,12 +673,13 @@ It makes sure the "continuation" `?_` is the main goal after refining.
|
||||
macro "refine_lift " e:term : tactic => `(tactic| focus (refine no_implicit_lambda% $e; rotate_right))
|
||||
|
||||
/--
|
||||
`have h : t := e` adds the hypothesis `h : t` to the current goal if `e` a term
|
||||
of type `t`.
|
||||
* If `t` is omitted, it will be inferred.
|
||||
* If `h` is omitted, the name `this` is used.
|
||||
* The variant `have pattern := e` is equivalent to `match e with | pattern => _`,
|
||||
and it is convenient for types that have only one applicable constructor.
|
||||
The `have` tactic is for adding hypotheses to the local context of the main goal.
|
||||
* `have h : t := e` adds the hypothesis `h : t` if `e` is a term of type `t`.
|
||||
* `have h := e` uses the type of `e` for `t`.
|
||||
* `have : t := e` and `have := e` use `this` for the name of the hypothesis.
|
||||
* `have pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
|
||||
where `_` stands for the tactics that follow this one.
|
||||
It is convenient for types that have only one applicable constructor.
|
||||
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
|
||||
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
-/
|
||||
@@ -572,12 +694,15 @@ If `h :` is omitted, the name `this` is used.
|
||||
-/
|
||||
macro "suffices " d:sufficesDecl : tactic => `(tactic| refine_lift suffices $d; ?_)
|
||||
/--
|
||||
`let h : t := e` adds the hypothesis `h : t := e` to the current goal if `e` a term of type `t`.
|
||||
If `t` is omitted, it will be inferred.
|
||||
The variant `let pattern := e` is equivalent to `match e with | pattern => _`,
|
||||
and it is convenient for types that have only applicable constructor.
|
||||
Example: given `h : p ∧ q ∧ r`, `let ⟨h₁, h₂, h₃⟩ := h` produces the hypotheses
|
||||
`h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
The `let` tactic is for adding definitions to the local context of the main goal.
|
||||
* `let x : t := e` adds the definition `x : t := e` if `e` is a term of type `t`.
|
||||
* `let x := e` uses the type of `e` for `t`.
|
||||
* `let : t := e` and `let := e` use `this` for the name of the hypothesis.
|
||||
* `let pat := e` for a pattern `pat` is equivalent to `match e with | pat => _`,
|
||||
where `_` stands for the tactics that follow this one.
|
||||
It is convenient for types that let only one applicable constructor.
|
||||
For example, given `p : α × β × γ`, `let ⟨x, y, z⟩ := p` produces the
|
||||
local variables `x : α`, `y : β`, and `z : γ`.
|
||||
-/
|
||||
macro "let " d:letDecl : tactic => `(tactic| refine_lift let $d:letDecl; ?_)
|
||||
/--
|
||||
@@ -872,6 +997,339 @@ The tactic `nomatch h` is shorthand for `exact nomatch h`.
|
||||
macro "nomatch " es:term,+ : tactic =>
|
||||
`(tactic| exact nomatch $es:term,*)
|
||||
|
||||
/--
|
||||
Acts like `have`, but removes a hypothesis with the same name as
|
||||
this one if possible. For example, if the state is:
|
||||
|
||||
```lean
|
||||
f : α → β
|
||||
h : α
|
||||
⊢ goal
|
||||
```
|
||||
|
||||
Then after `replace h := f h` the state will be:
|
||||
|
||||
```lean
|
||||
f : α → β
|
||||
h : β
|
||||
⊢ goal
|
||||
```
|
||||
|
||||
whereas `have h := f h` would result in:
|
||||
|
||||
```lean
|
||||
f : α → β
|
||||
h† : α
|
||||
h : β
|
||||
⊢ goal
|
||||
```
|
||||
|
||||
This can be used to simulate the `specialize` and `apply at` tactics of Coq.
|
||||
-/
|
||||
syntax (name := replace) "replace" haveDecl : tactic
|
||||
|
||||
/--
|
||||
`repeat' tac` runs `tac` on all of the goals to produce a new list of goals,
|
||||
then runs `tac` again on all of those goals, and repeats until `tac` fails on all remaining goals.
|
||||
-/
|
||||
syntax (name := repeat') "repeat' " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`repeat1' tac` applies `tac` to main goal at least once. If the application succeeds,
|
||||
the tactic is applied recursively to the generated subgoals until it eventually fails.
|
||||
-/
|
||||
syntax (name := repeat1') "repeat1' " tacticSeq : tactic
|
||||
|
||||
/-- `and_intros` applies `And.intro` until it does not make progress. -/
|
||||
syntax "and_intros" : tactic
|
||||
macro_rules | `(tactic| and_intros) => `(tactic| repeat' refine And.intro ?_ ?_)
|
||||
|
||||
/--
|
||||
`subst_eq` repeatedly substitutes according to the equality proof hypotheses in the context,
|
||||
replacing the left side of the equality with the right, until no more progress can be made.
|
||||
-/
|
||||
syntax (name := substEqs) "subst_eqs" : tactic
|
||||
|
||||
/-- The `run_tac doSeq` tactic executes code in `TacticM Unit`. -/
|
||||
syntax (name := runTac) "run_tac " doSeq : tactic
|
||||
|
||||
/-- `haveI` behaves like `have`, but inlines the value instead of producing a `let_fun` term. -/
|
||||
macro "haveI" d:haveDecl : tactic => `(tactic| refine_lift haveI $d:haveDecl; ?_)
|
||||
|
||||
/-- `letI` behaves like `let`, but inlines the value instead of producing a `let_fun` term. -/
|
||||
macro "letI" d:haveDecl : tactic => `(tactic| refine_lift letI $d:haveDecl; ?_)
|
||||
|
||||
/--
|
||||
The `omega` tactic, for resolving integer and natural linear arithmetic problems.
|
||||
|
||||
It is not yet a full decision procedure (no "dark" or "grey" shadows),
|
||||
but should be effective on many problems.
|
||||
|
||||
We handle hypotheses of the form `x = y`, `x < y`, `x ≤ y`, and `k ∣ x` for `x y` in `Nat` or `Int`
|
||||
(and `k` a literal), along with negations of these statements.
|
||||
|
||||
We decompose the sides of the inequalities as linear combinations of atoms.
|
||||
|
||||
If we encounter `x / k` or `x % k` for literal integers `k` we introduce new auxiliary variables
|
||||
and the relevant inequalities.
|
||||
|
||||
On the first pass, we do not perform case splits on natural subtraction.
|
||||
If `omega` fails, we recursively perform a case split on
|
||||
a natural subtraction appearing in a hypothesis, and try again.
|
||||
|
||||
The options
|
||||
```
|
||||
omega (config :=
|
||||
{ splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })
|
||||
```
|
||||
can be used to:
|
||||
* `splitDisjunctions`: split any disjunctions found in the context,
|
||||
if the problem is not otherwise solvable.
|
||||
* `splitNatSub`: for each appearance of `((a - b : Nat) : Int)`, split on `a ≤ b` if necessary.
|
||||
* `splitNatAbs`: for each appearance of `Int.natAbs a`, split on `0 ≤ a` if necessary.
|
||||
* `splitMinMax`: for each occurrence of `min a b`, split on `min a b = a ∨ min a b = b`
|
||||
Currently, all of these are on by default.
|
||||
-/
|
||||
syntax (name := omega) "omega" (config)? : tactic
|
||||
|
||||
/--
|
||||
`bv_omega` is `omega` with an additional preprocessor that turns statements about `BitVec` into statements about `Nat`.
|
||||
Currently the preprocessor is implemented as `try simp only [bv_toNat] at *`.
|
||||
`bv_toNat` is a `@[simp]` attribute that you can (cautiously) add to more theorems.
|
||||
-/
|
||||
macro "bv_omega" : tactic => `(tactic| (try simp only [bv_toNat] at *) <;> omega)
|
||||
|
||||
/-- Implementation of `norm_cast` (the full `norm_cast` calls `trivial` afterwards). -/
|
||||
syntax (name := normCast0) "norm_cast0" (location)? : tactic
|
||||
|
||||
/-- `assumption_mod_cast` is a variant of `assumption` that solves the goal
|
||||
using a hypothesis. Unlike `assumption`, it first pre-processes the goal and
|
||||
each hypothesis to move casts as far outwards as possible, so it can be used
|
||||
in more situations.
|
||||
|
||||
Concretely, it runs `norm_cast` on the goal. For each local hypothesis `h`, it also
|
||||
normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
|
||||
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
|
||||
|
||||
/--
|
||||
The `norm_cast` family of tactics is used to normalize casts inside expressions.
|
||||
It is basically a `simp` tactic with a specific set of lemmas to move casts
|
||||
upwards in the expression.
|
||||
Therefore even in situations where non-terminal `simp` calls are discouraged (because of fragility),
|
||||
`norm_cast` is considered safe.
|
||||
It also has special handling of numerals.
|
||||
|
||||
For instance, given an assumption
|
||||
```lean
|
||||
a b : ℤ
|
||||
h : ↑a + ↑b < (10 : ℚ)
|
||||
```
|
||||
|
||||
writing `norm_cast at h` will turn `h` into
|
||||
```lean
|
||||
h : a + b < 10
|
||||
```
|
||||
|
||||
There are also variants of `exact`, `apply`, `rw`, and `assumption` that
|
||||
work modulo `norm_cast` - in other words, they apply `norm_cast` to make
|
||||
them more flexible. They are called `exact_mod_cast`, `apply_mod_cast`,
|
||||
`rw_mod_cast`, and `assumption_mod_cast`, respectively.
|
||||
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
|
||||
in the goal and `h` before using `exact h` or `apply h`.
|
||||
Writing `assumption_mod_cast` will normalize casts in the goal and, for
|
||||
every hypothesis `h` in the context, it will try to normalize casts in `h` and use
|
||||
`exact h`.
|
||||
`rw_mod_cast` acts like the `rw` tactic but it applies `norm_cast` between steps.
|
||||
|
||||
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
|
||||
-/
|
||||
macro "norm_cast" loc:(location)? : tactic =>
|
||||
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
`push_cast` rewrites the goal to move casts inward, toward the leaf nodes.
|
||||
This uses `norm_cast` lemmas in the forward direction.
|
||||
For example, `↑(a + b)` will be written to `↑a + ↑b`.
|
||||
It is equivalent to `simp only with push_cast`.
|
||||
It can also be used at hypotheses with `push_cast at h`
|
||||
and with extra simp lemmas with `push_cast [int.add_zero]`.
|
||||
|
||||
```lean
|
||||
example (a b : ℕ) (h1 : ((a + b : ℕ) : ℤ) = 10) (h2 : ((a + b + 0 : ℕ) : ℤ) = 10) :
|
||||
((a + b : ℕ) : ℤ) = 10 :=
|
||||
begin
|
||||
push_cast,
|
||||
push_cast at h1,
|
||||
push_cast [int.add_zero] at h2,
|
||||
end
|
||||
```
|
||||
-/
|
||||
syntax (name := pushCast) "push_cast" (config)? (discharger)? (&" only")?
|
||||
(" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
|
||||
/--
|
||||
`norm_cast_add_elim foo` registers `foo` as an elim-lemma in `norm_cast`.
|
||||
-/
|
||||
syntax (name := normCastAddElim) "norm_cast_add_elim" ident : command
|
||||
|
||||
/--
|
||||
* `symm` applies to a goal whose target has the form `t ~ u` where `~` is a symmetric relation,
|
||||
that is, a relation which has a symmetry lemma tagged with the attribute [symm].
|
||||
It replaces the target with `u ~ t`.
|
||||
* `symm at h` will rewrite a hypothesis `h : t ~ u` to `h : u ~ t`.
|
||||
-/
|
||||
syntax (name := symm) "symm" (location)? : tactic
|
||||
|
||||
/-- For every hypothesis `h : a ~ b` where a `@[symm]` lemma is available,
|
||||
add a hypothesis `h_symm : b ~ a`. -/
|
||||
syntax (name := symmSaturate) "symm_saturate" : tactic
|
||||
|
||||
namespace SolveByElim
|
||||
|
||||
/-- Syntax for omitting a local hypothesis in `solve_by_elim`. -/
|
||||
syntax erase := "-" term:max
|
||||
/-- Syntax for including all local hypotheses in `solve_by_elim`. -/
|
||||
syntax star := "*"
|
||||
/-- Syntax for adding or removing a term, or `*`, in `solve_by_elim`. -/
|
||||
syntax arg := star <|> erase <|> term
|
||||
/-- Syntax for adding and removing terms in `solve_by_elim`. -/
|
||||
syntax args := " [" SolveByElim.arg,* "]"
|
||||
/-- Syntax for using all lemmas labelled with an attribute in `solve_by_elim`. -/
|
||||
syntax using_ := " using " ident,*
|
||||
|
||||
end SolveByElim
|
||||
|
||||
section SolveByElim
|
||||
open SolveByElim (args using_)
|
||||
|
||||
/--
|
||||
`solve_by_elim` calls `apply` on the main goal to find an assumption whose head matches
|
||||
and then repeatedly calls `apply` on the generated subgoals until no subgoals remain,
|
||||
performing at most `maxDepth` (defaults to 6) recursive steps.
|
||||
|
||||
`solve_by_elim` discharges the current goal or fails.
|
||||
|
||||
`solve_by_elim` performs backtracking if subgoals can not be solved.
|
||||
|
||||
By default, the assumptions passed to `apply` are the local context, `rfl`, `trivial`,
|
||||
`congrFun` and `congrArg`.
|
||||
|
||||
The assumptions can be modified with similar syntax as for `simp`:
|
||||
* `solve_by_elim [h₁, h₂, ..., hᵣ]` also applies the given expressions.
|
||||
* `solve_by_elim only [h₁, h₂, ..., hᵣ]` does not include the local context,
|
||||
`rfl`, `trivial`, `congrFun`, or `congrArg` unless they are explicitly included.
|
||||
* `solve_by_elim [-h₁, ... -hₙ]` removes the given local hypotheses.
|
||||
* `solve_by_elim using [a₁, ...]` uses all lemmas which have been labelled
|
||||
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
|
||||
|
||||
`solve_by_elim*` tries to solve all goals together, using backtracking if a solution for one goal
|
||||
makes other goals impossible.
|
||||
(Adding or removing local hypotheses may not be well-behaved when starting with multiple goals.)
|
||||
|
||||
Optional arguments passed via a configuration argument as `solve_by_elim (config := { ... })`
|
||||
- `maxDepth`: number of attempts at discharging generated subgoals
|
||||
- `symm`: adds all hypotheses derived by `symm` (defaults to `true`).
|
||||
- `exfalso`: allow calling `exfalso` and trying again if `solve_by_elim` fails
|
||||
(defaults to `true`).
|
||||
- `transparency`: change the transparency mode when calling `apply`. Defaults to `.default`,
|
||||
but it is often useful to change to `.reducible`,
|
||||
so semireducible definitions will not be unfolded when trying to apply a lemma.
|
||||
|
||||
See also the doc-comment for `Std.Tactic.BacktrackConfig` for the options
|
||||
`proc`, `suspend`, and `discharge` which allow further customization of `solve_by_elim`.
|
||||
Both `apply_assumption` and `apply_rules` are implemented via these hooks.
|
||||
-/
|
||||
syntax (name := solveByElim)
|
||||
"solve_by_elim" "*"? (config)? (&" only")? (args)? (using_)? : tactic
|
||||
|
||||
/--
|
||||
`apply_assumption` looks for an assumption of the form `... → ∀ _, ... → head`
|
||||
where `head` matches the current goal.
|
||||
|
||||
You can specify additional rules to apply using `apply_assumption [...]`.
|
||||
By default `apply_assumption` will also try `rfl`, `trivial`, `congrFun`, and `congrArg`.
|
||||
If you don't want these, or don't want to use all hypotheses, use `apply_assumption only [...]`.
|
||||
You can use `apply_assumption [-h]` to omit a local hypothesis.
|
||||
You can use `apply_assumption using [a₁, ...]` to use all lemmas which have been labelled
|
||||
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
|
||||
|
||||
`apply_assumption` will use consequences of local hypotheses obtained via `symm`.
|
||||
|
||||
If `apply_assumption` fails, it will call `exfalso` and try again.
|
||||
Thus if there is an assumption of the form `P → ¬ Q`, the new tactic state
|
||||
will have two goals, `P` and `Q`.
|
||||
|
||||
You can pass a further configuration via the syntax `apply_rules (config := {...}) lemmas`.
|
||||
The options supported are the same as for `solve_by_elim` (and include all the options for `apply`).
|
||||
-/
|
||||
syntax (name := applyAssumption)
|
||||
"apply_assumption" (config)? (&" only")? (args)? (using_)? : tactic
|
||||
|
||||
/--
|
||||
`apply_rules [l₁, l₂, ...]` tries to solve the main goal by iteratively
|
||||
applying the list of lemmas `[l₁, l₂, ...]` or by applying a local hypothesis.
|
||||
If `apply` generates new goals, `apply_rules` iteratively tries to solve those goals.
|
||||
You can use `apply_rules [-h]` to omit a local hypothesis.
|
||||
|
||||
`apply_rules` will also use `rfl`, `trivial`, `congrFun` and `congrArg`.
|
||||
These can be disabled, as can local hypotheses, by using `apply_rules only [...]`.
|
||||
|
||||
You can use `apply_rules using [a₁, ...]` to use all lemmas which have been labelled
|
||||
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
|
||||
|
||||
You can pass a further configuration via the syntax `apply_rules (config := {...})`.
|
||||
The options supported are the same as for `solve_by_elim` (and include all the options for `apply`).
|
||||
|
||||
`apply_rules` will try calling `symm` on hypotheses and `exfalso` on the goal as needed.
|
||||
This can be disabled with `apply_rules (config := {symm := false, exfalso := false})`.
|
||||
|
||||
You can bound the iteration depth using the syntax `apply_rules (config := {maxDepth := n})`.
|
||||
|
||||
Unlike `solve_by_elim`, `apply_rules` does not perform backtracking, and greedily applies
|
||||
a lemma from the list until it gets stuck.
|
||||
-/
|
||||
syntax (name := applyRules) "apply_rules" (config)? (&" only")? (args)? (using_)? : tactic
|
||||
end SolveByElim
|
||||
|
||||
/--
|
||||
Searches environment for definitions or theorems that can solve the goal using `exact`
|
||||
with conditions resolved by `solve_by_elim`.
|
||||
|
||||
The optional `using` clause provides identifiers in the local context that must be
|
||||
used by `exact?` when closing the goal. This is most useful if there are multiple
|
||||
ways to resolve the goal, and one wants to guide which lemma is used.
|
||||
-/
|
||||
syntax (name := exact?) "exact?" (" using " (colGt ident),+)? : tactic
|
||||
|
||||
/--
|
||||
Searches environment for definitions or theorems that can refine the goal using `apply`
|
||||
with conditions resolved when possible with `solve_by_elim`.
|
||||
|
||||
The optional `using` clause provides identifiers in the local context that must be
|
||||
used when closing the goal.
|
||||
-/
|
||||
syntax (name := apply?) "apply?" (" using " (colGt term),+)? : tactic
|
||||
|
||||
/--
|
||||
`show_term tac` runs `tac`, then prints the generated term in the form
|
||||
"exact X Y Z" or "refine X ?_ Z" if there are remaining subgoals.
|
||||
|
||||
(For some tactics, the printed term will not be human readable.)
|
||||
-/
|
||||
syntax (name := showTerm) "show_term " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`show_term e` elaborates `e`, then prints the generated term.
|
||||
-/
|
||||
macro (name := showTermElab) tk:"show_term " t:term : term =>
|
||||
`(term| no_implicit_lambda% (show_term_elab%$tk $t))
|
||||
|
||||
/--
|
||||
The command `by?` will print a suggestion for replacing the proof block with a proof term
|
||||
using `show_term`.
|
||||
-/
|
||||
macro (name := by?) tk:"by?" t:tacticSeq : term => `(show_term%$tk by%$tk $t)
|
||||
|
||||
end Tactic
|
||||
|
||||
namespace Attr
|
||||
@@ -919,6 +1377,59 @@ If there are several with the same priority, it is uses the "most recent one". E
|
||||
```
|
||||
-/
|
||||
syntax (name := simp) "simp" (Tactic.simpPre <|> Tactic.simpPost)? (ppSpace prio)? : attr
|
||||
|
||||
|
||||
/-- The possible `norm_cast` kinds: `elim`, `move`, or `squash`. -/
|
||||
syntax normCastLabel := &"elim" <|> &"move" <|> &"squash"
|
||||
|
||||
/--
|
||||
The `norm_cast` attribute should be given to lemmas that describe the
|
||||
behaviour of a coercion with respect to an operator, a relation, or a particular
|
||||
function.
|
||||
|
||||
It only concerns equality or iff lemmas involving `↑`, `⇑` and `↥`, describing the behavior of
|
||||
the coercion functions.
|
||||
It does not apply to the explicit functions that define the coercions.
|
||||
|
||||
Examples:
|
||||
```lean
|
||||
@[norm_cast] theorem coe_nat_inj' {m n : ℕ} : (↑m : ℤ) = ↑n ↔ m = n
|
||||
|
||||
@[norm_cast] theorem coe_int_denom (n : ℤ) : (n : ℚ).denom = 1
|
||||
|
||||
@[norm_cast] theorem cast_id : ∀ n : ℚ, ↑n = n
|
||||
|
||||
@[norm_cast] theorem coe_nat_add (m n : ℕ) : (↑(m + n) : ℤ) = ↑m + ↑n
|
||||
|
||||
@[norm_cast] theorem cast_coe_nat (n : ℕ) : ((n : ℤ) : α) = n
|
||||
|
||||
@[norm_cast] theorem cast_one : ((1 : ℚ) : α) = 1
|
||||
```
|
||||
|
||||
Lemmas tagged with `@[norm_cast]` are classified into three categories: `move`, `elim`, and
|
||||
`squash`. They are classified roughly as follows:
|
||||
|
||||
* elim lemma: LHS has 0 head coes and ≥ 1 internal coe
|
||||
* move lemma: LHS has 1 head coe and 0 internal coes, RHS has 0 head coes and ≥ 1 internal coes
|
||||
* squash lemma: LHS has ≥ 1 head coes and 0 internal coes, RHS has fewer head coes
|
||||
|
||||
`norm_cast` uses `move` and `elim` lemmas to factor coercions toward the root of an expression
|
||||
and to cancel them from both sides of an equation or relation. It uses `squash` lemmas to clean
|
||||
up the result.
|
||||
|
||||
It is typically not necessary to specify these categories, as `norm_cast` lemmas are
|
||||
automatically classified by default. The automatic classification can be overridden by
|
||||
giving an optional `elim`, `move`, or `squash` parameter to the attribute.
|
||||
|
||||
```lean
|
||||
@[simp, norm_cast elim] lemma nat_cast_re (n : ℕ) : (n : ℂ).re = n := by
|
||||
rw [← of_real_nat_cast, of_real_re]
|
||||
```
|
||||
|
||||
Don't do this unless you understand what you are doing.
|
||||
-/
|
||||
syntax (name := norm_cast) "norm_cast" (ppSpace normCastLabel)? (ppSpace num)? : attr
|
||||
|
||||
end Attr
|
||||
|
||||
end Parser
|
||||
@@ -938,13 +1449,14 @@ macro_rules | `(‹$type›) => `((by assumption : $type))
|
||||
by the notation `arr[i]` to prove any side conditions that arise when
|
||||
constructing the term (e.g. the index is in bounds of the array).
|
||||
The default behavior is to just try `trivial` (which handles the case
|
||||
where `i < arr.size` is in the context) and `simp_arith`
|
||||
where `i < arr.size` is in the context) and `simp_arith` and `omega`
|
||||
(for doing linear arithmetic in the index).
|
||||
-/
|
||||
syntax "get_elem_tactic_trivial" : tactic
|
||||
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| trivial)
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| omega)
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| trivial)
|
||||
|
||||
/--
|
||||
`get_elem_tactic` is the tactic automatically called by the notation `arr[i]`
|
||||
@@ -955,6 +1467,24 @@ users are encouraged to extend `get_elem_tactic_trivial` instead of this tactic.
|
||||
-/
|
||||
macro "get_elem_tactic" : tactic =>
|
||||
`(tactic| first
|
||||
/-
|
||||
Recall that `macro_rules` are tried in reverse order.
|
||||
We want `assumption` to be tried first.
|
||||
This is important for theorems such as
|
||||
```
|
||||
[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
|
||||
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop ▸ hi) (Nat.sub_le _ _)) :=
|
||||
```
|
||||
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
|
||||
they add new `macro_rules` for `get_elem_tactic_trivial`.
|
||||
|
||||
TODO: Implement priorities for `macro_rules`.
|
||||
TODO: Ensure we have a **high-priority** macro_rules for `get_elem_tactic_trivial` which is just `assumption`.
|
||||
-/
|
||||
| assumption
|
||||
| get_elem_tactic_trivial
|
||||
| fail "failed to prove index is valid, possible solutions:
|
||||
- Use `have`-expressions to prove the index is valid
|
||||
@@ -970,3 +1500,9 @@ macro_rules | `($x[$i]) => `(getElem $x $i (by get_elem_tactic))
|
||||
@[inherit_doc getElem]
|
||||
syntax term noWs "[" withoutPosition(term) "]'" term:max : term
|
||||
macro_rules | `($x[$i]'$h) => `(getElem $x $i $h)
|
||||
|
||||
/--
|
||||
Searches environment for definitions or theorems that can be substituted in
|
||||
for `exact?% to solve the goal.
|
||||
-/
|
||||
syntax (name := Lean.Parser.Syntax.exact?) "exact?%" : term
|
||||
|
||||
@@ -41,4 +41,46 @@ macro_rules
|
||||
| `(tactic| if%$tk $c then%$ttk $pos else%$etk $neg) =>
|
||||
expandIfThenElse tk ttk etk pos neg fun pos neg => `(if h : $c then $pos else $neg)
|
||||
|
||||
/--
|
||||
`iterate n tac` runs `tac` exactly `n` times.
|
||||
`iterate tac` runs `tac` repeatedly until failure.
|
||||
|
||||
`iterate`'s argument is a tactic sequence,
|
||||
so multiple tactics can be run using `iterate n (tac₁; tac₂; ⋯)` or
|
||||
```lean
|
||||
iterate n
|
||||
tac₁
|
||||
tac₂
|
||||
⋯
|
||||
```
|
||||
-/
|
||||
syntax "iterate" (ppSpace num)? ppSpace tacticSeq : tactic
|
||||
macro_rules
|
||||
| `(tactic| iterate $seq:tacticSeq) =>
|
||||
`(tactic| try ($seq:tacticSeq); iterate $seq:tacticSeq)
|
||||
| `(tactic| iterate $n $seq:tacticSeq) =>
|
||||
match n.1.toNat with
|
||||
| 0 => `(tactic| skip)
|
||||
| n+1 => `(tactic| ($seq:tacticSeq); iterate $(quote n) $seq:tacticSeq)
|
||||
|
||||
/--
|
||||
Rewrites with the given rules, normalizing casts prior to each step.
|
||||
-/
|
||||
syntax "rw_mod_cast" (config)? rwRuleSeq (location)? : tactic
|
||||
macro_rules
|
||||
| `(tactic| rw_mod_cast $[$config]? [$rules,*] $[$loc]?) => do
|
||||
let tacs ← rules.getElems.mapM fun rule =>
|
||||
`(tactic| (norm_cast at *; rw $[$config]? [$rule] $[$loc]?))
|
||||
`(tactic| ($[$tacs]*))
|
||||
|
||||
/--
|
||||
Normalize casts in the goal and the given expression, then close the goal with `exact`.
|
||||
-/
|
||||
macro "exact_mod_cast " e:term : tactic => `(tactic| exact mod_cast ($e : _))
|
||||
|
||||
/--
|
||||
Normalize casts in the goal and the given expression, then `apply` the expression to the goal.
|
||||
-/
|
||||
macro "apply_mod_cast " e:term : tactic => `(tactic| apply mod_cast ($e : _))
|
||||
|
||||
end Lean.Parser.Tactic
|
||||
|
||||
@@ -206,12 +206,39 @@ protected inductive Lex : α × β → α × β → Prop where
|
||||
| left {a₁} (b₁) {a₂} (b₂) (h : ra a₁ a₂) : Prod.Lex (a₁, b₁) (a₂, b₂)
|
||||
| right (a) {b₁ b₂} (h : rb b₁ b₂) : Prod.Lex (a, b₁) (a, b₂)
|
||||
|
||||
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⟩
|
||||
|
||||
namespace Lex
|
||||
|
||||
instance [αeqDec : DecidableEq α] {r : α → α → Prop} [rDec : DecidableRel r]
|
||||
{s : β → β → Prop} [sDec : DecidableRel s] : DecidableRel (Prod.Lex r s)
|
||||
| (a, b), (a', b') =>
|
||||
match rDec a a' with
|
||||
| isTrue raa' => isTrue $ left b b' raa'
|
||||
| isFalse nraa' =>
|
||||
match αeqDec a a' with
|
||||
| isTrue eq => by
|
||||
subst eq
|
||||
cases sDec b b' with
|
||||
| isTrue sbb' => exact isTrue $ right a sbb'
|
||||
| isFalse nsbb' =>
|
||||
apply isFalse; intro contra; cases contra <;> contradiction
|
||||
| isFalse neqaa' => by
|
||||
apply isFalse; intro contra; cases contra <;> contradiction
|
||||
|
||||
-- TODO: generalize
|
||||
def Lex.right' {a₁ : Nat} {b₁ : β} (h₁ : a₁ ≤ a₂) (h₂ : rb b₁ b₂) : Prod.Lex Nat.lt rb (a₁, b₁) (a₂, b₂) :=
|
||||
def right' {a₁ : Nat} {b₁ : β} (h₁ : a₁ ≤ a₂) (h₂ : rb b₁ b₂) : Prod.Lex Nat.lt rb (a₁, b₁) (a₂, b₂) :=
|
||||
match Nat.eq_or_lt_of_le h₁ with
|
||||
| Or.inl h => h ▸ Prod.Lex.right a₁ h₂
|
||||
| Or.inr h => Prod.Lex.left b₁ _ h
|
||||
|
||||
end Lex
|
||||
|
||||
-- relational product based on ra and rb
|
||||
inductive RProd : α × β → α × β → Prop where
|
||||
| intro {a₁ b₁ a₂ b₂} (h₁ : ra a₁ a₂) (h₂ : rb b₁ b₂) : RProd (a₁, b₁) (a₂, b₂)
|
||||
|
||||
@@ -11,7 +11,7 @@ import Init.WF
|
||||
/-- Unfold definitions commonly used in well founded relation definitions.
|
||||
This is primarily intended for internal use in `decreasing_tactic`. -/
|
||||
macro "simp_wf" : tactic =>
|
||||
`(tactic| try simp (config := { unfoldPartialApp := true }) [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel, WellFoundedRelation.rel])
|
||||
`(tactic| try simp (config := { unfoldPartialApp := true, zetaDelta := true }) [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel, WellFoundedRelation.rel])
|
||||
|
||||
/-- Extensible helper tactic for `decreasing_tactic`. This handles the "base case"
|
||||
reasoning after applying lexicographic order lemmas.
|
||||
@@ -22,7 +22,8 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic| linarith)
|
||||
-/
|
||||
syntax "decreasing_trivial" : tactic
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| (simp (config := { arith := true, failIfUnchanged := false })); done)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| (simp (config := { arith := true, failIfUnchanged := false })) <;> done)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| omega)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| assumption)
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.sub_succ_lt_self; assumption) -- a - (i+1) < a - i if i < a
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.pred_lt'; assumption) -- i-1 < i if j < i
|
||||
|
||||
@@ -35,3 +35,4 @@ import Lean.Widget
|
||||
import Lean.Log
|
||||
import Lean.Linter
|
||||
import Lean.SubExpr
|
||||
import Lean.LabelAttribute
|
||||
|
||||
@@ -3,6 +3,7 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.CoreM
|
||||
import Lean.MonadEnv
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user