mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 11:24:07 +00:00
Compare commits
559 Commits
finModDiv
...
simproc_st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df2571286b | ||
|
|
b7dcd434fb | ||
|
|
80b10103a0 | ||
|
|
a0aea373a1 | ||
|
|
7a8ff91d33 | ||
|
|
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 | ||
|
|
90b08ef22e | ||
|
|
66e8cb7966 | ||
|
|
4718af5474 | ||
|
|
c138801c3a | ||
|
|
5b4c24ff97 | ||
|
|
1cb7450f40 | ||
|
|
02d1ebb564 | ||
|
|
488bfe2128 | ||
|
|
55402a5899 | ||
|
|
659218cf17 | ||
|
|
904239ae61 | ||
|
|
b548b4faae | ||
|
|
a7364499d2 | ||
|
|
003835111d | ||
|
|
61a8695ab1 | ||
|
|
127214bd18 | ||
|
|
b1944b662c | ||
|
|
a17832ba14 | ||
|
|
561ac09d61 | ||
|
|
f68429d3a7 | ||
|
|
a58232b820 | ||
|
|
696b08dca2 | ||
|
|
3a63b72eea | ||
|
|
9c160b8030 | ||
|
|
4bd75825b4 | ||
|
|
709e9909e7 | ||
|
|
83dd720337 | ||
|
|
ac631f4736 | ||
|
|
1f547225d1 | ||
|
|
09a43990aa | ||
|
|
819848a0db | ||
|
|
8f8b0a8322 | ||
|
|
9f633dcba2 | ||
|
|
cd4c7e4c35 | ||
|
|
9908823764 | ||
|
|
3e313d38f4 | ||
|
|
1b101a3d43 | ||
|
|
adcec8e67a | ||
|
|
86d032ebf9 | ||
|
|
92ca504903 | ||
|
|
021dd2d509 | ||
|
|
2ad3c6406e | ||
|
|
211770e2f9 | ||
|
|
760e824b9f | ||
|
|
17722369c6 | ||
|
|
64688d4cee | ||
|
|
69d462623e | ||
|
|
17520fa0b8 | ||
|
|
0055baf73a | ||
|
|
f40c999f68 | ||
|
|
cf092e7941 | ||
|
|
43bbedca46 | ||
|
|
509f35df02 | ||
|
|
732b266de0 | ||
|
|
1d8cf38ff9 | ||
|
|
a4226a4f6d | ||
|
|
76224e409b | ||
|
|
c3383de6ff | ||
|
|
e5b1c87606 | ||
|
|
da072c2ec8 | ||
|
|
d3c71ce2ff | ||
|
|
da21ef4fe8 | ||
|
|
168217b2bd | ||
|
|
8deb1838aa | ||
|
|
3d1b3c6b44 | ||
|
|
676121c71d | ||
|
|
6439d93389 | ||
|
|
e4e6601546 | ||
|
|
01469bdbd6 | ||
|
|
01750e2139 | ||
|
|
8037a8733d | ||
|
|
c4e6e48690 | ||
|
|
9cfca51257 | ||
|
|
de886c617d | ||
|
|
755b59c2cf | ||
|
|
266075b8a4 | ||
|
|
8db28ac32f | ||
|
|
b4a290a203 | ||
|
|
03f344a35f | ||
|
|
a48ca7b0a4 | ||
|
|
1cb1602977 | ||
|
|
c98deeb709 | ||
|
|
cd0be38bb4 | ||
|
|
578a2308b1 | ||
|
|
279607f5f8 | ||
|
|
456e435fe0 | ||
|
|
31981090e4 | ||
|
|
dd77dbdc11 | ||
|
|
fcb30c269b | ||
|
|
5f59d7f7b4 | ||
|
|
1364157e91 | ||
|
|
a524fd4be8 | ||
|
|
de23226d0c | ||
|
|
550fa6994e | ||
|
|
f9e5f1f1fd | ||
|
|
6b0e7e1f46 | ||
|
|
9fb44fae29 | ||
|
|
1f4359cc80 | ||
|
|
8293fd4e09 | ||
|
|
2beb948a3b | ||
|
|
409c6cac4c | ||
|
|
ec39de8cae | ||
|
|
586c3f9140 | ||
|
|
feda615ed5 | ||
|
|
4f41ccfcbf | ||
|
|
e9f69d1068 | ||
|
|
5cc9f6f9cb | ||
|
|
09aa845940 | ||
|
|
73b87f2558 | ||
|
|
c0f264ffe0 | ||
|
|
52d0f715c3 | ||
|
|
ec30da8af7 | ||
|
|
27b7002138 | ||
|
|
a2ed4db562 | ||
|
|
628633d02e | ||
|
|
f8edf452de | ||
|
|
12dc171c48 | ||
|
|
42e6214a42 | ||
|
|
53af5ead53 | ||
|
|
b706c0064e | ||
|
|
8e1b51701b | ||
|
|
ad068824d0 | ||
|
|
7c4c57759d | ||
|
|
1118931516 | ||
|
|
7150638836 | ||
|
|
30693a2dae | ||
|
|
368ead54b2 | ||
|
|
7c10415cd8 | ||
|
|
b5122b6a7b | ||
|
|
8bc1a9c4ba | ||
|
|
4169cac51f | ||
|
|
c394a834c3 | ||
|
|
9069c538ad | ||
|
|
4e16eb0476 | ||
|
|
e924ef229c | ||
|
|
8012eedab5 | ||
|
|
33c53a2418 | ||
|
|
3b9b13b706 | ||
|
|
94d51b2321 | ||
|
|
0342d62109 | ||
|
|
4e5ce6b65d | ||
|
|
e11b320cd6 | ||
|
|
cb6bfefc7a | ||
|
|
25ea5f6fa1 | ||
|
|
4958404f37 | ||
|
|
3e11b5fe15 | ||
|
|
57bc058209 | ||
|
|
610fa69f15 | ||
|
|
3a9b594fc5 | ||
|
|
0bc8fe48e3 | ||
|
|
7350d0a3ff | ||
|
|
b376b1594e | ||
|
|
88801166b6 | ||
|
|
ad58deeae3 | ||
|
|
666d454b42 | ||
|
|
b7efd200f0 | ||
|
|
e83e467667 | ||
|
|
2efa9de78a | ||
|
|
25baf73005 | ||
|
|
0bd424b5e6 | ||
|
|
d841ef5eb5 | ||
|
|
188ff2dd20 | ||
|
|
7564b204ec | ||
|
|
6fd7350c7b | ||
|
|
7ed4d1c432 | ||
|
|
5f847c4ce3 | ||
|
|
090d158fb9 | ||
|
|
81ced3bd0f | ||
|
|
ab721c64b3 | ||
|
|
93369e8773 | ||
|
|
23f2314da7 | ||
|
|
8a23c294a4 | ||
|
|
a7a3ae13dd | ||
|
|
5edd59806c | ||
|
|
a2aadee28f | ||
|
|
923216f9a9 | ||
|
|
0f9702f4b4 | ||
|
|
df53e6c4cf | ||
|
|
916c97b625 | ||
|
|
439689b219 | ||
|
|
1d78712b6c | ||
|
|
39f716f902 | ||
|
|
22c8154811 | ||
|
|
05e9983e25 | ||
|
|
f51b356002 | ||
|
|
ec9570fdd0 | ||
|
|
b37fdea5bf | ||
|
|
29c245ceba | ||
|
|
b8b49c50b9 | ||
|
|
127b309a0d | ||
|
|
b7c3ff6e6d | ||
|
|
0aa2b83450 | ||
|
|
684f32fabe | ||
|
|
eefcbbb37b | ||
|
|
903493799d | ||
|
|
7d90b0558e | ||
|
|
504b6dc93f | ||
|
|
6998acad66 | ||
|
|
cc1dcf8043 | ||
|
|
f54bce2abb | ||
|
|
1145976ff9 | ||
|
|
13d41f82d7 | ||
|
|
caf7a21c6f | ||
|
|
7c38649527 | ||
|
|
d1a15dea03 | ||
|
|
f1f8db4856 | ||
|
|
bcc49d1c5f | ||
|
|
63d00ea3c2 | ||
|
|
fdc52e0ea9 | ||
|
|
767139b235 | ||
|
|
bddb2152e5 | ||
|
|
8d04ac171d | ||
|
|
ae6fe098cb | ||
|
|
79c7b27034 | ||
|
|
2644b239a3 | ||
|
|
eb432cd3b7 | ||
|
|
312ea12bc2 | ||
|
|
67bfa19ce0 | ||
|
|
3335b2a01e | ||
|
|
78816a3ee7 | ||
|
|
7acbee8ae4 | ||
|
|
4dd59690e0 | ||
|
|
a2226a43ac | ||
|
|
62c3e56247 | ||
|
|
89d7eb8b78 | ||
|
|
8475ec7e36 | ||
|
|
4497aba1a9 | ||
|
|
cddc8089bc | ||
|
|
ce15b43798 | ||
|
|
430f4d28e4 | ||
|
|
d279a4871f | ||
|
|
f208d7b50f | ||
|
|
df18f3f1ff | ||
|
|
fbcfe6596e | ||
|
|
b5b664e570 | ||
|
|
2f216b5255 | ||
|
|
d4dca3baac | ||
|
|
de7d78a9f1 | ||
|
|
6a629f7d7f | ||
|
|
f74516a032 | ||
|
|
78200b309f | ||
|
|
b120080b85 | ||
|
|
4b8c342833 | ||
|
|
fa26d222cb | ||
|
|
e2f957109f | ||
|
|
20dd63aabf | ||
|
|
c656e71eb8 | ||
|
|
104c92d4f3 | ||
|
|
5cd90f5826 | ||
|
|
178ab8ef2e | ||
|
|
e6c0484074 | ||
|
|
dd42a0919d | ||
|
|
1b2bbe717d | ||
|
|
00359a0347 | ||
|
|
c474dff38c | ||
|
|
f2a92f3331 | ||
|
|
bcbcf50442 | ||
|
|
ec8811a75a | ||
|
|
b3a85631d8 | ||
|
|
5d35e9496e | ||
|
|
d4f10bc07e | ||
|
|
feb0cb6fc4 | ||
|
|
d6c81f8594 | ||
|
|
17825bf81d | ||
|
|
9290b491bb | ||
|
|
c91ece4f58 | ||
|
|
93a6279025 | ||
|
|
5c2292a923 | ||
|
|
14296ae720 | ||
|
|
6d23450642 | ||
|
|
92f1755e9b | ||
|
|
465f0feb2d | ||
|
|
24466a25f3 | ||
|
|
e4eff3bc6e | ||
|
|
66cb44c53c | ||
|
|
8be3897a8b | ||
|
|
bd89787a87 | ||
|
|
a5af90c724 | ||
|
|
5937f4208a | ||
|
|
ea5b55b8f2 | ||
|
|
0fca41ddb2 | ||
|
|
f356d8830e | ||
|
|
5b6e4faacd | ||
|
|
0ad611cf2f | ||
|
|
3a0edd05e6 | ||
|
|
99331219f9 | ||
|
|
18459cb537 | ||
|
|
e4f2c39ab2 | ||
|
|
3025a4a9a1 | ||
|
|
367ac01279 | ||
|
|
4f2f704962 | ||
|
|
34264a4b1d | ||
|
|
5d22145b83 | ||
|
|
0a6aed61e9 | ||
|
|
6c7a765abb | ||
|
|
c1f6daf1ac | ||
|
|
ffbea840bf | ||
|
|
190ac50994 | ||
|
|
c20d65771c | ||
|
|
cbba783bcf | ||
|
|
a4aaabf396 | ||
|
|
984d55c962 | ||
|
|
0249a8c15e | ||
|
|
6592df52cc | ||
|
|
9769ad6572 | ||
|
|
79251f5fa2 | ||
|
|
f142d9f798 | ||
|
|
7ff7cf9b5a | ||
|
|
5639302989 | ||
|
|
5f5d579986 | ||
|
|
681fca1f8f | ||
|
|
e34656ce75 | ||
|
|
5a68ad9ef4 | ||
|
|
a422f3f2c9 | ||
|
|
260eaebf4e | ||
|
|
dede354e77 | ||
|
|
5eb4a007a6 | ||
|
|
54dd588fc2 | ||
|
|
9efdde23e0 | ||
|
|
91917516f1 | ||
|
|
fb30932ca7 | ||
|
|
0adca630cc | ||
|
|
37362658ab | ||
|
|
66aa2c46a8 | ||
|
|
b97b0ad2aa | ||
|
|
fbefbce8c7 | ||
|
|
f1b274279b | ||
|
|
6a33afb745 | ||
|
|
9800e066bc | ||
|
|
5858549037 | ||
|
|
4d39a0b0e3 | ||
|
|
9bf0f5116b | ||
|
|
8b86beeb07 | ||
|
|
8881517018 | ||
|
|
0668544a35 | ||
|
|
1362268472 | ||
|
|
65d08fdcdd | ||
|
|
e29c3239e3 | ||
|
|
ed1a98d5ae | ||
|
|
c6e4b98793 | ||
|
|
ab36ed477e | ||
|
|
5d1d493635 | ||
|
|
893d480c77 | ||
|
|
857ba0a3e5 | ||
|
|
ad77e7e762 | ||
|
|
b3e9bb4997 | ||
|
|
139973217c | ||
|
|
b770060b9e | ||
|
|
8a2054ca09 | ||
|
|
171837216a | ||
|
|
19c81a19ea | ||
|
|
7cc2c9f1c9 | ||
|
|
7fb7b5c5cb | ||
|
|
bbc759522a | ||
|
|
712d3c2292 | ||
|
|
73540ecd48 | ||
|
|
d07e8fd6a4 | ||
|
|
446d547817 | ||
|
|
37c2ec10e9 | ||
|
|
6f2eb3f6b4 | ||
|
|
76a7754d08 | ||
|
|
77ee031172 | ||
|
|
2f35651308 | ||
|
|
62dc8d7308 | ||
|
|
6d349201b4 | ||
|
|
dcb92296f6 | ||
|
|
4ec3d78afa | ||
|
|
2ff4821026 | ||
|
|
ffd79a0824 | ||
|
|
8cfcf7ce61 | ||
|
|
dbe1c7f459 | ||
|
|
4bd0525a99 | ||
|
|
31f234ba3c | ||
|
|
262f213391 | ||
|
|
8b145b05e2 | ||
|
|
f3c3a1b62d | ||
|
|
1f68dec119 | ||
|
|
fd0a209f74 | ||
|
|
5189578a48 | ||
|
|
dcb40f67c1 | ||
|
|
e53952f167 | ||
|
|
d7c05a5ac4 | ||
|
|
ac73c8d342 | ||
|
|
007b1b5979 | ||
|
|
b278172b7c | ||
|
|
d9eddc9652 | ||
|
|
2099190ad4 | ||
|
|
72f7144403 | ||
|
|
995725b256 | ||
|
|
b1f2fcf758 | ||
|
|
37c154b6de | ||
|
|
f201f63e49 | ||
|
|
691113ca7c | ||
|
|
ea20911a85 | ||
|
|
b0d1c3b99c | ||
|
|
8cf9d13ca4 | ||
|
|
47c09ac36c | ||
|
|
4afcdeb771 | ||
|
|
e217ad3929 | ||
|
|
1d061da98f | ||
|
|
59d3b3d85a | ||
|
|
433c094e95 | ||
|
|
5980e665a8 | ||
|
|
3b175fdb0e | ||
|
|
145a4952e5 | ||
|
|
f165414e13 | ||
|
|
1b3799ecde | ||
|
|
3369356788 | ||
|
|
b8d81e1081 | ||
|
|
0b37bad2cb | ||
|
|
82196efe94 | ||
|
|
4934f5c56d | ||
|
|
e360544001 | ||
|
|
03b681c056 | ||
|
|
1bc07a4e06 | ||
|
|
1e915b1248 | ||
|
|
da32b5f837 | ||
|
|
cfe5a5f188 | ||
|
|
29b09b0900 | ||
|
|
9d2ea99753 | ||
|
|
72fdddfed3 | ||
|
|
793329fd56 | ||
|
|
55bd2eb2e1 | ||
|
|
c359d03b60 | ||
|
|
49bdeb3c46 | ||
|
|
db281f60fe | ||
|
|
7286dfa38a | ||
|
|
50f2154cbb | ||
|
|
642bc5d8f3 | ||
|
|
f74ae5f9c0 | ||
|
|
df6626f06b | ||
|
|
5fc079d9ce | ||
|
|
af301fac55 | ||
|
|
175a6ab606 | ||
|
|
a53ec40df1 | ||
|
|
1abd5cc665 | ||
|
|
08c47b2d61 | ||
|
|
c916238e5c | ||
|
|
f76a17b33f | ||
|
|
dba299ac6a | ||
|
|
6c5f79c0df | ||
|
|
23c68cfc5b | ||
|
|
9874848f83 | ||
|
|
d3bc2ac1a9 | ||
|
|
462a583d98 | ||
|
|
c5691f816a | ||
|
|
fa3cf4d613 | ||
|
|
74b8dda181 | ||
|
|
5f37f7d86f | ||
|
|
dbcc7966cf | ||
|
|
a3642bd8d9 | ||
|
|
771ec8324c | ||
|
|
691defdc5d | ||
|
|
3b831271ee | ||
|
|
3a13200772 | ||
|
|
aecc83e2fc | ||
|
|
170fd845f2 | ||
|
|
d126c099f4 | ||
|
|
6063deb6bd | ||
|
|
291e95e3c5 | ||
|
|
bccbefdc1c | ||
|
|
d07ec56c33 | ||
|
|
b00c13a00e | ||
|
|
eaf85607f4 | ||
|
|
a7323c9805 | ||
|
|
50d0aced7f | ||
|
|
0bd15be1a1 | ||
|
|
370476cc14 | ||
|
|
9a7565d66c | ||
|
|
52f1000955 | ||
|
|
9d02e0ee6f | ||
|
|
1facbde113 | ||
|
|
b5e95bf632 | ||
|
|
6c20673737 | ||
|
|
b52317537c | ||
|
|
6e98453189 | ||
|
|
e435a45af7 | ||
|
|
419100d42b | ||
|
|
67b5cd9c0e | ||
|
|
ca6d1fd47a | ||
|
|
fb0d0245db | ||
|
|
e0802d2dea | ||
|
|
4441662490 | ||
|
|
8db978bb10 | ||
|
|
894c3abb37 | ||
|
|
2a91d3cf68 | ||
|
|
d15a0a4acb | ||
|
|
e0cba05167 | ||
|
|
2f9c964753 | ||
|
|
c0b021e196 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -33,7 +33,7 @@ assignees: ''
|
||||
|
||||
### Versions
|
||||
|
||||
[Output of `lean --version` in the folder that the issue occured in]
|
||||
[Output of `#eval Lean.versionString` or of `lean --version` in the folder that the issue occured in]
|
||||
[OS version]
|
||||
|
||||
### Additional Information
|
||||
|
||||
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,14 +1,14 @@
|
||||
* [ ] Put an X in this bracket to confirm you have read the
|
||||
[External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/doc/contributions.md).
|
||||
# Read this section before submitting
|
||||
|
||||
* Please put the link to your `RFC` or `bug` issue here.
|
||||
PRs missing this link will be marked as `missing RFC`.
|
||||
* Ensure your PR follows the [External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/CONTRIBUTING.md).
|
||||
* Please make sure the PR has excellent documentation and tests. If we label it `missing documentation` or `missing tests` then it needs fixing!
|
||||
* Include the link to your `RFC` or `bug` issue in the description.
|
||||
* If the issue does not already have approval from a developer, submit the PR as draft.
|
||||
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
|
||||
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
|
||||
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
|
||||
* Remove this section, up to and including the `---` before submitting.
|
||||
|
||||
* If that issue does not already have approval from a developer,
|
||||
please be sure to open this PR in "Draft" mode.
|
||||
---
|
||||
|
||||
* Please make sure the PR has excellent documentation and tests.
|
||||
If we label it `missing documentation` or `missing tests` then it needs fixing!
|
||||
|
||||
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels
|
||||
yourself, by writing a comment containing one of these labels on its own line.
|
||||
Closes #0000 (`RFC` or `bug` issue number fixed by this PR, if any)
|
||||
|
||||
22
.github/workflows/actionlint.yml
vendored
Normal file
22
.github/workflows/actionlint.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Actionlint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '.github/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/**'
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: actionlint
|
||||
uses: raven-actions/actionlint@v1
|
||||
with:
|
||||
pyflakes: false # we do not use python scripts
|
||||
33
.github/workflows/changelog.yml
vendored
33
.github/workflows/changelog.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: add PR to changelog
|
||||
|
||||
on:
|
||||
# needs read/write GH token, do *not* execute arbitrary code from PR
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
update-changelog:
|
||||
if: |
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'changelog') &&
|
||||
github.base_ref == 'master'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
# needs sufficiently elevated token to override branch protection rules
|
||||
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
|
||||
- name: Update changelog
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
escaped_link=$(sed -e 's/[\/&]/\\&/g' <<'EOF'
|
||||
[${{ github.event.pull_request.title}}](${{ github.event.pull_request.html_url }})
|
||||
EOF
|
||||
)
|
||||
# insert link below first dashes line (https://stackoverflow.com/a/9453461/161659)
|
||||
sed -i "0,/^---*/s/^---*/\0\n\n* $escaped_link./" RELEASES.md
|
||||
# commit as github-actions bot (https://github.com/orgs/community/discussions/26560#discussioncomment-3252339)
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config user.name "github-actions[bot]"
|
||||
git commit -i RELEASES.md -m "doc: update changelog"
|
||||
git push
|
||||
393
.github/workflows/ci.yml
vendored
393
.github/workflows/ci.yml
vendored
@@ -6,8 +6,8 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
merge_group:
|
||||
schedule:
|
||||
- cron: '0 7 * * *' # 8AM CET/11PM PT
|
||||
|
||||
@@ -16,51 +16,203 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
set-nightly:
|
||||
|
||||
# This job determines various settings for the following CI runs; see the `outputs` for details
|
||||
configure:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
nightly: ${{ steps.set.outputs.nightly }}
|
||||
# Should we run only a quick CI? Yes on a pull request without the full-ci label
|
||||
quick: ${{ steps.set-quick.outputs.quick }}
|
||||
# The build matrix, dynamically generated here
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty
|
||||
nightly: ${{ steps.set-nightly.outputs.nightly }}
|
||||
# Should this be the CI for a tagged release?
|
||||
# Yes only if a tag is pushed to the `leanprover` repository, and the tag is "v" followed by a valid semver.
|
||||
# It sets `set-release.outputs.RELEASE_TAG` to the tag
|
||||
# and sets `set-release.outputs.{LEAN_VERSION_MAJOR,LEAN_VERSION_MINOR,LEAN_VERSION_PATCH,LEAN_SPECIAL_VERSION_DESC}`
|
||||
# to the semver components parsed via regex.
|
||||
LEAN_VERSION_MAJOR: ${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ steps.set-release.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ steps.set-release.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ steps.set-release.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ steps.set-release.outputs.RELEASE_TAG }}
|
||||
|
||||
steps:
|
||||
- name: Run quick CI?
|
||||
id: set-quick
|
||||
env:
|
||||
quick: ${{
|
||||
github.event_name == 'pull_request' && !contains( github.event.pull_request.labels.*.name, 'full-ci')
|
||||
}}
|
||||
run: |
|
||||
echo "quick=${{env.quick}}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure build matrix
|
||||
id: set-matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const quick = ${{ steps.set-quick.outputs.quick }};
|
||||
console.log(`quick: ${quick}`)
|
||||
let matrix = [
|
||||
{
|
||||
// portable release build: use channel with older glibc (2.27)
|
||||
"name": "Linux LLVM",
|
||||
"os": "ubuntu-latest",
|
||||
"release": false,
|
||||
"quick": false,
|
||||
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{}}\" --run \"bash -euxo pipefail {0}\"",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
|
||||
"binary-check": "ldd -v",
|
||||
// foreign code may be linked against more recent glibc
|
||||
// reverse-ffi needs to be updated to link to LLVM libraries
|
||||
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
|
||||
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
|
||||
},
|
||||
{
|
||||
"name": "Linux release",
|
||||
"os": "ubuntu-latest",
|
||||
"release": true,
|
||||
"quick": true,
|
||||
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{}}\" --run \"bash -euxo pipefail {0}\"",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
|
||||
"binary-check": "ldd -v",
|
||||
// foreign code may be linked against more recent glibc
|
||||
"CTEST_OPTIONS": "-E 'foreign'"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"os": "ubuntu-latest",
|
||||
"check-stage3": true,
|
||||
"test-speedcenter": true,
|
||||
"quick": false,
|
||||
},
|
||||
{
|
||||
"name": "Linux Debug",
|
||||
"os": "ubuntu-latest",
|
||||
"quick": false,
|
||||
"CMAKE_OPTIONS": "-DCMAKE_BUILD_TYPE=Debug",
|
||||
// exclude seriously slow tests
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
|
||||
},
|
||||
{
|
||||
"name": "Linux fsanitize",
|
||||
"os": "ubuntu-latest",
|
||||
"quick": false,
|
||||
// turn off custom allocator & symbolic functions to make LSAN do its magic
|
||||
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF",
|
||||
// exclude seriously slow/problematic tests (laketests crash)
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
|
||||
},
|
||||
{
|
||||
"name": "macOS",
|
||||
"os": "macos-latest",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
|
||||
"binary-check": "otool -L",
|
||||
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
|
||||
},
|
||||
{
|
||||
"name": "macOS aarch64",
|
||||
"os": "macos-latest",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"cross_target": "aarch64-apple-darwin",
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*",
|
||||
"binary-check": "otool -L",
|
||||
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
"os": "windows-2022",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"shell": "msys2 {0}",
|
||||
"CMAKE_OPTIONS": "-G \"Unix Makefiles\" -DUSE_GMP=OFF",
|
||||
// for reasons unknown, interactivetests are flaky on Windows
|
||||
"CTEST_OPTIONS": "--repeat until-pass:2",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
|
||||
"binary-check": "ldd"
|
||||
},
|
||||
{
|
||||
"name": "Linux aarch64",
|
||||
"os": "ubuntu-latest",
|
||||
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64",
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"cross_target": "aarch64-unknown-linux-gnu",
|
||||
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{ localSystem.config = \\\"aarch64-unknown-linux-gnu\\\"; }}\" --run \"bash -euxo pipefail {0}\"",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
|
||||
},
|
||||
{
|
||||
"name": "Linux 32bit",
|
||||
"os": "ubuntu-latest",
|
||||
// Use 32bit on stage0 and stage1 to keep oleans compatible
|
||||
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86",
|
||||
"cmultilib": true,
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"shell": "bash -euxo pipefail {0}"
|
||||
},
|
||||
{
|
||||
"name": "Web Assembly",
|
||||
"os": "ubuntu-latest",
|
||||
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
|
||||
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32",
|
||||
"wasm": true,
|
||||
"cmultilib": true,
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
// Just a few selected tests because wasm is slow
|
||||
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean\""
|
||||
}
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
|
||||
if (quick) {
|
||||
return matrix.filter((job) => job.quick)
|
||||
} else {
|
||||
return matrix
|
||||
}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# don't schedule nightlies on forks
|
||||
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
|
||||
- name: Set Nightly
|
||||
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
|
||||
id: set
|
||||
id: set-nightly
|
||||
run: |
|
||||
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
|
||||
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
|
||||
git fetch nightly --tags
|
||||
LEAN_VERSION_STRING="nightly-$(date -u +%F)"
|
||||
# do nothing if commit already has a different tag
|
||||
if [[ $(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo $LEAN_VERSION_STRING) == $LEAN_VERSION_STRING ]]; then
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> $GITHUB_OUTPUT
|
||||
if [[ "$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo "$LEAN_VERSION_STRING")" == "$LEAN_VERSION_STRING" ]]; then
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# This job determines if this CI build is for a tagged release.
|
||||
# It only runs when a tag is pushed to the `leanprover` repository.
|
||||
# It sets `set-release.outputs.RELEASE_TAG` to the tag, if the tag is "v" followed by a valid semver,
|
||||
# and sets `set-release.outputs.{LEAN_VERSION_MAJOR,LEAN_VERSION_MINOR,LEAN_VERSION_PATCH,LEAN_SPECIAL_VERSION_DESC}`
|
||||
# to the semver components parsed via regex.
|
||||
set-release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
LEAN_VERSION_MAJOR: ${{ steps.set.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ steps.set.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ steps.set.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ steps.set.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ steps.set.outputs.RELEASE_TAG }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4'
|
||||
- name: Check for official release
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4'
|
||||
id: set
|
||||
id: set-release
|
||||
run: |
|
||||
TAG_NAME=${GITHUB_REF##*/}
|
||||
TAG_NAME="${GITHUB_REF##*/}"
|
||||
|
||||
# From https://github.com/fsaintjacques/semver-tool/blob/master/src/semver
|
||||
|
||||
@@ -77,108 +229,29 @@ jobs:
|
||||
|
||||
if [[ ${TAG_NAME} =~ ${SEMVER_REGEX} ]]; then
|
||||
echo "Tag ${TAG_NAME} matches SemVer regex, with groups ${BASH_REMATCH[1]} ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} ${BASH_REMATCH[4]}"
|
||||
echo "LEAN_VERSION_MAJOR=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_VERSION_MINOR=${BASH_REMATCH[2]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_VERSION_PATCH=${BASH_REMATCH[3]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_SPECIAL_VERSION_DESC=${BASH_REMATCH[4]##-}" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_TAG=$TAG_NAME" >> $GITHUB_OUTPUT
|
||||
{
|
||||
echo "LEAN_VERSION_MAJOR=${BASH_REMATCH[1]}"
|
||||
echo "LEAN_VERSION_MINOR=${BASH_REMATCH[2]}"
|
||||
echo "LEAN_VERSION_PATCH=${BASH_REMATCH[3]}"
|
||||
echo "LEAN_SPECIAL_VERSION_DESC=${BASH_REMATCH[4]##-}"
|
||||
echo "RELEASE_TAG=$TAG_NAME"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Tag ${TAG_NAME} did not match SemVer regex."
|
||||
fi
|
||||
|
||||
build:
|
||||
needs: [set-nightly, set-release]
|
||||
needs: [configure]
|
||||
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{fromJson(needs.configure.outputs.matrix)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'nix-shell --run "bash -euxo pipefail {0}"' }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# portable release build: use channel with older glibc (2.27)
|
||||
- name: Linux LLVM
|
||||
os: ubuntu-latest
|
||||
release: false
|
||||
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{}}" --run "bash -euxo pipefail {0}"
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst
|
||||
prepare-llvm: ../script/prepare-llvm-linux.sh lean-llvm*
|
||||
binary-check: ldd -v
|
||||
# foreign code may be linked against more recent glibc
|
||||
# reverse-ffi needs to be updated to link to LLVM libraries
|
||||
CTEST_OPTIONS: -E 'foreign|leanlaketest_reverse-ffi'
|
||||
CMAKE_OPTIONS: -DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config
|
||||
- name: Linux release
|
||||
os: ubuntu-latest
|
||||
release: true
|
||||
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{}}" --run "bash -euxo pipefail {0}"
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst
|
||||
prepare-llvm: ../script/prepare-llvm-linux.sh lean-llvm*
|
||||
binary-check: ldd -v
|
||||
# foreign code may be linked against more recent glibc
|
||||
CTEST_OPTIONS: -E 'foreign'
|
||||
- name: Linux
|
||||
os: ubuntu-latest
|
||||
check-stage3: true
|
||||
test-speedcenter: true
|
||||
- name: Linux Debug
|
||||
os: ubuntu-latest
|
||||
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Debug
|
||||
# exclude seriously slow tests
|
||||
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
|
||||
- name: Linux fsanitize
|
||||
os: ubuntu-latest
|
||||
# turn off custom allocator & symbolic functions to make LSAN do its magic
|
||||
CMAKE_OPTIONS: -DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF
|
||||
# exclude seriously slow/problematic tests (laketests crash)
|
||||
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
|
||||
- name: macOS
|
||||
os: macos-latest
|
||||
release: true
|
||||
shell: bash -euxo pipefail {0}
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
|
||||
prepare-llvm: ../script/prepare-llvm-macos.sh lean-llvm*
|
||||
binary-check: otool -L
|
||||
tar: gtar # https://github.com/actions/runner-images/issues/2619
|
||||
- name: macOS aarch64
|
||||
os: macos-latest
|
||||
release: true
|
||||
cross: true
|
||||
shell: bash -euxo pipefail {0}
|
||||
CMAKE_OPTIONS: -DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
|
||||
prepare-llvm: EXTRA_FLAGS=--target=aarch64-apple-darwin ../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
|
||||
binary-check: otool -L
|
||||
tar: gtar # https://github.com/actions/runner-images/issues/2619
|
||||
- name: Windows
|
||||
os: windows-2022
|
||||
release: true
|
||||
shell: msys2 {0}
|
||||
CMAKE_OPTIONS: -G "Unix Makefiles" -DUSE_GMP=OFF
|
||||
# for reasons unknown, interactivetests are flaky on Windows
|
||||
CTEST_OPTIONS: --repeat until-pass:2
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst
|
||||
prepare-llvm: ../script/prepare-llvm-mingw.sh lean-llvm*
|
||||
binary-check: ldd
|
||||
- name: Linux aarch64
|
||||
os: ubuntu-latest
|
||||
CMAKE_OPTIONS: -DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64
|
||||
release: true
|
||||
cross: true
|
||||
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{ localSystem.config = \"aarch64-unknown-linux-gnu\"; }}" --run "bash -euxo pipefail {0}"
|
||||
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst
|
||||
prepare-llvm: EXTRA_FLAGS=--target=aarch64-unknown-linux-gnu ../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
|
||||
- name: Web Assembly
|
||||
os: ubuntu-latest
|
||||
# Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
|
||||
CMAKE_OPTIONS: -DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX="" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
|
||||
wasm: true
|
||||
cross: true
|
||||
shell: bash -euxo pipefail {0}
|
||||
# Just a few selected test because wasm is slow
|
||||
CTEST_OPTIONS: -R "leantest_1007\.lean|leantest_Format\.lean|leanruntest\_1037.lean|leanruntest_ac_rfl\.lean"
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
# must be inside workspace
|
||||
@@ -197,11 +270,13 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
# 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 }}
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v18
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
|
||||
if: matrix.os == 'ubuntu-latest' && !matrix.wasm
|
||||
if: matrix.os == 'ubuntu-latest' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -214,7 +289,7 @@ jobs:
|
||||
brew install ccache tree zstd coreutils gmp
|
||||
if: matrix.os == 'macos-latest'
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v11
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
version: 3.1.44
|
||||
actions-cache-folder: emsdk
|
||||
@@ -223,7 +298,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache
|
||||
if: matrix.wasm
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -248,21 +323,29 @@ jobs:
|
||||
mkdir build
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
OPTIONS=()
|
||||
# arguments passed to `cmake`
|
||||
# this also enables githash embedding into stage 1 library
|
||||
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
|
||||
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
|
||||
if [[ -n '${{ matrix.cross_target }}' ]]; then
|
||||
# used by `prepare-llvm`
|
||||
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
|
||||
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
|
||||
wget -q ${{ matrix.llvm-url }}
|
||||
PREPARE="$(${{ matrix.prepare-llvm }})"
|
||||
eval "OPTIONS+=($PREPARE)"
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.set-nightly.outputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.set-nightly.outputs.nightly }})
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.nightly }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.set-release.outputs.RELEASE_TAG }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.set-release.outputs.LEAN_VERSION_MAJOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.set-release.outputs.LEAN_VERSION_MINOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.set-release.outputs.LEAN_VERSION_PATCH }})
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.configure.outputs.LEAN_VERSION_MAJOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.configure.outputs.LEAN_VERSION_MINOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.configure.outputs.LEAN_VERSION_PATCH }})
|
||||
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.set-release.outputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
@@ -273,13 +356,13 @@ jobs:
|
||||
- name: List Install Tree
|
||||
run: |
|
||||
# omit contents of Init/, ...
|
||||
tree --du -h lean-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
dir=$(echo lean-*)
|
||||
dir=$(echo lean-*-*)
|
||||
mkdir pack
|
||||
# high-compression tar.zst + zip for release, fast tar.zst otherwise
|
||||
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.set-nightly.outputs.nightly }}' || -n '${{ needs.set-release.outputs.RELEASE_TAG }}' ]]; then
|
||||
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.configure.outputs.nightly }}' || -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
|
||||
zip -rq pack/$dir.zip $dir
|
||||
else
|
||||
@@ -300,22 +383,22 @@ jobs:
|
||||
ulimit -c unlimited # coredumps
|
||||
# exclude nonreproducible test
|
||||
ctest -j4 --output-on-failure ${{ matrix.CTEST_OPTIONS }} < /dev/null
|
||||
if: matrix.wasm || !matrix.cross
|
||||
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.quick == 'false'
|
||||
- name: Check Test Binary
|
||||
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
|
||||
if: ${{ !matrix.cross }}
|
||||
if: ${{ !matrix.cross && needs.configure.outputs.quick == 'false' }}
|
||||
- name: Build Stage 2
|
||||
run: |
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
make -j4 stage2
|
||||
if: matrix.build-stage2 || matrix.check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
make -j4 check-stage3
|
||||
if: matrix.check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||
@@ -328,11 +411,11 @@ jobs:
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
make update-stage0 && make -j4
|
||||
if: matrix.name == 'Linux'
|
||||
if: matrix.name == 'Linux' && needs.configure.outputs.quick == 'false'
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
- name: Show stacktrace for coredumps
|
||||
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
for c in coredumps/*; do
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
@@ -340,7 +423,7 @@ jobs:
|
||||
done
|
||||
- name: Upload coredumps
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: coredumps-${{ matrix.name }}
|
||||
path: |
|
||||
@@ -352,6 +435,21 @@ jobs:
|
||||
./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
|
||||
# matrix job separately
|
||||
all-done:
|
||||
name: Build matrix complete
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: ${{ always() }}
|
||||
steps:
|
||||
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Some jobs failed')
|
||||
|
||||
# This job creates releases from tags
|
||||
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
|
||||
# We do not attempt to automatically construct a changelog here:
|
||||
@@ -375,8 +473,8 @@ jobs:
|
||||
# This job creates nightly releases during the cron job.
|
||||
# It is responsible for creating the tag, and automatically generating a changelog.
|
||||
release-nightly:
|
||||
needs: [set-nightly, build]
|
||||
if: needs.set-nightly.outputs.nightly
|
||||
needs: [configure, build]
|
||||
if: needs.configure.outputs.nightly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -392,15 +490,16 @@ jobs:
|
||||
run: |
|
||||
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
|
||||
git fetch nightly --tags
|
||||
git tag ${{ needs.set-nightly.outputs.nightly }}
|
||||
git push nightly ${{ needs.set-nightly.outputs.nightly }}
|
||||
last_tag=$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)
|
||||
git tag "${{ needs.configure.outputs.nightly }}"
|
||||
git push nightly "${{ needs.configure.outputs.nightly }}"
|
||||
git push -f origin refs/tags/${{ needs.configure.outputs.nightly }}:refs/heads/nightly
|
||||
last_tag="$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)"
|
||||
echo -e "*Changes since ${last_tag}:*\n\n" > diff.md
|
||||
git show $last_tag:RELEASES.md > old.md
|
||||
git show "$last_tag":RELEASES.md > old.md
|
||||
#./script/diff_changelogs.py old.md doc/changes.md >> diff.md
|
||||
diff --changed-group-format='%>' --unchanged-group-format='' old.md RELEASES.md >> diff.md || true
|
||||
echo -e "\n*Full commit log*\n" >> diff.md
|
||||
git log --oneline $last_tag..HEAD | sed 's/^/* /' >> diff.md
|
||||
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
|
||||
- name: Release Nightly
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
@@ -408,7 +507,7 @@ jobs:
|
||||
prerelease: true
|
||||
files: artifacts/*/*
|
||||
fail_on_unmatched_files: true
|
||||
tag_name: ${{ needs.set-nightly.outputs.nightly }}
|
||||
tag_name: ${{ needs.configure.outputs.nightly }}
|
||||
repository: ${{ github.repository_owner }}/lean4-nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
|
||||
|
||||
2
.github/workflows/labels-from-comments.yml
vendored
2
.github/workflows/labels-from-comments.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Add label based on comment
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
62
.github/workflows/nix-ci.yml
vendored
62
.github/workflows/nix-ci.yml
vendored
@@ -6,8 +6,7 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -18,7 +17,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: nix -v --experimental-features "nix-command flakes" run .#ciShell -- bash -euxo pipefail {0}
|
||||
shell: nix run .#ciShell -- bash -euxo pipefail {0}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -30,18 +29,13 @@ jobs:
|
||||
fail-fast: false
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
NIX_BUILD_ARGS: -v --print-build-logs --fallback
|
||||
NIX_BUILD_ARGS: --print-build-logs --fallback
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v18
|
||||
with:
|
||||
# https://github.com/NixOS/nix/issues/6572
|
||||
install_url: https://releases.nixos.org/nix/nix-2.7.0/install
|
||||
extra_nix_config: |
|
||||
extra-sandbox-paths = /nix/var/cache/ccache
|
||||
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
|
||||
# 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 }}
|
||||
- name: Set Up Nix Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -55,8 +49,13 @@ jobs:
|
||||
run: |
|
||||
# Nix seems to mutate the cache, so make a copy
|
||||
cp -r nix-store-cache nix-store-cache-copy || true
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
extra-conf: |
|
||||
extra-sandbox-paths = /nix/var/cache/ccache?
|
||||
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
|
||||
- name: Prepare CCache Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
sudo mkdir -m0770 -p /nix/var/cache/ccache
|
||||
sudo chown -R $USER /nix/var/cache/ccache
|
||||
@@ -69,7 +68,6 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-nix-ccache
|
||||
- name: Further Set Up CCache Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
sudo chown -R root:nixbld /nix/var/cache
|
||||
sudo chmod -R 770 /nix/var/cache
|
||||
@@ -89,7 +87,17 @@ jobs:
|
||||
run: |
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test,inked} -o push-doc
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
|
||||
# https://github.com/netlify/cli/issues/1809
|
||||
cp -r --dereference ./result ./dist
|
||||
if: matrix.name == 'Nix Linux'
|
||||
- name: Check manual for broken links
|
||||
id: lychee
|
||||
uses: lycheeverse/lychee-action@v1.9.0
|
||||
with:
|
||||
fail: false # report errors but do not block CI on temporary failures
|
||||
# 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
|
||||
@@ -97,13 +105,29 @@ jobs:
|
||||
run: |
|
||||
rm -rf nix-store-cache || true
|
||||
nix copy ./push-* --to file://$PWD/nix-store-cache?compression=none
|
||||
- name: Publish manual
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
- id: deploy-info
|
||||
name: Compute Deployment Metadata
|
||||
run: |
|
||||
set -e
|
||||
python3 -c 'import base64; print("alias="+base64.urlsafe_b64encode(bytes.fromhex("${{github.sha}}")).decode("utf-8").rstrip("="))' >> "$GITHUB_OUTPUT"
|
||||
echo "message=`git log -1 --pretty=format:"%s"`" >> "$GITHUB_OUTPUT"
|
||||
- name: Publish manual to Netlify
|
||||
uses: nwtgck/actions-netlify@v2.0
|
||||
id: publish-manual
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./result
|
||||
destination_dir: ./doc
|
||||
if: matrix.name == 'Nix Linux' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
publish-dir: ./dist
|
||||
production-branch: master
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
deploy-message: |
|
||||
${{ github.event_name == 'pull_request' && format('pr#{0}: {1}', github.event.number, github.event.pull_request.title) || format('ref/{0}: {1}', github.ref_name, steps.deploy-info.outputs.message) }}
|
||||
alias: ${{ steps.deploy-info.outputs.alias }}
|
||||
enable-commit-comment: false
|
||||
enable-pull-request-comment: false
|
||||
github-deployment-environment: "lean-lang.org/lean4/doc"
|
||||
fails-without-credentials: false
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: "b8e805d2-7e9b-4f80-91fb-a84d72fc4a68"
|
||||
- name: Fixup CCache Cache
|
||||
run: |
|
||||
sudo chown -R $USER /nix/var/cache
|
||||
|
||||
301
.github/workflows/pr-release.yml
vendored
301
.github/workflows/pr-release.yml
vendored
@@ -6,6 +6,10 @@
|
||||
# Instead we use `workflow_run`, which essentially allows us to escalate privileges
|
||||
# (but only runs the CI as described in the `master` branch, not in the PR branch).
|
||||
|
||||
# The main specification/documentation for this workflow is at
|
||||
# https://leanprover-community.github.io/contribute/tags_and_branches.html
|
||||
# Keep that in sync!
|
||||
|
||||
name: PR release
|
||||
|
||||
on:
|
||||
@@ -16,25 +20,16 @@ on:
|
||||
jobs:
|
||||
on-success:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.repository == 'leanprover/lean4'
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
|
||||
steps:
|
||||
- name: Retrieve information about the original workflow
|
||||
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
|
||||
# This action is deprecated and archived, but it seems hard to find a better solution for getting the PR number
|
||||
# see https://github.com/orgs/community/discussions/25220 for some discussion
|
||||
id: workflow-info
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sourceRunId: ${{ github.event.workflow_run.id }}
|
||||
- name: Checkout
|
||||
# Only proceed if the previous workflow had a pull request number.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
# Since `workflow_run` runs on master, we need to specify which commit to check out,
|
||||
# so that we tag the PR.
|
||||
ref: ${{ steps.workflow-info.outputs.targetCommitSha }}
|
||||
# We need a full checkout, so that we can push the PR commits to the `lean4-pr-releases` repo.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
@@ -45,14 +40,22 @@ jobs:
|
||||
path: artifacts
|
||||
name: build-.*
|
||||
name_is_regexp: true
|
||||
- name: Prepare release
|
||||
|
||||
- name: Push tag
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
git init --bare lean4.git
|
||||
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
|
||||
git -C lean4.git fetch -n origin master
|
||||
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
|
||||
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
|
||||
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
|
||||
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
- name: Delete existing release if present
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
|
||||
# Try to delete any existing release for the current PR.
|
||||
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
|
||||
git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
- name: Release
|
||||
@@ -70,57 +73,267 @@ jobs:
|
||||
# The token used here must have `workflow` privileges.
|
||||
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
|
||||
- name: Report release status
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
await github.rest.repos.createCommitStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
|
||||
state: "success",
|
||||
context: "PR toolchain",
|
||||
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}",
|
||||
});
|
||||
|
||||
- name: Add label
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
number: ${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
labels: toolchain-available
|
||||
script: |
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: ${{ steps.workflow-info.outputs.pullRequestNumber }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['toolchain-available']
|
||||
})
|
||||
|
||||
# We next automatically create a Mathlib branch using this toolchain.
|
||||
# Mathlib CI will be responsible for reporting back success or failure
|
||||
# to the PR comments asynchronously.
|
||||
- name: Cleanup workspace
|
||||
# Next, determine the most recent nightly release in this PR's history.
|
||||
- name: Find most recent nightly in feature branch
|
||||
id: most-recent-nightly-tag
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
sudo rm -rf *
|
||||
git -C lean4.git remote add nightly https://github.com/leanprover/lean4-nightly.git
|
||||
git -C lean4.git fetch nightly '+refs/tags/nightly-*:refs/tags/nightly-*'
|
||||
git -C lean4.git tag --merged "${{ steps.workflow-info.outputs.sourceHeadSha }}" --list "nightly-*" \
|
||||
| sort -rV | head -n 1 | sed "s/^nightly-*/MOST_RECENT_NIGHTLY=/" | tee -a "$GITHUB_ENV"
|
||||
|
||||
# Checkout the mathlib4 repository with all branches
|
||||
- name: Checkout mathlib4 repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: leanprover-community/mathlib4
|
||||
token: ${{ secrets.MATHLIB4_BOT }}
|
||||
ref: nightly-testing # This is more likely than `master` to work with the base of this PR.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check if branch exists
|
||||
- name: 'Setup jq'
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: check_branch
|
||||
uses: dcarbone/install-jq-action@v1.0.1
|
||||
|
||||
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
|
||||
- name: Check merge-base and nightly-testing-YYYY-MM-DD
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: ready
|
||||
run: |
|
||||
echo "Most recent nightly release in your branch: $MOST_RECENT_NIGHTLY"
|
||||
NIGHTLY_SHA=$(git -C lean4.git rev-parse "nightly-$MOST_RECENT_NIGHTLY^{commit}")
|
||||
echo "SHA of most recent nightly release: $NIGHTLY_SHA"
|
||||
MERGE_BASE_SHA=$(git -C lean4.git merge-base origin/master "${{ steps.workflow-info.outputs.sourceHeadSha }}")
|
||||
echo "SHA of merge-base: $MERGE_BASE_SHA"
|
||||
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
|
||||
echo "The merge base of this PR coincides with the nightly release"
|
||||
|
||||
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
|
||||
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE=""
|
||||
else
|
||||
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
|
||||
fi
|
||||
|
||||
STD_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover/std4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
|
||||
if [[ -n "$STD_REMOTE_TAGS" ]]; then
|
||||
echo "... and Std has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE=""
|
||||
else
|
||||
echo "... but Std does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Std CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Std CI should run now."
|
||||
fi
|
||||
|
||||
else
|
||||
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
|
||||
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."
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
|
||||
echo "Checking existing messages"
|
||||
|
||||
# The code for updating comments is duplicated in mathlib's
|
||||
# scripts/lean-pr-testing-comments.sh
|
||||
# so keep in sync
|
||||
|
||||
# Use GitHub API to check if a comment already exists
|
||||
existing_comment="$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-mathlib4-bot"))')"
|
||||
existing_comment_id="$(echo "$existing_comment" | jq -r .id)"
|
||||
existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
|
||||
|
||||
if [[ "$existing_comment_body" != *"$MESSAGE"* ]]; then
|
||||
MESSAGE="$MESSAGE ($(date "+%Y-%m-%d %H:%M:%S"))"
|
||||
|
||||
echo "Posting message to the comments: $MESSAGE"
|
||||
|
||||
# Append new result to the existing comment or post a new comment
|
||||
# It's essential we use the MATHLIB4_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
if [ -z "$existing_comment_id" ]; then
|
||||
INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):"
|
||||
# Post new comment with a bullet point
|
||||
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $val)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
else
|
||||
# Append new result to the existing comment
|
||||
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X PATCH \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
|
||||
fi
|
||||
else
|
||||
echo "The message already exists in the comment body."
|
||||
fi
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Report mathlib base
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const description =
|
||||
process.env.MOST_RECENT_NIGHTLY ?
|
||||
"nightly-" + process.env.MOST_RECENT_NIGHTLY :
|
||||
"not branched off nightly";
|
||||
await github.rest.repos.createCommitStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
|
||||
state: "success",
|
||||
context: "PR branched off:",
|
||||
description: description,
|
||||
});
|
||||
|
||||
# We next automatically create a Std branch using this toolchain.
|
||||
# Std doesn't itself have a mechanism to report results of CI from this branch back to Lean
|
||||
# Instead this is taken care of by Mathlib CI, which will fail if Std fails.
|
||||
- name: Cleanup workspace
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
sudo rm -rf ./*
|
||||
|
||||
# Checkout the Std repository with all branches
|
||||
- name: Checkout Std repository
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: leanprover/std4
|
||||
token: ${{ secrets.MATHLIB4_BOT }}
|
||||
ref: nightly-testing
|
||||
fetch-depth: 0 # This ensures we check out all tags and branches.
|
||||
|
||||
- name: Check if tag exists
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
id: check_std_tag
|
||||
run: |
|
||||
git config user.name "leanprover-community-mathlib4-bot"
|
||||
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
|
||||
|
||||
EXISTS=$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Std. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
echo "Using base branch: $BASE"
|
||||
|
||||
EXISTS="$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)"
|
||||
echo "Branch exists: $EXISTS"
|
||||
if [ "$EXISTS" = "0" ]; then
|
||||
echo "Branch does not exist, creating it."
|
||||
git checkout -b lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git checkout lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` branch may have moved since this branch was created, so merge their changes.
|
||||
# If the base of this Lean4 PR becomes significantly older than the nightly being used by `nightly-testing`
|
||||
# this will cause breakages rather than fixing them!
|
||||
# Without cumbersome requirements that Lean4 PRs are based off nightlies, I'm not sure there is a perfect solution here.
|
||||
git merge nightly-testing --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Std `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
- name: Push changes
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
|
||||
|
||||
# We next automatically create a Mathlib branch using this toolchain.
|
||||
# Mathlib CI will be responsible for reporting back success or failure
|
||||
# to the PR comments asynchronously.
|
||||
- name: Cleanup workspace
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
sudo rm -rf ./*
|
||||
|
||||
# Checkout the mathlib4 repository with all branches
|
||||
- name: Checkout mathlib4 repository
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: leanprover-community/mathlib4
|
||||
token: ${{ secrets.MATHLIB4_BOT }}
|
||||
ref: nightly-testing
|
||||
fetch-depth: 0 # This ensures we check out all tags and branches.
|
||||
|
||||
- name: Check if tag exists
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
id: check_mathlib_tag
|
||||
run: |
|
||||
git config user.name "leanprover-community-mathlib4-bot"
|
||||
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
|
||||
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
echo "Using base tag: $BASE"
|
||||
|
||||
EXISTS="$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)"
|
||||
echo "Branch exists: $EXISTS"
|
||||
if [ "$EXISTS" = "0" ]; then
|
||||
echo "Branch does not exist, creating it."
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
sed -i "s/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \".\+\"/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
|
||||
git add lakefile.lean
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
- name: Push changes
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
|
||||
20
.github/workflows/pr-title.yml
vendored
Normal file
20
.github/workflows/pr-title.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Check PR title for commit convention
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
jobs:
|
||||
check-pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR title
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const msg = context.payload.pull_request? context.payload.pull_request.title : context.payload.merge_group.head_commit.message;
|
||||
console.log(`Message: ${msg}`)
|
||||
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(msg)) {
|
||||
core.setFailed('PR title does not follow the Commit Convention (https://leanprover.github.io/lean4/doc/dev/commit_convention.html).');
|
||||
}
|
||||
31
.github/workflows/pr.yml
vendored
31
.github/workflows/pr.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: sanity-check opened PRs
|
||||
|
||||
on:
|
||||
# needs read/write GH token, do *not* execute arbitrary code from PR
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
check-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Commit Message
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { data: commits } = await github.rest.pulls.listCommits({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
});
|
||||
console.log(commits[0].commit.message);
|
||||
// check first commit only (and only once) since later commits might be intended to be squashed away
|
||||
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(commits[0].commit.message)) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: 'Thanks for your contribution! Please make sure to follow our [Commit Convention](https://leanprover.github.io/lean4/doc/dev/commit_convention.html).',
|
||||
});
|
||||
}
|
||||
64
.github/workflows/update-stage0.yml
vendored
Normal file
64
.github/workflows/update-stage0.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Update stage0
|
||||
|
||||
# This action will update stage0 on master as soon as
|
||||
# src/stdlib_flags.h and stage0/src/stdlib_flags.h
|
||||
# are out of sync there, or when manually triggered.
|
||||
# The update bypasses the merge queue to be quick.
|
||||
# Also see <doc/dev/bootstrap.md>.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: stage0
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-stage0:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# This action should push to an otherwise protected branch, so it
|
||||
# uses a deploy key with write permissions, as suggested at
|
||||
# https://stackoverflow.com/a/76135647/946226
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
|
||||
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
|
||||
- name: Check if automatic update is needed
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
if diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h
|
||||
then
|
||||
echo "src/stdlib_flags.h and stage0/src/stdlib_flags.h agree, nothing to do"
|
||||
echo "should_update_stage0=no" >> "$GITHUB_ENV"
|
||||
fi
|
||||
- name: Setup git user
|
||||
if: env.should_update_stage0 == 'yes'
|
||||
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
|
||||
with:
|
||||
name: lean4
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: nix run .#update-stage0-commit
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: git show --stat
|
||||
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'
|
||||
name: Sanity check # to avoid loops
|
||||
run: |
|
||||
diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h || exit 1
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: git push origin
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,6 +2,8 @@
|
||||
\#*
|
||||
.#*
|
||||
*.lock
|
||||
.lake
|
||||
lake-manifest.json
|
||||
build
|
||||
!/src/lake/Lake/Build
|
||||
GPATH
|
||||
|
||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"[markdown]": {
|
||||
"rewrap.wrappingColumn": 70
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ foreach(var ${vars})
|
||||
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
|
||||
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
|
||||
list(APPEND CL_ARGS "-D${var}=${${var}}")
|
||||
if("${var}" STREQUAL "USE_GMP")
|
||||
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
|
||||
# must forward options that generate incompatible .olean format
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
@@ -35,6 +35,8 @@ ExternalProject_add(stage0
|
||||
SOURCE_SUBDIR src
|
||||
BINARY_DIR stage0
|
||||
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
|
||||
# (however, `CHECK_OLEAN_VERSION=ON` in CI will override this as we need to
|
||||
# embed the githash into the stage 1 library built by stage 0)
|
||||
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
|
||||
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
|
||||
INSTALL_COMMAND "" # skip install
|
||||
|
||||
23
CODEOWNERS
Normal file
23
CODEOWNERS
Normal file
@@ -0,0 +1,23 @@
|
||||
# Code Owners
|
||||
#
|
||||
# Documents responsible people per component.
|
||||
# Listed persons will automatically be asked by GitHub to review a PR touching these paths.
|
||||
# If multiple names are listed, a review by any of them is considered sufficient by default.
|
||||
|
||||
/.github/ @Kha @semorrison
|
||||
/RELEASES.md @semorrison
|
||||
/src/ @leodemoura @Kha
|
||||
/src/Init/IO.lean @joehendrix
|
||||
/src/kernel/ @leodemoura
|
||||
/src/lake/ @tydeu
|
||||
/src/Lean/Compiler/ @leodemoura
|
||||
/src/Lean/Data/Lsp/ @mhuisi
|
||||
/src/Lean/Elab/Deriving/ @semorrison
|
||||
/src/Lean/Elab/Tactic/ @semorrison
|
||||
/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
|
||||
100
CONTRIBUTING.md
100
CONTRIBUTING.md
@@ -1,61 +1,79 @@
|
||||
# Contribution Guidelines
|
||||
External Contribution Guidelines
|
||||
============
|
||||
|
||||
Thank you for your interest in contributing to Lean! There are many ways to contribute and we appreciate all of them.
|
||||
In the past, we accepted most pull requests. This practice produced hard to maintain code, performance problems, and bugs. In order to improve the quality and maintainability of our codebase, we've established the following guidelines for external contributions.
|
||||
|
||||
## Bug reports
|
||||
Helpful links
|
||||
-------
|
||||
|
||||
Bug reports as new issues are always welcome. Please check the existing [issues](https://github.com/leanprover/lean4/issues) first.
|
||||
Reduce the issue to a self-contained, reproducible test case.
|
||||
If you have the chance, before reporting a bug, please search existing issues, as it's possible that
|
||||
someone else has already reported your error.
|
||||
If you're not sure if something is a bug or not, feel free to file a bug anyway. You may also want to discuss it with the Lean
|
||||
community using the [lean4 Zulip channel](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4).
|
||||
* [Development Setup](./doc/dev/index.md)
|
||||
* [Testing](./doc/dev/testing.md)
|
||||
* [Commit convention](./doc/dev/commit_convention.md)
|
||||
|
||||
## Simple fixes
|
||||
Before You Submit a Pull Request (PR):
|
||||
-------
|
||||
|
||||
Simple fixes for **typos and clear bugs** are welcome.
|
||||
**Start with an Issue**: Before submitting a PR, always open an issue discussing the problem you wish to solve or the feature you'd like to add. Use the prefix `RFC:` (request for comments) if you are proposing a new feature. Ask for feedback from other users. Take the time to summarize all the feedback. This allows the maintainers to evaluate your proposal more efficiently. When creating a RFC, consider the following questions:
|
||||
|
||||
# **IMPORTANT**
|
||||
- **User Experience**: How does this feature improve the user experience?
|
||||
|
||||
We are currently overwhelmed. We respectfully request that you hold off on submitting Pull Requests and creating Request for Comments (RFCs) at this time. Our team is actively seeking funding to expand the Lean development team and improve our capacity to review and integrate contributions. We appreciate your understanding and look forward to being able to accept contributions in the near future. In the meantime, the process described in the following sections is temporarily suspended.
|
||||
- **Beneficiaries**: Which Lean users and projects do benefit most from this feature/change?
|
||||
|
||||
## Documentation
|
||||
- **Community Feedback**: Have you sought feedback or insights from other Lean users?
|
||||
|
||||
Tutorial-like examples are very welcome.
|
||||
They are useful for finding rough edges and bugs in Lean 4, for highlighting new features, and for showing how to use Lean.
|
||||
If you want to store your tutorial in the Lean 4 repository to make sure future changes will not break it, we suggest the following workflow:
|
||||
* Contact one of the Lean developers on Zulip, and check whether your tutorial is a good match for the Lean 4 repository.
|
||||
* Send bug reports and report rough edges. We will work with you until the tutorial looks great.
|
||||
* Add plenty of comments and make sure others will be able to follow it.
|
||||
* Create a pull request in the Lean 4 repository. After merging, we will link it to the official documentation and make sure it becomes part of our test suite.
|
||||
- **Maintainability**: Will this change streamline code maintenance or simplify its structure?
|
||||
|
||||
You can use `.lean` or `.md` files to create your tutorial. The `.md` files are ideal when you want to format your prose using markdown. For an example, see [this `.md` file](https://github.com/leanprover/lean4/blob/master/doc/lean3changes.md).
|
||||
**Understand the Project**: Familiarize yourself with the project, existing issues, and latest commits. Ensure your contribution aligns with the project's direction and priorities.
|
||||
|
||||
Contributions to the reference manual are also welcome, but since Lean 4 is changing rapidly, please contact us first using Zulip
|
||||
to find out which parts are stable enough to document. We will work with you to get this kind of
|
||||
pull request merged. We are also happy to meet using Zoom, Skype or Google hangout to coordinate this kind of effort.
|
||||
**Stay Updated**: Regularly fetch and merge changes from the main branch to ensure your branch is up-to-date and can be smoothly integrated.
|
||||
|
||||
As Lean 4 matures, other forms of documentation (e.g., doc-strings) will be welcome too.
|
||||
**Help wanted**: We have issues tagged with ["help wanted"](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), if you want to contribute to the project, please take a look at them. If you are interested in one of them, post comments, ask questions, and engage with the core developers there.
|
||||
|
||||
## "Help wanted"
|
||||
Quality Over Quantity:
|
||||
-----
|
||||
|
||||
For issues marked as [`help wanted`](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), pull requests (PR) are welcome and we will work with you to get a PR merged. Some of these issues are nontrivial. If you are interested, please consider adding comments to the issue and/or messaging the Lean developers in [Zulip](https://leanprover.zulipchat.com/#).
|
||||
**Focused Changes**: Each PR should address a single, clearly-defined issue or feature. Avoid making multiple unrelated changes in a single PR.
|
||||
|
||||
## Unexpected Pull Requests
|
||||
**Write Tests**: Every new feature or bug fix should come with relevant tests. This ensures the robustness and reliability of the contribution.
|
||||
|
||||
We have very few core developers, and we cannot review arbitrary pull requests (PRs). Moreover, many features involve subtle tradeoffs, and it may require significant time and energy to even assess a proposed design. We suggest the following workflow:
|
||||
**Documentation**: Update relevant documentation, including comments in the code, to explain the logic and reasoning behind your changes.
|
||||
|
||||
* First, discuss your idea with the Lean community on Zulip. Ask the community to help collect examples, document the requirements, and detect complications.
|
||||
* If there is broad support, create a detailed issue for it on the Lean 4 repository at GitHub, and tag the issue with `RFC`.
|
||||
* Ask the community for help documenting the requirements, and for collecting examples and concerns.
|
||||
* Wait for one of the core developers to give you a "go ahead". At this point, the core developers will work with you to make sure your PR gets merged.
|
||||
Coding Standards:
|
||||
----
|
||||
|
||||
We don't want to waste your time by you implementing a feature and then us not being able to merge it.
|
||||
**Follow the Code Style**: Ensure that your code follows the established coding style of the project.
|
||||
|
||||
## How to Contribute
|
||||
**Lean on Lean**: Use Lean's built-in features and libraries effectively, avoiding reinventions.
|
||||
|
||||
* Always follow the [commit convention](https://lean-lang.org/lean4/doc/dev/commit_convention.html).
|
||||
* Follow the style of the surrounding code. When in doubt, look at other files using the particular syntax as well.
|
||||
* Make sure your code is documented.
|
||||
* New features or bug fixes should come with appropriate tests.
|
||||
* Ensure all tests work before submitting a PR; see [Development Setup](https://lean-lang.org/lean4/doc/make/index.html#development-setup) and [Fixing Tests](https://lean-lang.org/lean4/doc/dev/fixing_tests.html).
|
||||
**Performance**: Make sure that your changes do not introduce performance regressions. If possible, optimize the solution for speed and resource usage.
|
||||
|
||||
PR Submission:
|
||||
---
|
||||
|
||||
**Descriptive Title and Summary**: The PR title should briefly explain the purpose of the PR. The summary should give more detailed information on what changes are made and why. Links to Zulip threads are not acceptable as a summary. You are responsible for summarizing the discussion, and getting support for it.
|
||||
|
||||
**Follow the commit convention**: Pull requests are squash merged, and the
|
||||
commit message is taken from the pull request title and body, so make sure they adhere to the [commit convention](https://github.com/leanprover/lean4/blob/master/doc/dev/commit_convention.md). Put questions and extra information, which should not be part of the final commit message, into a first comment rather than the Pull Request description.
|
||||
Because the change will be squashed, there is no need to polish the commit messages and history on the branch.
|
||||
|
||||
**Link to Relevant Issues**: Reference any issues that your PR addresses to provide context.
|
||||
|
||||
**Stay Responsive**: Once the PR is submitted, stay responsive to feedback and be prepared to make necessary revisions. We will close any PR that has been inactive (no response or updates from the submitter) for more than a month.
|
||||
|
||||
Reviews and Feedback:
|
||||
----
|
||||
|
||||
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
|
||||
|
||||
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.
|
||||
|
||||
**Continuous Integration**: Ensure that all CI checks pass on your PR. Failed checks will delay the review process. The maintainers will not check PRs containing failures.
|
||||
|
||||
What to Expect:
|
||||
----
|
||||
|
||||
**Not All PRs Get Merged**: While we appreciate every contribution, not all PRs will be merged. Ensure your changes align with the project's goals and quality standards.
|
||||
|
||||
**Feedback is a Gift**: It helps improve the project and can also help you grow as a developer or contributor.
|
||||
|
||||
**Community Involvement**: Engage with the Lean community on our communication channels. This can lead to better collaboration and understanding of the project's direction.
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
This is the repository for **Lean 4**.
|
||||
|
||||
We provide [nightly releases](https://github.com/leanprover/lean4-nightly/releases)
|
||||
and have just begun regular [stable point releases](https://github.com/leanprover/lean4/releases).
|
||||
|
||||
# About
|
||||
|
||||
- [Quickstart](https://github.com/leanprover/lean4/blob/master/doc/quickstart.md)
|
||||
- [Walkthrough installation video](https://www.youtube.com/watch?v=yZo6k48L0VY)
|
||||
- [Quick tour video](https://youtu.be/zyXtbb_eYbY)
|
||||
- [Quickstart](https://lean-lang.org/lean4/doc/quickstart.html)
|
||||
- [Homepage](https://lean-lang.org)
|
||||
- [Theorem Proving Tutorial](https://lean-lang.org/theorem_proving_in_lean4/)
|
||||
- [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/)
|
||||
- [Manual](https://lean-lang.org/lean4/doc/)
|
||||
- [Release notes](RELEASES.md) starting at v4.0.0-m3
|
||||
- [Examples](https://lean-lang.org/lean4/doc/examples.html)
|
||||
- [External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/doc/contributions.md)
|
||||
- [External Contribution Guidelines](CONTRIBUTING.md)
|
||||
- [FAQ](https://lean-lang.org/lean4/doc/faq.html)
|
||||
|
||||
# Installation
|
||||
|
||||
413
RELEASES.md
413
RELEASES.md
@@ -5,13 +5,416 @@ There is not yet a strong guarantee of backwards compatibility between versions,
|
||||
only an expectation that breaking changes will be documented in this file.
|
||||
|
||||
This file contains work-in-progress notes for the upcoming release, as well as previous stable releases.
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status of each version.
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
|
||||
of each version.
|
||||
|
||||
v4.3.0 (development in progress)
|
||||
v4.7.0 (development in progress)
|
||||
---------
|
||||
|
||||
* 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.
|
||||
|
||||
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
|
||||
|
||||
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 `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
|
||||
/-
|
||||
`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 _) := ...
|
||||
```
|
||||
|
||||
* The syntax of the `termination_by` and `decreasing_by` termination hints is overhauled:
|
||||
|
||||
* They are now placed directly after the function they apply to, instead of
|
||||
after the whole `mutual` block.
|
||||
* Therefore, the function name no longer has to be mentioned in the hint.
|
||||
* If the function has a `where` clause, the `termination_by` and
|
||||
`decreasing_by` for that function come before the `where`. The
|
||||
functions in the `where` clause can have their own termination hints, each
|
||||
following the corresponding definition.
|
||||
* The `termination_by` clause can only bind “extra parameters”, that are not
|
||||
already bound by the function header, but are bound in a lambda (`:= fun x
|
||||
y z =>`) or in patterns (`| x, n + 1 => …`). These extra parameters used to
|
||||
be understood as a suffix of the function parameters; now it is a prefix.
|
||||
|
||||
Migration guide: In simple cases just remove the function name, and any
|
||||
variables already bound at the header.
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat := …
|
||||
-termination_by foo a b => a - b
|
||||
+termination_by a b => a - b
|
||||
```
|
||||
or
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat := …
|
||||
-termination_by _ a b => a - b
|
||||
+termination_by a b => a - b
|
||||
```
|
||||
|
||||
If the parameters are bound in the function header (before the `:`), remove them as well:
|
||||
```diff
|
||||
def foo (a b : Nat) : Nat := …
|
||||
-termination_by foo a b => a - b
|
||||
+termination_by a - b
|
||||
```
|
||||
|
||||
Else, if there are multiple extra parameters, make sure to refer to the right
|
||||
ones; the bound variables are interpreted from left to right, no longer from
|
||||
right to left:
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat → Nat
|
||||
| a, b, c => …
|
||||
-termination_by foo b c => b
|
||||
+termination_by a b => b
|
||||
```
|
||||
|
||||
In the case of a `mutual` block, place the termination arguments (without the
|
||||
function name) next to the function definition:
|
||||
```diff
|
||||
-mutual
|
||||
-def foo : Nat → Nat → Nat := …
|
||||
-def bar : Nat → Nat := …
|
||||
-end
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar a => a
|
||||
+mutual
|
||||
+def foo : Nat → Nat → Nat := …
|
||||
+termination_by a b => a - b
|
||||
+def bar : Nat → Nat := …
|
||||
+termination_by a => a
|
||||
+end
|
||||
```
|
||||
|
||||
Similarly, if you have (mutual) recursion through `where` or `let rec`, the
|
||||
termination hints are now placed directly after the function they apply to:
|
||||
```diff
|
||||
-def foo (a b : Nat) : Nat := …
|
||||
- where bar (x : Nat) : Nat := …
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar x => x
|
||||
+def foo (a b : Nat) : Nat := …
|
||||
+termination_by a - b
|
||||
+ where
|
||||
+ bar (x : Nat) : Nat := …
|
||||
+ termination_by x
|
||||
|
||||
-def foo (a b : Nat) : Nat :=
|
||||
- let rec bar (x : Nat) : Nat := …
|
||||
- …
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar x => x
|
||||
+def foo (a b : Nat) : Nat :=
|
||||
+ let rec bar (x : Nat) : Nat := …
|
||||
+ termination_by x
|
||||
+ …
|
||||
+termination_by a - b
|
||||
```
|
||||
|
||||
In cases where a single `decreasing_by` clause applied to multiple mutually
|
||||
recursive functions before, the tactic now has to be duplicated.
|
||||
|
||||
* The semantics of `decreasing_by` changed; the tactic is applied to all
|
||||
termination proof goals together, not individually.
|
||||
|
||||
This helps when writing termination proofs interactively, as one can focus
|
||||
each subgoal individually, for example using `·`. Previously, the given
|
||||
tactic script had to work for _all_ goals, and one had to resort to tactic
|
||||
combinators like `first`:
|
||||
|
||||
```diff
|
||||
def foo (n : Nat) := … foo e1 … foo e2 …
|
||||
-decreasing_by
|
||||
-simp_wf
|
||||
-first | apply something_about_e1; …
|
||||
- | apply something_about_e2; …
|
||||
+decreasing_by
|
||||
+all_goals simp_wf
|
||||
+· apply something_about_e1; …
|
||||
+· apply something_about_e2; …
|
||||
```
|
||||
|
||||
To obtain the old behaviour of applying a tactic to each goal individually,
|
||||
use `all_goals`:
|
||||
```diff
|
||||
def foo (n : Nat) := …
|
||||
-decreasing_by some_tactic
|
||||
+decreasing_by all_goals some_tactic
|
||||
```
|
||||
|
||||
In the case of mutual recursion each `decreasing_by` now applies to just its
|
||||
function. If some functions in a recursive group do not have their own
|
||||
`decreasing_by`, the default `decreasing_tactic` is used. If the same tactic
|
||||
ought to be applied to multiple functions, the `decreasing_by` clause has to
|
||||
be repeated at each of these functions.
|
||||
|
||||
* Modify `InfoTree.context` to facilitate augmenting it with partial contexts while elaborating a command. This breaks backwards compatibility with all downstream projects that traverse the `InfoTree` manually instead of going through the functions in `InfoUtils.lean`, as well as those manually creating and saving `InfoTree`s. See [PR #3159](https://github.com/leanprover/lean4/pull/3159) for how to migrate your code.
|
||||
|
||||
* Add language server support for [call hierarchy requests](https://www.youtube.com/watch?v=r5LA7ivUb2c) ([PR #3082](https://github.com/leanprover/lean4/pull/3082)). The change to the .ilean format in this PR means that projects must be fully rebuilt once in order to generate .ilean files with the new format before features like "find references" work correctly again.
|
||||
|
||||
* Structure instances with multiple sources (for example `{a, b, c with x := 0}`) now have their fields filled from these sources
|
||||
in strict left-to-right order. Furthermore, the structure instance elaborator now aggressively use sources to fill in subobject
|
||||
fields, which prevents unnecessary eta expansion of the sources,
|
||||
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).
|
||||
|
||||
* 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))
|
||||
|
||||
* Add pretty printer options `pp.numeralTypes` and `pp.natLit`.
|
||||
When `pp.numeralTypes` is true, then natural number literals, integer literals, and rational number literals
|
||||
are pretty printed with type ascriptions, such as `(2 : Rat)`, `(-2 : Rat)`, and `(-2 / 3 : Rat)`.
|
||||
When `pp.natLit` is true, then raw natural number literals are pretty printed as `nat_lit 2`.
|
||||
[PR #2933](https://github.com/leanprover/lean4/pull/2933) and [RFC #3021](https://github.com/leanprover/lean4/issues/3021).
|
||||
|
||||
Lake updates:
|
||||
* improved platform information & control [#3226](https://github.com/leanprover/lean4/pull/3226)
|
||||
* `lake update` from unsupported manifest versions [#3149](https://github.com/leanprover/lean4/pull/3149)
|
||||
|
||||
Other improvements:
|
||||
* make `intro` be aware of `let_fun` [#3115](https://github.com/leanprover/lean4/pull/3115)
|
||||
* 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)
|
||||
* 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)
|
||||
* do not strip dotted components from lean module names [#2994](https://github.com/leanprover/lean4/pull/2994), fixing [#2999](https://github.com/leanprover/lean4/issues/2999)
|
||||
* fix `deriving` only deriving the first declaration for some handlers [#3058](https://github.com/leanprover/lean4/pull/3058), fixing [#3057](https://github.com/leanprover/lean4/issues/3057)
|
||||
* do not instantiate metavariables in kabstract/rw for disallowed occurrences [#2539](https://github.com/leanprover/lean4/pull/2539), fixing [#2538](https://github.com/leanprover/lean4/issues/2538)
|
||||
* hover info for `cases h : ...` [#3084](https://github.com/leanprover/lean4/pull/3084)
|
||||
|
||||
v4.5.0
|
||||
---------
|
||||
|
||||
* Modify the lexical syntax of string literals to have string gaps, which are escape sequences of the form `"\" newline whitespace*`.
|
||||
These have the interpetation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
|
||||
The following is equivalent to `"this is a string"`.
|
||||
```lean
|
||||
"this is \
|
||||
a string"
|
||||
```
|
||||
[PR #2821](https://github.com/leanprover/lean4/pull/2821) and [RFC #2838](https://github.com/leanprover/lean4/issues/2838).
|
||||
|
||||
* Add raw string literal syntax. For example, `r"\n"` is equivalent to `"\\n"`, with no escape processing.
|
||||
To include double quote characters in a raw string one can add sufficiently many `#` characters before and after
|
||||
the bounding `"`s, as in `r#"the "the" is in quotes"#` for `"the \"the\" is in quotes"`.
|
||||
[PR #2929](https://github.com/leanprover/lean4/pull/2929) and [issue #1422](https://github.com/leanprover/lean4/issues/1422).
|
||||
|
||||
* The low-level `termination_by'` clause is no longer supported.
|
||||
|
||||
Migration guide: Use `termination_by` instead, e.g.:
|
||||
```diff
|
||||
-termination_by' measure (fun ⟨i, _⟩ => as.size - i)
|
||||
+termination_by i _ => as.size - i
|
||||
```
|
||||
|
||||
If the well-founded relation you want to use is not the one that the
|
||||
`WellFoundedRelation` type class would infer for your termination argument,
|
||||
you can use `WellFounded.wrap` from the std libarary to explicitly give one:
|
||||
```diff
|
||||
-termination_by' ⟨r, hwf⟩
|
||||
+termination_by x => hwf.wrap x
|
||||
```
|
||||
|
||||
* Support snippet edits in LSP `TextEdit`s. See `Lean.Lsp.SnippetString` for more details.
|
||||
|
||||
* Deprecations and changes in the widget API.
|
||||
- `Widget.UserWidgetDefinition` is deprecated in favour of `Widget.Module`. The annotation `@[widget]` is deprecated in favour of `@[widget_module]`. To migrate a definition of type `UserWidgetDefinition`, remove the `name` field and replace the type with `Widget.Module`. Removing the `name` results in a title bar no longer being drawn above your panel widget. To add it back, draw it as part of the component using `<details open=true><summary class='mv2 pointer'>{name}</summary>{rest_of_widget}</details>`. See an example migration [here](https://github.com/leanprover/std4/pull/475/files#diff-857376079661a0c28a53b7ff84701afabbdf529836a6944d106c5294f0e68109R43-R83).
|
||||
- The new command `show_panel_widgets` allows displaying always-on and locally-on panel widgets.
|
||||
- `RpcEncodable` widget props can now be stored in the infotree.
|
||||
- See [RFC 2963](https://github.com/leanprover/lean4/issues/2963) for more details and motivation.
|
||||
|
||||
* If no usable lexicographic order can be found automatically for a termination proof, explain why.
|
||||
See [feat: GuessLex: if no measure is found, explain why](https://github.com/leanprover/lean4/pull/2960).
|
||||
|
||||
* Option to print [inferred termination argument](https://github.com/leanprover/lean4/pull/3012).
|
||||
With `set_option showInferredTerminationBy true` you will get messages like
|
||||
```
|
||||
Inferred termination argument:
|
||||
termination_by
|
||||
ackermann n m => (sizeOf n, sizeOf m)
|
||||
```
|
||||
for automatically generated `termination_by` clauses.
|
||||
|
||||
* More detailed error messages for [invalid mutual blocks](https://github.com/leanprover/lean4/pull/2949).
|
||||
|
||||
* [Multiple](https://github.com/leanprover/lean4/pull/2923) [improvements](https://github.com/leanprover/lean4/pull/2969) to the output of `simp?` and `simp_all?`.
|
||||
|
||||
* Tactics with `withLocation *` [no longer fail](https://github.com/leanprover/lean4/pull/2917) if they close the main goal.
|
||||
|
||||
* Implementation of a `test_extern` command for writing tests for `@[extern]` and `@[implemented_by]` functions.
|
||||
Usage is
|
||||
```
|
||||
import Lean.Util.TestExtern
|
||||
|
||||
test_extern Nat.add 17 37
|
||||
```
|
||||
The head symbol must be the constant with the `@[extern]` or `@[implemented_by]` attribute. The return type must have a `DecidableEq` instance.
|
||||
|
||||
Bug fixes for
|
||||
[#2853](https://github.com/leanprover/lean4/issues/2853), [#2953](https://github.com/leanprover/lean4/issues/2953), [#2966](https://github.com/leanprover/lean4/issues/2966),
|
||||
[#2971](https://github.com/leanprover/lean4/issues/2971), [#2990](https://github.com/leanprover/lean4/issues/2990), [#3094](https://github.com/leanprover/lean4/issues/3094).
|
||||
|
||||
Bug fix for [eager evaluation of default value](https://github.com/leanprover/lean4/pull/3043) in `Option.getD`.
|
||||
Avoid [panic in `leanPosToLspPos`](https://github.com/leanprover/lean4/pull/3071) when file source is unavailable.
|
||||
Improve [short-circuiting behavior](https://github.com/leanprover/lean4/pull/2972) for `List.all` and `List.any`.
|
||||
|
||||
Several Lake bug fixes: [#3036](https://github.com/leanprover/lean4/issues/3036), [#3064](https://github.com/leanprover/lean4/issues/3064), [#3069](https://github.com/leanprover/lean4/issues/3069).
|
||||
|
||||
v4.4.0
|
||||
---------
|
||||
|
||||
* Lake and the language server now support per-package server options using the `moreServerOptions` config field, as well as options that apply to both the language server and `lean` using the `leanOptions` config field. Setting either of these fields instead of `moreServerArgs` ensures that viewing files from a dependency uses the options for that dependency. Additionally, `moreServerArgs` is being deprecated in favor of the `moreGlobalServerArgs` field. See PR [#2858](https://github.com/leanprover/lean4/pull/2858).
|
||||
|
||||
A Lakefile with the following deprecated package declaration:
|
||||
```lean
|
||||
def moreServerArgs := #[
|
||||
"-Dpp.unicode.fun=true"
|
||||
]
|
||||
def moreLeanArgs := moreServerArgs
|
||||
|
||||
package SomePackage where
|
||||
moreServerArgs := moreServerArgs
|
||||
moreLeanArgs := moreLeanArgs
|
||||
```
|
||||
|
||||
... can be updated to the following package declaration to use per-package options:
|
||||
```lean
|
||||
package SomePackage where
|
||||
leanOptions := #[⟨`pp.unicode.fun, true⟩]
|
||||
```
|
||||
* [Rename request handler](https://github.com/leanprover/lean4/pull/2462).
|
||||
* [Import auto-completion](https://github.com/leanprover/lean4/pull/2904).
|
||||
* [`pp.beta`` to apply beta reduction when pretty printing](https://github.com/leanprover/lean4/pull/2864).
|
||||
* [Embed and check githash in .olean](https://github.com/leanprover/lean4/pull/2766).
|
||||
* [Guess lexicographic order for well-founded recursion](https://github.com/leanprover/lean4/pull/2874).
|
||||
* [Allow trailing comma in tuples, lists, and tactics](https://github.com/leanprover/lean4/pull/2643).
|
||||
|
||||
Bug fixes for [#2628](https://github.com/leanprover/lean4/issues/2628), [#2883](https://github.com/leanprover/lean4/issues/2883),
|
||||
[#2810](https://github.com/leanprover/lean4/issues/2810), [#2925](https://github.com/leanprover/lean4/issues/2925), and [#2914](https://github.com/leanprover/lean4/issues/2914).
|
||||
|
||||
**Lake:**
|
||||
|
||||
* `lake init .` and a bare `lake init` and will now use the current directory as the package name. [#2890](https://github.com/leanprover/lean4/pull/2890)
|
||||
* `lake new` and `lake init` will now produce errors on invalid package names such as `..`, `foo/bar`, `Init`, `Lean`, `Lake`, and `Main`. See issue [#2637](https://github.com/leanprover/lean4/issues/2637) and PR [#2890](https://github.com/leanprover/lean4/pull/2890).
|
||||
* `lean_lib` no longer converts its name to upper camel case (e.g., `lean_lib bar` will include modules named `bar.*` rather than `Bar.*`). See issue [#2567](https://github.com/leanprover/lean4/issues/2567) and PR [#2889](https://github.com/leanprover/lean4/pull/2889).
|
||||
* Lean and Lake now properly support non-identifier library names (e.g., `lake new 123-hello` and `import «123Hello»` now work correctly). See issue [#2865](https://github.com/leanprover/lean4/issues/2865) and PR [#2889](https://github.com/leanprover/lean4/pull/2888).
|
||||
* Lake now filters the environment extensions loaded from a compiled configuration (`lakefile.olean`) to include only those relevant to Lake's workspace loading process. This resolves segmentation faults caused by environment extension type mismatches (e.g., when defining custom elaborators via `elab` in configurations). See issue [#2632](https://github.com/leanprover/lean4/issues/2632) and PR [#2896](https://github.com/leanprover/lean4/pull/2896).
|
||||
* Cloud releases will now properly be re-unpacked if the build directory is removed. See PR [#2928](https://github.com/leanprover/lean4/pull/2928).
|
||||
* Lake's `math` template has been simplified. See PR [#2930](https://github.com/leanprover/lean4/pull/2930).
|
||||
* `lake exe <target>` now parses `target` like a build target (as the help text states it should) rather than as a basic name. For example, `lake exe @mathlib/runLinter` should now work. See PR [#2932](https://github.com/leanprover/lean4/pull/2932).
|
||||
* `lake new foo.bar [std]` now generates executables named `foo-bar` and `lake new foo.bar exe` properly creates `foo/bar.lean`. See PR [#2932](https://github.com/leanprover/lean4/pull/2932).
|
||||
* Later packages and libraries in the dependency tree are now preferred over earlier ones. That is, the later ones "shadow" the earlier ones. Such an ordering is more consistent with how declarations generally work in programming languages. This will break any package that relied on the previous ordering. See issue [#2548](https://github.com/leanprover/lean4/issues/2548) and PR [#2937](https://github.com/leanprover/lean4/pull/2937).
|
||||
* Executable roots are no longer mistakenly treated as importable. They will no longer be picked up by `findModule?`. See PR [#2937](https://github.com/leanprover/lean4/pull/2937).
|
||||
|
||||
v4.3.0
|
||||
---------
|
||||
|
||||
* `simp [f]` does not unfold partial applications of `f` anymore. See issue [#2042](https://github.com/leanprover/lean4/issues/2042).
|
||||
To fix proofs affected by this change, use `unfold f` or `simp (config := { unfoldPartialApp := true }) [f]`.
|
||||
* By default, `simp` will no longer try to use Decidable instances to rewrite terms. In particular, not all decidable goals will be closed by `simp`, and the `decide` tactic may be useful in such cases. The `decide` simp configuration option can be used to locally restore the old `simp` behavior, as in `simp (config := {decide := true})`; this includes using Decidable instances to verify side goals such as numeric inequalities.
|
||||
|
||||
* Many bug fixes:
|
||||
* [Add left/right actions to term tree coercion elaborator and make `^`` a right action](https://github.com/leanprover/lean4/pull/2778)
|
||||
* [Fix for #2775, don't catch max recursion depth errors](https://github.com/leanprover/lean4/pull/2790)
|
||||
* [Reduction of `Decidable` instances very slow when using `cases` tactic](https://github.com/leanprover/lean4/issues/2552)
|
||||
* [`simp` not rewriting in binder](https://github.com/leanprover/lean4/issues/1926)
|
||||
* [`simp` unfolding `let` even with `zeta := false` option](https://github.com/leanprover/lean4/issues/2669)
|
||||
* [`simp` (with beta/zeta disabled) and discrimination trees](https://github.com/leanprover/lean4/issues/2281)
|
||||
* [unknown free variable introduced by `rw ... at h`](https://github.com/leanprover/lean4/issues/2711)
|
||||
* [`dsimp` doesn't use `rfl` theorems which consist of an unapplied constant](https://github.com/leanprover/lean4/issues/2685)
|
||||
* [`dsimp` does not close reflexive equality goals if they are wrapped in metadata](https://github.com/leanprover/lean4/issues/2514)
|
||||
* [`rw [h]` uses `h` from the environment in preference to `h` from the local context](https://github.com/leanprover/lean4/issues/2729)
|
||||
* [missing `withAssignableSyntheticOpaque` for `assumption` tactic](https://github.com/leanprover/lean4/issues/2361)
|
||||
* [ignoring default value for field warning](https://github.com/leanprover/lean4/issues/2178)
|
||||
* [Cancel outstanding tasks on document edit in the language server](https://github.com/leanprover/lean4/pull/2648).
|
||||
* [Remove unnecessary `%` operations in `Fin.mod` and `Fin.div`](https://github.com/leanprover/lean4/pull/2688)
|
||||
* [Avoid `DecidableEq` in `Array.mem`](https://github.com/leanprover/lean4/pull/2774)
|
||||
* [Ensure `USize.size` unifies with `?m + 1`](https://github.com/leanprover/lean4/issues/1926)
|
||||
* [Improve compatibility with emacs eglot client](https://github.com/leanprover/lean4/pull/2721)
|
||||
|
||||
**Lake:**
|
||||
|
||||
* [Sensible defaults for `lake new MyProject math`](https://github.com/leanprover/lean4/pull/2770)
|
||||
* Changed `postUpdate?` configuration option to a `post_update` declaration. See the `post_update` syntax docstring for more information on the new syntax.
|
||||
* [A manifest is automatically created on workspace load if one does not exists.](https://github.com/leanprover/lean4/pull/2680).
|
||||
* The `:=` syntax for configuration declarations (i.e., `package`, `lean_lib`, and `lean_exe`) has been deprecated. For example, `package foo := {...}` is deprecated.
|
||||
* [support for overriding package URLs via `LAKE_PKG_URL_MAP`](https://github.com/leanprover/lean4/pull/2709)
|
||||
* Moved the default build directory (e.g., `build`), default packages directory (e.g., `lake-packages`), and the compiled configuration (e.g., `lakefile.olean`) into a new dedicated directory for Lake outputs, `.lake`. The cloud release build archives are also stored here, fixing [#2713](https://github.com/leanprover/lean4/issues/2713).
|
||||
* Update manifest format to version 7 (see [lean4#2801](https://github.com/leanprover/lean4/pull/2801) for details on the changes).
|
||||
* Deprecate the `manifestFile` field of a package configuration.
|
||||
* There is now a more rigorous check on `lakefile.olean` compatibility (see [#2842](https://github.com/leanprover/lean4/pull/2842) for more details).
|
||||
|
||||
v4.2.0
|
||||
---------
|
||||
|
||||
* [isDefEq cache for terms not containing metavariables.](https://github.com/leanprover/lean4/pull/2644).
|
||||
* [Cancel outstanding tasks on document edit in the language server](https://github.com/leanprover/lean4/pull/2648).
|
||||
* Make [`Environment.mk`](https://github.com/leanprover/lean4/pull/2604) and [`Environment.add`](https://github.com/leanprover/lean4/pull/2642) private, and add [`replay`](https://github.com/leanprover/lean4/pull/2617) as a safer alternative.
|
||||
* `IO.Process.output` no longer inherits the standard input of the caller.
|
||||
* [Do not inhibit caching](https://github.com/leanprover/lean4/pull/2612) of default-level `match` reduction.
|
||||
@@ -20,10 +423,6 @@ v4.3.0 (development in progress)
|
||||
* [Show path of failed import in Lake](https://github.com/leanprover/lean4/pull/2616).
|
||||
* [Fix linker warnings on macOS](https://github.com/leanprover/lean4/pull/2598).
|
||||
* **Lake:** Add `postUpdate?` package configuration option. Used by a package to specify some code which should be run after a successful `lake update` of the package or one of its downstream dependencies. ([lake#185](https://github.com/leanprover/lake/issues/185))
|
||||
|
||||
v4.2.0
|
||||
---------
|
||||
|
||||
* Improvements to Lake startup time ([#2572](https://github.com/leanprover/lean4/pull/2572), [#2573](https://github.com/leanprover/lean4/pull/2573))
|
||||
* `refine e` now replaces the main goal with metavariables which were created during elaboration of `e` and no longer captures pre-existing metavariables that occur in `e` ([#2502](https://github.com/leanprover/lean4/pull/2502)).
|
||||
* This is accomplished via changes to `withCollectingNewGoalsFrom`, which also affects `elabTermWithHoles`, `refine'`, `calc` (tactic), and `specialize`. Likewise, all of these now only include newly-created metavariables in their output.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
- [Tour of Lean](./tour.md)
|
||||
- [Setting Up Lean](./quickstart.md)
|
||||
- [Extended Setup Notes](./setup.md)
|
||||
- [Nix Setup](./setup/nix.md)
|
||||
- [Theorem Proving in Lean](./tpil.md)
|
||||
- [Functional Programming in Lean](fplean.md)
|
||||
- [Examples](./examples.md)
|
||||
@@ -86,7 +85,6 @@
|
||||
- [macOS Setup](./make/osx-10.9.md)
|
||||
- [Windows MSYS2 Setup](./make/msys2.md)
|
||||
- [Windows with WSL](./make/wsl.md)
|
||||
- [Nix Setup (*Experimental*)](./make/nix.md)
|
||||
- [Bootstrapping](./dev/bootstrap.md)
|
||||
- [Testing](./dev/testing.md)
|
||||
- [Debugging](./dev/debugging.md)
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
External Contribution Guidelines
|
||||
============
|
||||
|
||||
**In the past, we accepted most pull requests. This practice produced hard to maintain code, performance problems, and bugs.** In order to improve the quality and maintainability of our codebase, we've established the following guidelines for external contributions.
|
||||
|
||||
Before You Submit a Pull Request (PR):
|
||||
-------
|
||||
|
||||
**Start with an Issue**: Before submitting a PR, always open an issue discussing the problem you wish to solve or the feature you'd like to add. Use the prefix `RFC:` (request for comments) if you are proposing a new feature. Ask for feedback from other users. Take the time to summarize all the feedback. This allows the maintainers to evaluate your proposal more efficiently. When creating a RFC, consider the following questions:
|
||||
|
||||
- **User Experience**: How does this feature improve the user experience?
|
||||
|
||||
- **Beneficiaries**: Which Lean users and projects do benefit most from this feature/change?
|
||||
|
||||
- **Community Feedback**: Have you sought feedback or insights from other Lean users?
|
||||
|
||||
- **Maintainability**: Will this change streamline code maintenance or simplify its structure?
|
||||
|
||||
**Understand the Project**: Familiarize yourself with the project, existing issues, and latest commits. Ensure your contribution aligns with the project's direction and priorities.
|
||||
|
||||
**Stay Updated**: Regularly fetch and merge changes from the main branch to ensure your branch is up-to-date and can be smoothly integrated.
|
||||
|
||||
**Help wanted**: We have issues tagged with ["help wanted"](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), if you want to contribute to the project, please take a look at them. If you are interested in one of them, post comments, ask questions, and engage with the core developers there.
|
||||
|
||||
Quality Over Quantity:
|
||||
-----
|
||||
|
||||
**Focused Changes**: Each PR should address a single, clearly-defined issue or feature. Avoid making multiple unrelated changes in a single PR.
|
||||
|
||||
**Write Tests**: Every new feature or bug fix should come with relevant tests. This ensures the robustness and reliability of the contribution.
|
||||
|
||||
**Documentation**: Update relevant documentation, including comments in the code, to explain the logic and reasoning behind your changes.
|
||||
|
||||
Coding Standards:
|
||||
----
|
||||
|
||||
**Follow the Code Style**: Ensure that your code follows the established coding style of the project.
|
||||
|
||||
**Lean on Lean**: Use Lean's built-in features and libraries effectively, avoiding reinventions.
|
||||
|
||||
**Performance**: Make sure that your changes do not introduce performance regressions. If possible, optimize the solution for speed and resource usage.
|
||||
|
||||
PR Submission:
|
||||
---
|
||||
|
||||
**Descriptive Title and Summary**: The PR title should briefly explain the purpose of the PR. The summary should give more detailed information on what changes are made and why. Links to Zulip threads are not acceptable as a summary. You are responsible for summarizing the discussion, and getting support for it.
|
||||
|
||||
**Link to Relevant Issues**: Reference any issues that your PR addresses to provide context.
|
||||
|
||||
**Stay Responsive**: Once the PR is submitted, stay responsive to feedback and be prepared to make necessary revisions. We will close any PR that has been inactive (no response or updates from the submitter) for more than a month.
|
||||
|
||||
Reviews and Feedback:
|
||||
----
|
||||
|
||||
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
|
||||
|
||||
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.
|
||||
|
||||
**Continuous Integration**: Ensure that all CI checks pass on your PR. Failed checks will delay the review process. The maintainers will not check PRs containing failures.
|
||||
|
||||
What to Expect:
|
||||
----
|
||||
|
||||
**Not All PRs Get Merged**: While we appreciate every contribution, not all PRs will be merged. Ensure your changes align with the project's goals and quality standards.
|
||||
|
||||
**Feedback is a Gift**: It helps improve the project and can also help you grow as a developer or contributor.
|
||||
|
||||
**Community Involvement**: Engage with the Lean community on our communication channels. This can lead to better collaboration and understanding of the project's direction.
|
||||
@@ -483,7 +483,43 @@ def baz : Char → Nat
|
||||
| _ => 3
|
||||
```
|
||||
|
||||
If any of the terms ``tᵢ`` in the template above contain a recursive call to ``foo``, the equation compiler tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side. The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
|
||||
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
|
||||
namespace Vector
|
||||
|
||||
def head : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
|
||||
def tail : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
|
||||
def map (f : α → β → γ) : Vector α n → Vector β n → Vector γ n
|
||||
| nil, nil => nil
|
||||
| cons a va, cons b vb => cons (f a b) (map f va vb)
|
||||
|
||||
end Vector
|
||||
```
|
||||
|
||||
.. _recursive_functions:
|
||||
|
||||
Recursive functions
|
||||
===================
|
||||
|
||||
Lean must ensure that a recursive function terminates, for which there are two strategies: _structural recursion_, in which all recursive calls are made on smaller parts of the input data, and _well-founded recursion_, in which recursive calls are justified by showing that arguments to recursive calls are smaller according to some other measure.
|
||||
|
||||
Structural recursion
|
||||
--------------------
|
||||
|
||||
If the definition of a function contains recursive calls, Lean first tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side.
|
||||
|
||||
The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
@@ -504,7 +540,12 @@ example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
|
||||
end Hide
|
||||
```
|
||||
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then show that each recursive call is decreasing under the lexicographic order of the arguments with respect to ``sizeOf`` measure. If it fails, the error message provides information as to the goal that Lean tried to prove. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition. In this case of well-founded recursion, the defining equations hold only propositionally, and can be accessed using ``simp`` and ``rewrite`` with the name ``foo``.
|
||||
Well-founded recursion
|
||||
---------------------
|
||||
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then tries to find a permutation of the arguments such that each recursive call is decreasing under the lexicographic order with respect to ``sizeOf`` measures. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition.
|
||||
|
||||
In the case of well-founded recursion, the equation used to declare the function holds only propositionally, but not definitionally, and can be accessed using ``unfold``, ``simp`` and ``rewrite`` with the function name (for example ``unfold foo`` or ``simp [foo]``, where ``foo`` is the function defined with well-founded recursion).
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
@@ -528,9 +569,53 @@ by rw [div]; rfl
|
||||
end Hide
|
||||
```
|
||||
|
||||
If Lean cannot find a permutation of the arguments for which all recursive calls are decreasing, it will print a table that contains, for every recursive call, which arguments Lean could prove to be decreasing. For example, a function with three recursive calls and four parameters might cause the following message to be printed
|
||||
|
||||
```
|
||||
example.lean:37:0-43:31: error: Could not find a decreasing measure.
|
||||
The arguments relate at each recursive call as follows:
|
||||
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
|
||||
x1 x2 x3 x4
|
||||
1) 39:6-27 = = _ =
|
||||
2) 40:6-25 = ? _ <
|
||||
3) 41:6-25 < _ _ _
|
||||
Please use `termination_by` to specify a decreasing measure.
|
||||
```
|
||||
|
||||
This table should be read as follows:
|
||||
|
||||
* In the first recursive call, in line 39, arguments 1, 2 and 4 are equal to the function's parameters.
|
||||
* The second recursive call, in line 40, has an equal first argument, a smaller fourth argument, and nothing could be inferred for the second argument.
|
||||
* The third recursive call, in line 41, has a decreasing first argument.
|
||||
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
|
||||
|
||||
|
||||
Lean will print the termination argument it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
|
||||
If Lean does not find the termination argument, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
```
|
||||
termination_by e
|
||||
```
|
||||
where ``e`` is an expression that depends on the parameters of the function and should be decreasing at each recursive call. The type of `e` should be an instance of the class ``WellFoundedRelation``, which determines how to compare two values of that type.
|
||||
|
||||
If ``f`` has parameters “after the ``:``” (for example when defining functions via patterns using `|`), then these can be brought into scope using the syntax
|
||||
```
|
||||
termination_by a₁ … aₙ => e
|
||||
```
|
||||
|
||||
By default, Lean uses the tactic ``decreasing_tactic`` when proving that an argument is decreasing; see its documentation for how to globally extend it. You can also choose to use a different tactic for a given function definition with the clause
|
||||
```
|
||||
decreasing_by <tac>
|
||||
```
|
||||
which should come after ``termination_by`, if present.
|
||||
|
||||
|
||||
Note that recursive definitions can in general require nested recursions, that is, recursion on different arguments of ``foo`` in the template above. The equation compiler handles this by abstracting later arguments, and recursively defining higher-order functions to meet the specification.
|
||||
|
||||
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). They are compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
|
||||
Mutual recursion
|
||||
----------------
|
||||
|
||||
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). Mutual definitions are always compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
|
||||
|
||||
```lean
|
||||
mutual
|
||||
@@ -587,29 +672,31 @@ def num_consts_lst : List Term → Nat
|
||||
end
|
||||
```
|
||||
|
||||
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination argument (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
```
|
||||
mutual
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
decreasing_by decreasing_tactic
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
end
|
||||
```
|
||||
|
||||
namespace Vector
|
||||
Another way to express mutual recursion is using local function definitions in ``where`` or ``let rec`` clauses: these can be mutually recursive with each other and their containing function:
|
||||
|
||||
def head {α : Type} : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
|
||||
def tail {α : Type} : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
|
||||
def map {α β γ : Type} (f : α → β → γ) :
|
||||
∀ {n}, Vector α n → Vector β n → Vector γ n
|
||||
| 0, nil, nil => nil
|
||||
| n+1, cons a va, cons b vb => cons (f a b) (map f va vb)
|
||||
|
||||
end Vector
|
||||
```
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
where
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
```
|
||||
|
||||
.. _match_expressions:
|
||||
|
||||
@@ -65,16 +65,36 @@ You now have a Lean binary and library that include your changes, though their
|
||||
own compilation was not influenced by them, that you can use to test your
|
||||
changes on test programs whose compilation *will* be influenced by the changes.
|
||||
|
||||
Finally, when we want to use new language features in the library, we need to
|
||||
update the stage 0 compiler, which can be done via `make -C stageN update-stage0`.
|
||||
`make update-stage0` without `-C` defaults to stage1.
|
||||
## Updating stage0
|
||||
|
||||
Updates to `stage0` should be their own commits in the Git history. In
|
||||
other words, before running `make update-stage0`, please commit your
|
||||
work. Then, commit the updated `stage0` compiler code with the commit message:
|
||||
Finally, when we want to use new language features in the library, we need to
|
||||
update the archived C source code of the stage 0 compiler in `stage0/src`.
|
||||
|
||||
The github repository will automatically update stage0 on `master` once
|
||||
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
|
||||
|
||||
If you have write access to the lean4 repository, you can also also manually
|
||||
trigger that process, for example to be able to use new features in the compiler itself.
|
||||
You can do that on <https://github.com/nomeata/lean4/actions/workflows/update-stage0.yml>
|
||||
or using Github CLI with
|
||||
```
|
||||
gh workflow run update-stage0.yml
|
||||
```
|
||||
|
||||
Leaving stage0 updates to the CI automation is preferrable, but should you need
|
||||
to do it locally, you can use `make update-stage0` in `build/release`, to
|
||||
update `stage0` from `stage1`, `make -C stageN update-stage0` to update from
|
||||
another stage, or `nix run .#update-stage0-commit` to update using nix.
|
||||
|
||||
Updates to `stage0` should be their own commits in the Git history. So should
|
||||
you have to include the stage0 update in your PR (rather than using above
|
||||
automation after merging changes), commit your work before running `make
|
||||
update-stage0`, commit the updated `stage0` compiler code with the commit
|
||||
message:
|
||||
```
|
||||
chore: update stage0
|
||||
```
|
||||
and coordinate with the admins to not squash your PR.
|
||||
|
||||
## Further Bootstrapping Complications
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
Git Commit Convention
|
||||
=====================
|
||||
|
||||
We are using the following convention for writing git-commit messages.
|
||||
It is based on the one from AngularJS project([doc][angularjs-doc],
|
||||
We are using the following convention for writing git commit messages. For pull
|
||||
requests, make sure the pull request title and description follow this
|
||||
convention, as the squash-merge commit will inherit title and body from the
|
||||
pull request.
|
||||
|
||||
This convention is based on the one from the AngularJS project ([doc][angularjs-doc],
|
||||
[commits][angularjs-git]).
|
||||
|
||||
|
||||
[angularjs-git]: https://github.com/angular/angular.js/commits/master
|
||||
[angularjs-doc]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
|
||||
|
||||
@@ -121,4 +121,4 @@ Thus to e.g. run `#eval` on such a declaration, you need to
|
||||
Note that it is not sufficient to load the foreign library containing the external symbol because the interpreter depends on code that is emitted for each `@[extern]` declaration.
|
||||
Thus it is not possible to interpret an `@[extern]` declaration in the same file.
|
||||
|
||||
See `tests/compiler/foreign` for an example.
|
||||
See [`tests/compiler/foreign`](https://github.com/leanprover/lean4/tree/master/tests/compiler/foreign/) for an example.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Development Workflow
|
||||
|
||||
If you want to make changes to Lean itself, start by [building Lean](../make/index.html) from a clean checkout to make sure that everything is set up correctly.
|
||||
If you want to make changes to Lean itself, start by [building Lean](../make/index.md) from a clean checkout to make sure that everything is set up correctly.
|
||||
After that, read on below to find out how to set up your editor for changing the Lean source code, followed by further sections of the development manual where applicable such as on the [test suite](testing.md) and [commit convention](commit_convention.md).
|
||||
|
||||
If you are planning to make any changes that may affect the compilation of Lean itself, e.g. changes to the parser, elaborator, or compiler, you should first read about the [bootstrapping pipeline](bootstrap.md).
|
||||
@@ -30,20 +30,14 @@ powershell -f elan-init.ps1 --default-toolchain none
|
||||
del elan-init.ps1
|
||||
```
|
||||
|
||||
You can use `elan toolchain link` to give a specific stage build
|
||||
directory a reference name, then use `elan override set` to associate
|
||||
such a name to the current directory. We usually want to use `stage0`
|
||||
for editing files in `src` and `stage1` for everything else (e.g.
|
||||
tests).
|
||||
The `lean-toolchain` files in the Lean 4 repository are set up to use the `lean4-stage0`
|
||||
toolchain for editing files in `src` and the `lean4` toolchain for editing files in `tests`.
|
||||
|
||||
Run the following commands to make `lean4` point at `stage1` and `lean4-stage0` point at `stage0`:
|
||||
```bash
|
||||
# in the Lean rootdir
|
||||
elan toolchain link lean4 build/release/stage1
|
||||
elan toolchain link lean4-stage0 build/release/stage0
|
||||
# make `lean` etc. point to stage1 in the rootdir and subdirs
|
||||
elan override set lean4
|
||||
cd src
|
||||
# make `lean` etc. point to stage0 anywhere inside `src`
|
||||
elan override set lean4-stage0
|
||||
```
|
||||
|
||||
You can also use the `+toolchain` shorthand (e.g. `lean +lean4-debug`) to switch
|
||||
@@ -65,6 +59,15 @@ If you push `my-tag` to a fork in your github account `my_name`,
|
||||
you can then put `my_name/lean4:my-tag` in your `lean-toolchain` file in a project using `lake`.
|
||||
(You must use a tag name that does not start with a numeral, or contain `_`).
|
||||
|
||||
### VS Code
|
||||
|
||||
There is a `lean.code-workspace` file that correctly sets up VS Code with workspace roots for the stage0/stage1 setup described above as well as with other settings.
|
||||
You should always load it when working on Lean, such as by invoking
|
||||
```
|
||||
code lean.code-workspace
|
||||
```
|
||||
on the command line.
|
||||
|
||||
### `ccache`
|
||||
|
||||
Lean's build process uses [`ccache`](https://ccache.dev/) if it is
|
||||
|
||||
@@ -5,7 +5,6 @@ After [building Lean](../make/index.md) you can run all the tests using
|
||||
cd build/release
|
||||
make test ARGS=-j4
|
||||
```
|
||||
|
||||
Change the 4 to the maximum number of parallel tests you want to
|
||||
allow. The best choice is the number of CPU cores on your machine as
|
||||
the tests are mostly CPU bound. You can find the number of processors
|
||||
@@ -17,6 +16,12 @@ adding the `-C stageN` argument. The default when run as above is stage 1. The
|
||||
Lean tests will automatically use that stage's corresponding Lean
|
||||
executables
|
||||
|
||||
Running `make test` will not pick up new test files; run
|
||||
```bash
|
||||
cmake build/release/stage1
|
||||
```
|
||||
to update the list of tests.
|
||||
|
||||
You can also use `ctest` directly if you are in the right folder. So
|
||||
to run stage1 tests with a 300 second timeout run this:
|
||||
|
||||
@@ -24,6 +29,9 @@ to run stage1 tests with a 300 second timeout run this:
|
||||
cd build/release/stage1
|
||||
ctest -j 4 --output-on-failure --timeout 300
|
||||
```
|
||||
Useful `ctest` flags are `-R <name of test>` to run a single test, and
|
||||
`--rerun-failed` to run all tests that failed during the last run.
|
||||
You can also pass `ctest` flags via `make test ARGS="--rerun-failed"`.
|
||||
|
||||
To get verbose output from ctest pass the `--verbose` command line
|
||||
option. Test output is normally suppressed and only summary
|
||||
@@ -33,17 +41,17 @@ information is displayed. This option will show all test output.
|
||||
|
||||
All these tests are included by [src/shell/CMakeLists.txt](https://github.com/leanprover/lean4/blob/master/src/shell/CMakeLists.txt):
|
||||
|
||||
- `tests/lean`: contains tests that come equipped with a
|
||||
.lean.expected.out file. The driver script `test_single.sh` runs
|
||||
- [`tests/lean`](https://github.com/leanprover/lean4/tree/master/tests/lean/): contains tests that come equipped with a
|
||||
.lean.expected.out file. The driver script [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/test_single.sh) runs
|
||||
each test and checks the actual output (*.produced.out) with the
|
||||
checked in expected output.
|
||||
|
||||
- `tests/lean/run`: contains tests that are run through the lean
|
||||
- [`tests/lean/run`](https://github.com/leanprover/lean4/tree/master/tests/lean/run/): contains tests that are run through the lean
|
||||
command line one file at a time. These tests only look for error
|
||||
codes and do not check the expected output even though output is
|
||||
produced, it is ignored.
|
||||
|
||||
- `tests/lean/interactive`: are designed to test server requests at a
|
||||
- [`tests/lean/interactive`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/): are designed to test server requests at a
|
||||
given position in the input file. Each .lean file contains comments
|
||||
that indicate how to simulate a client request at that position.
|
||||
using a `--^` point to the line position. Example:
|
||||
@@ -53,7 +61,7 @@ All these tests are included by [src/shell/CMakeLists.txt](https://github.com/le
|
||||
Bla.
|
||||
--^ textDocument/completion
|
||||
```
|
||||
In this example, the test driver `test_single.sh` will simulate an
|
||||
In this example, the test driver [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/test_single.sh) will simulate an
|
||||
auto-completion request at `Bla.`. The expected output is stored in
|
||||
a .lean.expected.out in the json format that is part of the
|
||||
[Language Server
|
||||
@@ -70,21 +78,21 @@ All these tests are included by [src/shell/CMakeLists.txt](https://github.com/le
|
||||
--^ collectDiagnostics
|
||||
```
|
||||
|
||||
- `tests/lean/server`: Tests more of the Lean `--server` protocol.
|
||||
- [`tests/lean/server`](https://github.com/leanprover/lean4/tree/master/tests/lean/server/): Tests more of the Lean `--server` protocol.
|
||||
There are just a few of them, and it uses .log files containing
|
||||
JSON.
|
||||
|
||||
- `tests/compiler`: contains tests that will run the Lean compiler and
|
||||
- [`tests/compiler`](https://github.com/leanprover/lean4/tree/master/tests/compiler/): contains tests that will run the Lean compiler and
|
||||
build an executable that is executed and the output is compared to
|
||||
the .lean.expected.out file. This test also contains a subfolder
|
||||
`foreign` which shows how to extend Lean using C++.
|
||||
[`foreign`](https://github.com/leanprover/lean4/tree/master/tests/compiler/foreign/) which shows how to extend Lean using C++.
|
||||
|
||||
- `tests/lean/trust0`: tests that run Lean in a mode that Lean doesn't
|
||||
- [`tests/lean/trust0`](https://github.com/leanprover/lean4/tree/master/tests/lean/trust0): tests that run Lean in a mode that Lean doesn't
|
||||
even trust the .olean files (i.e., trust 0).
|
||||
|
||||
- `tests/bench`: contains performance tests.
|
||||
- [`tests/bench`](https://github.com/leanprover/lean4/tree/master/tests/bench/): contains performance tests.
|
||||
|
||||
- `tests/plugin`: tests that compiled Lean code can be loaded into
|
||||
- [`tests/plugin`](https://github.com/leanprover/lean4/tree/master/tests/plugin/): tests that compiled Lean code can be loaded into
|
||||
`lean` via the `--plugin` command line option.
|
||||
|
||||
## Writing Good Tests
|
||||
@@ -95,7 +103,7 @@ Every test file should contain:
|
||||
and, if not 100% clear, why that is the desirable behavior
|
||||
|
||||
At the time of writing, most tests do not follow these new guidelines yet.
|
||||
For an example of a conforming test, see `tests/lean/1971.lean`.
|
||||
For an example of a conforming test, see [`tests/lean/1971.lean`](https://github.com/leanprover/lean4/tree/master/tests/lean/1971.lean).
|
||||
|
||||
## Fixing Tests
|
||||
|
||||
@@ -111,7 +119,7 @@ First, we must install [meld](http://meldmerge.org/). On Ubuntu, we can do it by
|
||||
sudo apt-get install meld
|
||||
```
|
||||
|
||||
Now, suppose `bad_class.lean` test is broken. We can see the problem by going to `tests/lean` directory and
|
||||
Now, suppose `bad_class.lean` test is broken. We can see the problem by going to [`tests/lean`](https://github.com/leanprover/lean4/tree/master/tests/lean) directory and
|
||||
executing
|
||||
|
||||
```
|
||||
@@ -124,8 +132,3 @@ outputs. `meld` can also be used to repair the problems.
|
||||
|
||||
In Emacs, we can also execute `M-x lean4-diff-test-file` to check/diff the file of the current buffer.
|
||||
To mass-copy all `.produced.out` files to the respective `.expected.out` file, use `tests/lean/copy-produced`.
|
||||
When using the Nix setup, add `--keep-failed` to the `nix build` call and then call
|
||||
```sh
|
||||
tests/lean/copy-produced <build-dir>/source/tests/lean
|
||||
```
|
||||
instead where `<build-dir>` is the path printed out by `nix build`.
|
||||
|
||||
@@ -282,7 +282,7 @@ theorem BinTree.find_insert_of_ne (b : BinTree β) (h : k ≠ k') (v : β)
|
||||
let ⟨t, h⟩ := b; simp
|
||||
induction t with simp
|
||||
| leaf =>
|
||||
split <;> (try simp) <;> split <;> (try simp)
|
||||
intros
|
||||
have_eq k k'
|
||||
contradiction
|
||||
| node left key value right ihl ihr =>
|
||||
|
||||
@@ -82,7 +82,7 @@ theorem List.palindrome_ind (motive : List α → Prop)
|
||||
have ih := palindrome_ind motive h₁ h₂ h₃ (a₂::as').dropLast
|
||||
have : [a₁] ++ (a₂::as').dropLast ++ [(a₂::as').last (by simp)] = a₁::a₂::as' := by simp
|
||||
this ▸ h₃ _ _ _ ih
|
||||
termination_by _ as => as.length
|
||||
termination_by as.length
|
||||
|
||||
/-!
|
||||
We use our new induction principle to prove that if `as.reverse = as`, then `Palindrome as` holds.
|
||||
|
||||
@@ -15,9 +15,8 @@ sections of a Lean document. User widgets are rendered in the Lean infoview.
|
||||
To try it out, simply type in the following code and place your cursor over the `#widget` command.
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def helloWidget : UserWidgetDefinition where
|
||||
name := "Hello"
|
||||
@[widget_module]
|
||||
def helloWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
export default function(props) {
|
||||
@@ -25,7 +24,7 @@ def helloWidget : UserWidgetDefinition where
|
||||
return React.createElement('p', {}, name + '!')
|
||||
}"
|
||||
|
||||
#widget helloWidget .null
|
||||
#widget helloWidget
|
||||
|
||||
/-!
|
||||
If you want to dive into a full sample right away, check out
|
||||
@@ -56,7 +55,11 @@ to the React component. In our first invocation of `#widget`, we set it to `.nul
|
||||
happens when you type in:
|
||||
-/
|
||||
|
||||
#widget helloWidget (Json.mkObj [("name", "<your name here>")])
|
||||
structure HelloWidgetProps where
|
||||
name? : Option String := none
|
||||
deriving Server.RpcEncodable
|
||||
|
||||
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
|
||||
|
||||
/-!
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
|
||||
@@ -132,9 +135,8 @@ on this we either display an `InteractiveCode` with the type, `mapRpcError` the
|
||||
to turn it into a readable message, or show a `Loading..` message, respectively.
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def checkWidget : UserWidgetDefinition where
|
||||
name := "#check as a service"
|
||||
@[widget_module]
|
||||
def checkWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
@@ -160,7 +162,7 @@ export default function(props) {
|
||||
Finally we can try out the widget.
|
||||
-/
|
||||
|
||||
#widget checkWidget .null
|
||||
#widget checkWidget
|
||||
|
||||
/-!
|
||||

|
||||
@@ -193,9 +195,8 @@ interact with the text editor.
|
||||
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def insertTextWidget : UserWidgetDefinition where
|
||||
name := "textInserter"
|
||||
@[widget_module]
|
||||
def insertTextWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
@@ -213,4 +214,4 @@ export default function(props) {
|
||||
|
||||
/-! Finally, we can try this out: -/
|
||||
|
||||
#widget insertTextWidget .null
|
||||
#widget insertTextWidget
|
||||
|
||||
9
doc/flake.lock
generated
9
doc/flake.lock
generated
@@ -69,15 +69,16 @@
|
||||
"leanInk": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1666154782,
|
||||
"narHash": "sha256-0ELqEca6jZT4BW/mqkDD+uYuxW5QlZUFlNwZkvugsg8=",
|
||||
"owner": "digama0",
|
||||
"lastModified": 1704976501,
|
||||
"narHash": "sha256-FSBUsbX0HxakSnYRYzRBDN2YKmH9EkA0q9p7TSPEJTI=",
|
||||
"owner": "leanprover",
|
||||
"repo": "LeanInk",
|
||||
"rev": "12a2aec9b5f4aa84e84fb01a9af1da00d8aaff4e",
|
||||
"rev": "51821e3c2c032c88e4b2956483899d373ec090c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "leanprover",
|
||||
"ref": "refs/pull/57/merge",
|
||||
"repo": "LeanInk",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
flake = false;
|
||||
};
|
||||
inputs.leanInk = {
|
||||
url = "github:leanprover/LeanInk";
|
||||
url = "github:leanprover/LeanInk/refs/pull/57/merge";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ def fact x :=
|
||||
|
||||
#eval fact 100
|
||||
```
|
||||
By default, Lean only accepts total functions. The `partial` keyword should be used when Lean cannot
|
||||
establish that a function always terminates.
|
||||
By default, Lean only accepts total functions.
|
||||
The `partial` keyword may be used to define a recursive function without a termination proof; `partial` functions compute in compiled programs, but are opaque in proofs and during type checking.
|
||||
```lean
|
||||
partial def g (x : Nat) (p : Nat -> Bool) : Nat :=
|
||||
if p x then
|
||||
|
||||
BIN
doc/images/setup_guide.png
Normal file
BIN
doc/images/setup_guide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
doc/images/show-setup-guide.png
Normal file
BIN
doc/images/show-setup-guide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -8,7 +8,7 @@ A Lean program consists of a stream of UTF-8 tokens where each token
|
||||
is one of the following:
|
||||
|
||||
```
|
||||
token: symbol | command | ident | string | char | numeral |
|
||||
token: symbol | command | ident | string | raw_string | char | numeral |
|
||||
: decimal | doc_comment | mod_doc_comment | field_notation
|
||||
```
|
||||
|
||||
@@ -79,15 +79,35 @@ special characters:
|
||||
[Unicode table](https://unicode-table.com/en/) so "\xA9 Copyright 2021" is "© Copyright 2021".
|
||||
- `\uHHHH` puts the character represented by the 4 digit hexadecimal into the string, so the following
|
||||
string "\u65e5\u672c" will become "日本" which means "Japan".
|
||||
- `\` followed by a newline and then any amount of whitespace is a "gap" that is equivalent to the empty string,
|
||||
useful for letting a string literal span across multiple lines. Gaps spanning multiple lines can be confusing,
|
||||
so the parser raises an error if the trailing whitespace contains any newlines.
|
||||
|
||||
So the complete syntax is:
|
||||
|
||||
```
|
||||
string : '"' string_item '"'
|
||||
string_item : string_char | string_escape
|
||||
string_char : [^\\]
|
||||
string_escape: "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4} )
|
||||
string_item : string_char | char_escape | string_gap
|
||||
string_char : [^"\\]
|
||||
char_escape : "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4})
|
||||
hex_char : [0-9a-fA-F]
|
||||
string_gap : "\" newline whitespace*
|
||||
```
|
||||
|
||||
Raw String Literals
|
||||
===================
|
||||
|
||||
Raw string literals are string literals without any escape character processing.
|
||||
They begin with `r##...#"` (with zero or more `#` characters) and end with `"#...##` (with the same number of `#` characters).
|
||||
The contents of a raw string literal may contain `"##..#` so long as the number of `#` characters
|
||||
is less than the number of `#` characters used to begin the raw string literal.
|
||||
|
||||
```
|
||||
raw_string : raw_string_aux(0) | raw_string_aux(1) | raw_string_aux(2) | ...
|
||||
raw_string_aux(n) : 'r' '#'{n} '"' raw_string_item '"' '#'{n}
|
||||
raw_string_item(n) : raw_string_char | raw_string_quote(n)
|
||||
raw_string_char : [^"]
|
||||
raw_string_quote(n) : '"' '#'{0..n-1}
|
||||
```
|
||||
|
||||
Char Literals
|
||||
@@ -96,7 +116,9 @@ Char Literals
|
||||
Char literals are enclosed by single quotes (``'``).
|
||||
|
||||
```
|
||||
char: "'" string_item "'"
|
||||
char : "'" char_item "'"
|
||||
char_item : char_char | char_escape
|
||||
char_char : [^'\\]
|
||||
```
|
||||
|
||||
Numeric Literals
|
||||
|
||||
@@ -10,12 +10,9 @@ Platform-Specific Setup
|
||||
|
||||
- [Linux (Ubuntu)](ubuntu.md)
|
||||
- [Windows (msys2)](msys2.md)
|
||||
- [Windows (Visual Studio)](msvc.md)
|
||||
- [Windows (WSL)](wsl.md)
|
||||
- [macOS (homebrew)](osx-10.9.md)
|
||||
- Linux/macOS/WSL via [Nix](https://nixos.org/nix/): Call `nix-shell` in the project root. That's it.
|
||||
- There is also an [**experimental** setup based purely on Nix](nix.md) that works fundamentally differently from the
|
||||
make/CMake setup described on this page.
|
||||
|
||||
Generic Build Instructions
|
||||
--------------------------
|
||||
|
||||
110
doc/make/nix.md
110
doc/make/nix.md
@@ -1,110 +0,0 @@
|
||||
# Building with Nix
|
||||
|
||||
While [Nix](https://nixos.org/nix/) can be used to quickly open a shell with all dependencies for the [standard setup](index.md) installed, the user-facing [Nix Setup](../setup.md#nix-setup) can also be used to work *on* Lean.
|
||||
|
||||
## Setup
|
||||
|
||||
Follow the setup in the link above; to open the Lean shell inside a Lean checkout, you can also use
|
||||
```bash
|
||||
# in the Lean root directory
|
||||
$ nix-shell -A nix
|
||||
```
|
||||
|
||||
On top of the local and remote Nix cache, we do still rely on CCache as well to make C/C++ build steps incremental, which are atomic steps from Nix's point of view.
|
||||
To enable CCache, add the following line to the config file mentioned in the setup:
|
||||
```bash
|
||||
extra-sandbox-paths = /nix/var/cache/ccache
|
||||
```
|
||||
Then set up that directory as follows:
|
||||
```bash
|
||||
sudo mkdir -m0770 -p /nix/var/cache/ccache
|
||||
# macOS standard chown doesn't support --reference
|
||||
nix shell .#nixpkgs.coreutils -c sudo chown --reference=/nix/store /nix/var/cache/ccache
|
||||
```
|
||||
|
||||
## Basic Build Commands
|
||||
|
||||
From the Lean root directory inside the Lean shell:
|
||||
```bash
|
||||
nix build .#stage1 # build this stage's stdlib & executable
|
||||
nix build .#stage1.test # run all tests
|
||||
nix run .#stage1.update-stage0 # update ./stage0 from this stage
|
||||
nix run .#stage1.update-stage0-commit # ...and commit the results
|
||||
```
|
||||
The `stage1.` part in each command is optional:
|
||||
```bash
|
||||
nix build .#test # run tests for stage 1
|
||||
nix build . # build stage 1
|
||||
nix build # ditto
|
||||
```
|
||||
|
||||
## Build Process Description
|
||||
|
||||
The Nix build process conceptually works the same as described in [Lean Build Pipeline](index.md#lean-build-pipeline).
|
||||
However, there are two important differences in practice apart from the standard Nix properties (hermeneutic, reproducible builds stored in a global hash-indexed store etc.):
|
||||
* Only files tracked by git (using `git add` or at least `git add --intent-to-add`) are compiled.
|
||||
This is actually a general property of Nix flakes, and has the benefit of making it basically impossible to forget to commit a file (at least in `src/`).
|
||||
* Only files reachable from `src/Lean.lean` are compiled.
|
||||
This is because modules are discovered not from a directory listing anymore but by recursively compiling all dependencies of that top module.
|
||||
|
||||
## Editor Integration
|
||||
|
||||
As in the standard Nix setup.
|
||||
After adding `src/` as an LSP workspace, it should automatically fall back to using stage 0 in there.
|
||||
|
||||
Note that the UX of `{emacs,vscode}-dev` is quite different from the Make-based setup regarding the compilation of dependencies:
|
||||
there is no mutable directory incrementally filled by the build that we could point the editor at for .olean files.
|
||||
Instead, `emacs-dev` will gather the individual dependency outputs from the Nix store when checking a file -- and build them on the fly when necessary.
|
||||
However, it will only ever load changes saved to disk, not ones opened in other buffers.
|
||||
|
||||
The absence of a mutable output directory also means that the Lean server will not automatically pick up `.ilean` metadata from newly compiled files.
|
||||
Instead, you can run `nix run .#link-ilean` to symlink the `.ilean` tree of the stdlib state at that point in time to `src/build/lib`, where the server should automatically find them.
|
||||
|
||||
## Other Fun Stuff to Do with Nix
|
||||
|
||||
Open Emacs with Lean set up from an arbitrary commit (without even cloning Lean beforehand... if your Nix is new enough):
|
||||
```bash
|
||||
nix run github:leanprover/lean4/7e4edeb#emacs-package
|
||||
```
|
||||
|
||||
Open a shell with `lean` and `LEAN_PATH` set up for compiling a specific module (this is exactly what `emacs-dev` is doing internally):
|
||||
```bash
|
||||
nix develop .#mods.\"Lean.Parser.Basic\"
|
||||
# alternatively, directly pass a command to execute:
|
||||
nix develop .#stage2.mods.\"Init.Control.Basic\" -c bash -c 'lean $src -Dtrace.Elab.command=true'
|
||||
```
|
||||
|
||||
Not sure what you just broke? Run Lean from (e.g.) the previous commit on a file:
|
||||
```bash
|
||||
nix run .\?rev=$(git rev-parse @^) scratch.lean
|
||||
```
|
||||
|
||||
Work on two adjacent stages at the same time without the need for repeatedly updating and reverting `stage0/`:
|
||||
```bash
|
||||
# open an editor that will use only committed changes (so first commit them when changing files)
|
||||
nix run .#HEAD-as-stage1.emacs-dev&
|
||||
# open a second editor that will use those committed changes as stage 0
|
||||
# (so don't commit changes done here until you are done and ran a final `update-stage0-commit`)
|
||||
nix run .#HEAD-as-stage0.emacs-dev&
|
||||
```
|
||||
To run `nix build` on the second stage outside of the second editor, use
|
||||
```bash
|
||||
nix build .#stage0-from-input --override-input lean-stage0 .\?rev=$(git rev-parse HEAD)
|
||||
```
|
||||
This setup will inadvertently change your `flake.lock` file, which you can revert when you are done.
|
||||
|
||||
...more surely to come...
|
||||
|
||||
## Debugging
|
||||
|
||||
Since Nix copies all source files before compilation, you will need to map debug symbols back to the original path using `set substitute-path` in GDB.
|
||||
For example, for a build on Linux with the Nix sandbox activated:
|
||||
```bash
|
||||
(gdb) f
|
||||
#1 0x0000000000d23a4f in lean_inc (o=0x1) at /build/source/build/include/lean/lean.h:562
|
||||
562 /build/source/build/include/lean/lean.h: No such file or directory.
|
||||
(gdb) set substitute-path /build/source/build src
|
||||
(gdb) f
|
||||
#1 0x0000000000d23a4f in lean_inc (o=0x1) at /build/source/build/include/lean/lean.h:562
|
||||
562 static inline void lean_inc(lean_object * o) { if (!lean_is_scalar(o)) lean_inc_ref(o); }
|
||||
```
|
||||
@@ -60,7 +60,7 @@ While parsing `a * (b + c)`, `(b + c)` is assigned a precedence `60` by the addi
|
||||
the right argument to have precedence **at least** 71. Thus, this parse is invalid. In contrast, `(a * b) + c` assigns
|
||||
a precedence of `70` to `(a * b)`. This is compatible with addition which expects the left argument to have precedence
|
||||
**at least `60` ** (`70` is greater than `60`). Thus, the string `a * b + c` is parsed as `(a * b) + c`.
|
||||
For more details, please look at the [Lean manual on syntax extensions](../syntax.md#notations-and-precedence).
|
||||
For more details, please look at the [Lean manual on syntax extensions](./notation.md#notations-and-precedence).
|
||||
|
||||
To go from strings into `Arith`, we define a macro to
|
||||
translate the syntax category `arith` into an `Arith` inductive value that
|
||||
|
||||
@@ -1,55 +1,18 @@
|
||||
# Quickstart
|
||||
|
||||
These instructions will walk you through setting up Lean using the "basic" setup and VS Code as the editor.
|
||||
See [Setup](./setup.md) for other ways, supported platforms, and more details on setting up Lean.
|
||||
|
||||
See quick [walkthrough demo video](https://www.youtube.com/watch?v=yZo6k48L0VY).
|
||||
These instructions will walk you through setting up Lean 4 together with VS Code as an editor for Lean 4.
|
||||
See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Install [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
1. Launch VS Code and install the `lean4` extension.
|
||||
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
|
||||
|
||||

|
||||
|
||||
1. Create a new file using "File > New Text File" (`Ctrl+N`). Click the `Select a language` prompt, type in `lean4`, and hit ENTER. You should see the following popup:
|
||||

|
||||
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: Show Setup Guide".
|
||||
|
||||
Click the "Install Lean using Elan" button. You should see some progress output like this:
|
||||

|
||||
|
||||
```
|
||||
info: syncing channel updates for 'stable'
|
||||
info: latest update on stable, lean version v4.0.0
|
||||
info: downloading component 'lean'
|
||||
```
|
||||
If there is no popup, you probably have Elan installed already.
|
||||
You may want to make sure that your default toolchain is Lean 4 in this case by running `elan default leanprover/lean4:stable` and reopen the file, as the next step will fail otherwise.
|
||||
1. Follow the Lean 4 setup guide. It will walk you through learning resources for Lean 4, teach you how to set up Lean's dependencies on your platform, install Lean 4 for you at the click of a button and help you set up your first project.
|
||||
|
||||
1. While it is installing, you can paste the following Lean program into the new file:
|
||||
|
||||
```lean
|
||||
#eval Lean.versionString
|
||||
```
|
||||
|
||||
When the installation has finished, the Lean Language Server should start automatically and you should get syntax-highlighting and a "Lean Infoview" popping up on the right. You will see the output of the `#eval` statement when
|
||||
you place your cursor at the end of the statement.
|
||||
|
||||

|
||||
|
||||
You are set up!
|
||||
|
||||
## Create a Lean Project
|
||||
|
||||
*If your goal is to contribute to [mathlib4](https://github.com/leanprover-community/mathlib4) or use it as a dependency, please see its readme for specific instructions on how to do that.*
|
||||
|
||||
You can now create a Lean project in a new folder. Run `lake init foo` from "View > Terminal" to create a package, followed by `lake build` to get an executable version of your Lean program.
|
||||
On Linux/macOS, you first have to follow the instructions printed by the Lean installation or log out and in again for the Lean executables to be available in you terminal.
|
||||
|
||||
Note: Packages **have** to be opened using "File > Open Folder..." for imports to work.
|
||||
Saved changes are visible in other files after running "Lean 4: Refresh File Dependencies" (`Ctrl+Shift+X`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**The InfoView says "Waiting for Lean server to start..." forever.**
|
||||
|
||||
Check that the VS Code Terminal is not showing some installation errors from `elan`.
|
||||
If that doesn't work, try also running the VS Code command `Developer: Reload Window`.
|
||||

|
||||
|
||||
10
doc/setup.md
10
doc/setup.md
@@ -2,7 +2,7 @@
|
||||
|
||||
### Tier 1
|
||||
|
||||
Platforms built & tested by our CI, available as nightly releases via elan (see below)
|
||||
Platforms built & tested by our CI, available as binary releases via elan (see below)
|
||||
|
||||
* x86-64 Linux with glibc 2.27+
|
||||
* x86-64 macOS 10.15+
|
||||
@@ -10,13 +10,15 @@ Platforms built & tested by our CI, available as nightly releases via elan (see
|
||||
|
||||
### Tier 2
|
||||
|
||||
Platforms cross-compiled but not tested by our CI, available as nightly releases
|
||||
Platforms cross-compiled but not tested by our CI, available as binary releases
|
||||
|
||||
Releases may be silently broken due to the lack of automated testing.
|
||||
Issue reports and fixes are welcome.
|
||||
|
||||
* aarch64 Linux with glibc 2.27+
|
||||
* aarch64 (Apple Silicon) macOS
|
||||
* x86 (32-bit) Linux
|
||||
* Emscripten Web Assembly
|
||||
|
||||
<!--
|
||||
### Tier 3
|
||||
@@ -48,10 +50,10 @@ Foo.lean # main file, import via `import Foo`
|
||||
Foo/
|
||||
A.lean # further files, import via e.g. `import Foo.A`
|
||||
A/... # further nesting
|
||||
build/ # `lake` build output directory
|
||||
.lake/ # `lake` build output directory
|
||||
```
|
||||
|
||||
After running `lake build` you will see a binary named `./build/bin/foo` and when you run it you should see the output:
|
||||
After running `lake build` you will see a binary named `./.lake/build/bin/foo` and when you run it you should see the output:
|
||||
```
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
# Nix Setup
|
||||
|
||||
An alternative setup based on Nix provides a perfectly reproducible development environment for your project from the Lean version down to the editor and Lean extension.
|
||||
However, it is still experimental and subject to change; in particular, it is heavily based on an unreleased version of Nix enabling [Nix Flakes](https://www.tweag.io/blog/2020-05-25-flakes/). The setup has been tested on NixOS, other Linux distributions, and macOS.
|
||||
|
||||
After installing (any version of) Nix (<https://nixos.org/download.html>), you can easily open a shell with the particular pre-release version of Nix needed by and tested with our setup (called the "Lean shell" from here on):
|
||||
```bash
|
||||
$ nix-shell https://github.com/leanprover/lean4/archive/master.tar.gz -A nix
|
||||
```
|
||||
While this shell is sufficient for executing the steps below, it is recommended to also set the following options in `/etc/nix/nix.conf` (`nix.extraOptions` in NixOS):
|
||||
```
|
||||
max-jobs = auto # Allow building multiple derivations in parallel
|
||||
keep-outputs = true # Do not garbage-collect build time-only dependencies (e.g. clang)
|
||||
# Allow fetching build results from the Lean Cachix cache
|
||||
trusted-substituters = https://lean4.cachix.org/
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= lean4.cachix.org-1:mawtxSxcaiWE24xCXXgh3qnvlTkyU7evRRnGeAhD4Wk=
|
||||
```
|
||||
On a multi-user installation of Nix (the default), you need to restart the Nix daemon afterwards:
|
||||
```bash
|
||||
sudo pkill nix-daemon
|
||||
```
|
||||
|
||||
The [Cachix](https://cachix.org/) integration will magically beam any build steps already executed by the CI right onto your machine when calling Nix commands in the shell opened above.
|
||||
It can be set up analogously as a cache for your own project.
|
||||
|
||||
Note: Your system Nix might print warnings about not knowing some of the settings used by the Lean shell Nix, which can be ignored.
|
||||
|
||||
## Basic Commands
|
||||
|
||||
From a Lean shell, run
|
||||
```bash
|
||||
$ nix flake new mypkg -t github:leanprover/lean4
|
||||
```
|
||||
to create a new Lean package in directory `mypkg` using the latest commit of Lean 4.
|
||||
Such packages follow the same directory layout as described in the standard setup, except for a `lakefile.lean` replaced by a `flake.nix` file set up so you can run Nix commands on it, for example:
|
||||
```bash
|
||||
$ nix build # build package and all dependencies
|
||||
$ nix build .#executable # compile `main` definition into executable (after you've added one)
|
||||
$ nix run .#emacs-dev # open a pinned version of Emacs with lean4-mode fully set up
|
||||
$ nix run .#emacs-dev MyPackage.lean # arguments can be passed as well, e.g. the file to open
|
||||
$ nix run .#vscode-dev MyPackage.lean # ditto, using VS Code
|
||||
```
|
||||
Note that if you rename `MyPackage.lean`, you also have to adjust the `name` attribute in `flake.nix` accordingly.
|
||||
Also note that if you turn the package into a Git repository, only tracked files will be visible to Nix.
|
||||
|
||||
As in the standard setup, changes need to be saved to be visible in other files, which have then to be invalidated via an editor command.
|
||||
|
||||
If you don't want to or cannot start the pinned editor from Nix, e.g. because you're running Lean inside WSL/a container/on a different machine, you can manually point your editor at the `lean` wrapper script the commands above use internally:
|
||||
```bash
|
||||
$ nix build .#lean-dev -o result-lean-dev
|
||||
```
|
||||
The resulting `./result-lean-dev/bin/lean` script essentially runs `nix run .#lean` in the current project's root directory when you open a Lean file or use the "refresh dependencies" command such that the correct Lean version for that project is executed.
|
||||
This includes selecting the correct stage of Lean (which it will compile on the fly, though without progress output) if you are [working on Lean itself](./make/nix.md#editor-integration).
|
||||
|
||||
Package dependencies can be added as further input flakes and passed to the `deps` list of `buildLeanPackage`. Example: <https://github.com/Kha/testpkg2/blob/master/flake.nix#L5>
|
||||
|
||||
For hacking, it can be useful to temporarily override an input with a local checkout/different version of a dependency:
|
||||
```bash
|
||||
$ nix build --override-input somedep path/to/somedep
|
||||
```
|
||||
|
||||
On a build error, Nix will show the last 10 lines of the output by default. You can pass `-L` to `nix build` to show all lines, or pass the shown `*.drv` path to `nix log` to show the full log after the fact.
|
||||
|
||||
Keeping all outputs ever built on a machine alive can accumulate to quite impressive amounts of disk space, so you might want to trigger the Nix GC when `/nix/store/` has grown too large:
|
||||
```bash
|
||||
nix-collect-garbage
|
||||
```
|
||||
This will remove everything not reachable from "GC roots" such as the `./result` symlink created by `nix build`.
|
||||
|
||||
Note that the package information in `flake.nix` is currently completely independent from `lakefile.lean` used in the standard setup.
|
||||
Unifying the two formats is TBD.
|
||||
@@ -67,6 +67,9 @@ theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f
|
||||
\end{document}
|
||||
```
|
||||
|
||||
If your version of `minted` is v2.7 or newer, but before v3.0,
|
||||
you will additionally need to follow the workaround described in https://github.com/gpoore/minted/issues/360.
|
||||
|
||||
You can then compile `test.tex` by executing the following command:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -15,7 +15,7 @@ The most fundamental pieces of any Lean program are functions organized into nam
|
||||
[Functions](./functions.md) perform work on inputs to produce outputs,
|
||||
and they are organized under [namespaces](./namespaces.md),
|
||||
which are the primary way you group things in Lean.
|
||||
They are defined using the [`def`](./definitions.md) command,
|
||||
They are defined using the `def` command,
|
||||
which give the function a name and define its arguments.
|
||||
|
||||
```lean
|
||||
|
||||
@@ -99,11 +99,11 @@ Let us start with the first step of the program above, declaring an appropriate
|
||||
|
||||
```lean
|
||||
# namespace Ex
|
||||
class Inhabited (a : Type u) where
|
||||
class Inhabited (a : Sort u) where
|
||||
default : a
|
||||
|
||||
#check @Inhabited.default
|
||||
-- Inhabited.default : {a : Type u} → [self : Inhabited a] → a
|
||||
-- Inhabited.default : {a : Sort u} → [self : Inhabited a] → a
|
||||
# end Ex
|
||||
```
|
||||
Note `Inhabited.default` doesn't have any explicit argument.
|
||||
@@ -114,7 +114,7 @@ Now we populate the class with some instances:
|
||||
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type _) where
|
||||
# class Inhabited (a : Sort _) where
|
||||
# default : a
|
||||
instance : Inhabited Bool where
|
||||
default := true
|
||||
@@ -138,7 +138,7 @@ instance : Inhabited Prop where
|
||||
You can use the command `export` to create the alias `default` for `Inhabited.default`
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type _) where
|
||||
# class Inhabited (a : Sort _) where
|
||||
# default : a
|
||||
# instance : Inhabited Bool where
|
||||
# default := true
|
||||
@@ -174,7 +174,7 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
|
||||
With this added to the earlier instance declarations, type class instance can infer, for example, a default element of ``Nat × Bool``:
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type u) where
|
||||
# class Inhabited (a : Sort u) where
|
||||
# default : a
|
||||
# instance : Inhabited Bool where
|
||||
# default := true
|
||||
@@ -191,8 +191,14 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
|
||||
```
|
||||
Similarly, we can inhabit type function with suitable constant functions:
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Sort u) where
|
||||
# default : a
|
||||
# opaque default [Inhabited a] : a :=
|
||||
# Inhabited.default
|
||||
instance [Inhabited b] : Inhabited (a -> b) where
|
||||
default := fun _ => default
|
||||
# end Ex
|
||||
```
|
||||
As an exercise, try defining default instances for other types, such as `List` and `Sum` types.
|
||||
|
||||
|
||||
@@ -37,6 +37,6 @@ Lean has numerous features, including:
|
||||
- [Extensible syntax](./syntax.md)
|
||||
- Hygienic macros
|
||||
- [Dependent types](https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html)
|
||||
- [Metaprogramming](./metaprogramming.md)
|
||||
- [Metaprogramming](./macro_overview.md)
|
||||
- Multithreading
|
||||
- Verification: you can prove properties of your functions using Lean itself
|
||||
|
||||
1
lean-toolchain
Normal file
1
lean-toolchain
Normal file
@@ -0,0 +1 @@
|
||||
lean4
|
||||
57
lean.code-workspace
Normal file
57
lean.code-workspace
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "src"
|
||||
},
|
||||
{
|
||||
"path": "tests"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build/release",
|
||||
"cmake.generator": "Unix Makefiles",
|
||||
"[markdown]": {
|
||||
"rewrap.wrappingColumn": 70
|
||||
},
|
||||
"[lean4]": {
|
||||
"editor.rulers": [
|
||||
100
|
||||
]
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"type": "shell",
|
||||
"command": "make -C build/release -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "test",
|
||||
"type": "shell",
|
||||
"command": "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4); CTEST_OUTPUT_ON_FAILURE=1 make -C build/release test -j$NPROC ARGS=\"-j$NPROC\"",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
"leanprover.lean4"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -83,13 +83,13 @@ rec {
|
||||
# use same stage for retrieving dependencies
|
||||
lean-leanDeps = stage0;
|
||||
lean-final = self;
|
||||
leanFlags = [ "-DwarningAsError=true" ];
|
||||
} ({
|
||||
src = src + "/src";
|
||||
roots = [ { mod = args.name; glob = "andSubmodules"; } ];
|
||||
fullSrc = src;
|
||||
srcPath = "$PWD/src:$PWD/src/lake";
|
||||
inherit debug;
|
||||
leanFlags = [ "-DwarningAsError=true" ];
|
||||
} // args);
|
||||
Init' = build { name = "Init"; deps = []; };
|
||||
Lean' = build { name = "Lean"; deps = [ Init' ]; };
|
||||
|
||||
@@ -10,7 +10,7 @@ function pebkac() {
|
||||
[[ $# -gt 0 ]] || pebkac
|
||||
case $1 in
|
||||
--version)
|
||||
# minimum version for `lake server` with fallback
|
||||
# minimum version for `lake serve` with fallback
|
||||
echo 3.1.0
|
||||
;;
|
||||
print-paths)
|
||||
|
||||
@@ -9,7 +9,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 3)
|
||||
set(LEAN_VERSION_MINOR 7)
|
||||
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'")
|
||||
@@ -18,6 +18,14 @@ if (LEAN_SPECIAL_VERSION_DESC)
|
||||
string(APPEND LEAN_VERSION_STRING "-${LEAN_SPECIAL_VERSION_DESC}")
|
||||
endif()
|
||||
|
||||
set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform")
|
||||
if (NOT LEAN_PLATFORM_TARGET)
|
||||
# this may fail when the compiler is not clang, but this should only happen in local builds where
|
||||
# the value of the variable is not of immediate relevance
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-target-triple
|
||||
OUTPUT_VARIABLE LEAN_PLATFORM_TARGET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
set(LEAN_EXTRA_LINKER_FLAGS "" CACHE STRING "Additional flags used by the linker")
|
||||
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
|
||||
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
|
||||
@@ -64,10 +72,10 @@ option(BSYMBOLIC "Link with -Bsymbolic to reduce call overhead in shared librari
|
||||
option(USE_GMP "USE_GMP" ON)
|
||||
|
||||
# development-specific options
|
||||
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" ON)
|
||||
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
|
||||
|
||||
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
|
||||
set(LEANC_CC "cc" CACHE STRING "C compiler to use in `leanc`")
|
||||
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
|
||||
|
||||
if ("${LAZY_RC}" MATCHES "ON")
|
||||
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
|
||||
@@ -93,8 +101,9 @@ if ("${RUNTIME_STATS}" MATCHES "ON")
|
||||
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_RUNTIME_STATS")
|
||||
endif()
|
||||
|
||||
if (NOT("${CHECK_OLEAN_VERSION}" MATCHES "ON"))
|
||||
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_IGNORE_OLEAN_VERSION")
|
||||
if ("${CHECK_OLEAN_VERSION}" MATCHES "ON")
|
||||
set(USE_GITHASH ON)
|
||||
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_CHECK_OLEAN_VERSION")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
@@ -401,26 +410,17 @@ if(MULTI_THREAD AND NOT MSVC AND (NOT ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin"))
|
||||
endif()
|
||||
|
||||
# Git HASH
|
||||
set(LEAN_PACKAGE_VERSION "NOT-FOUND")
|
||||
if(USE_GITHASH)
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
if(${GIT_SHA1} MATCHES "GITDIR-NOTFOUND")
|
||||
message(STATUS "Failed to read git_sha1")
|
||||
set(GIT_SHA1 "")
|
||||
if(EXISTS "${LEAN_SOURCE_DIR}/bin/package_version")
|
||||
file(STRINGS "${LEAN_SOURCE_DIR}/bin/package_version" LEAN_PACKAGE_VERSION)
|
||||
message(STATUS "Package version detected: ${LEAN_PACKAGE_VERSION}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "git commit sha1: ${GIT_SHA1}")
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA1 "")
|
||||
if(EXISTS "${LEAN_SOURCE_DIR}/bin/package_version")
|
||||
file(STRINGS "${LEAN_SOURCE_DIR}/bin/package_version" LEAN_PACKAGE_VERSION)
|
||||
message(STATUS "Package version detected: ${LEAN_PACKAGE_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
|
||||
|
||||
@@ -447,12 +447,13 @@ include_directories(${LEAN_SOURCE_DIR})
|
||||
include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers
|
||||
include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" headers
|
||||
|
||||
# Use CMake profile C++ flags for building Lean libraries, but do not embed in `leanc`
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
|
||||
# These are used in lean.mk (and libleanrt) and passed through by stdlib.make
|
||||
# They are not embedded into `leanc` since they are build profile/machine specific
|
||||
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
|
||||
|
||||
# Do embed flag for finding system libraries in dev builds
|
||||
if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
|
||||
string(APPEND LEANC_OPTS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
|
||||
endif()
|
||||
|
||||
if(${STAGE} GREATER 1)
|
||||
|
||||
@@ -7,6 +7,9 @@ prelude
|
||||
import Init.Prelude
|
||||
import Init.Notation
|
||||
import Init.Tactics
|
||||
import Init.TacticsExtra
|
||||
import Init.ByCases
|
||||
import Init.RCases
|
||||
import Init.Core
|
||||
import Init.Control
|
||||
import Init.Data.Basic
|
||||
@@ -17,9 +20,15 @@ import Init.System
|
||||
import Init.Util
|
||||
import Init.Dynamic
|
||||
import Init.ShareCommon
|
||||
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
|
||||
|
||||
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
|
||||
74
src/Init/ByCases.lean
Normal file
74
src/Init/ByCases.lean
Normal file
@@ -0,0 +1,74 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. 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)
|
||||
|
||||
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
|
||||
@[simp] theorem dite_not (P : Prop) {_ : Decidable P} (x : ¬P → α) (y : ¬¬P → α) :
|
||||
dite (¬P) x y = dite P (fun h => y (not_not_intro h)) x := by
|
||||
by_cases h : P <;> simp [h]
|
||||
|
||||
/-- 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 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
|
||||
@@ -1,11 +1,10 @@
|
||||
/-
|
||||
Copyright (c) 2020 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
|
||||
import Init.NotationExtra
|
||||
import Init.PropLemmas
|
||||
|
||||
universe u v
|
||||
|
||||
@@ -22,7 +21,7 @@ noncomputable def choose {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : α :
|
||||
theorem choose_spec {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : p (choose h) :=
|
||||
(indefiniteDescription p h).property
|
||||
|
||||
/-- Diaconescu's theorem: excluded middle from choice, Function extensionality and propositional extensionality. -/
|
||||
/-- **Diaconescu's theorem**: excluded middle from choice, Function extensionality and propositional extensionality. -/
|
||||
theorem em (p : Prop) : p ∨ ¬p :=
|
||||
let U (x : Prop) : Prop := x = True ∨ p
|
||||
let V (x : Prop) : Prop := x = False ∨ p
|
||||
@@ -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,21 +122,36 @@ 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
|
||||
|
||||
/--
|
||||
`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
|
||||
/-- The Double Negation Theorem: `¬¬P` is equivalent to `P`.
|
||||
The left-to-right direction, double negation elimination (DNE),
|
||||
is classically true but not constructively. -/
|
||||
@[scoped simp] theorem not_not : ¬¬a ↔ a := Decidable.not_not
|
||||
|
||||
macro_rules
|
||||
| `(tactic| by_cases $h : $e) =>
|
||||
`(tactic|
|
||||
cases em $e with
|
||||
| inl $h => _
|
||||
| inr $h => _)
|
||||
| `(tactic| by_cases $e) =>
|
||||
`(tactic|
|
||||
cases em $e with
|
||||
| inl h => _
|
||||
| inr h => _)
|
||||
@[simp] 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
|
||||
|
||||
end Classical
|
||||
|
||||
/-- 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
|
||||
|
||||
@@ -290,6 +290,12 @@ between e.g. `↑x + ↑y` and `↑(x + y)`.
|
||||
-/
|
||||
syntax:1024 (name := coeNotation) "↑" term:1024 : term
|
||||
|
||||
/-- `⇑ t` coerces `t` to a function. -/
|
||||
syntax:1024 (name := coeFunNotation) "⇑" term:1024 : term
|
||||
|
||||
/-- `↥ t` coerces `t` to a type. -/
|
||||
syntax:1024 (name := coeSortNotation) "↥" term:1024 : term
|
||||
|
||||
/-! # Basic instances -/
|
||||
|
||||
instance boolToProp : Coe Bool Prop where
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 `α`,
|
||||
@@ -411,9 +537,10 @@ set_option linter.unusedVariables.funArgs false in
|
||||
be available and then calls `f` on the result.
|
||||
|
||||
`prio`, if provided, is the priority of the task.
|
||||
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
|
||||
-/
|
||||
@[noinline, extern "lean_task_map"]
|
||||
protected def map {α : Type u} {β : Type v} (f : α → β) (x : Task α) (prio := Priority.default) : Task β :=
|
||||
protected def map (f : α → β) (x : Task α) (prio := Priority.default) (sync := false) : Task β :=
|
||||
⟨f x.get⟩
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
@@ -424,9 +551,11 @@ for the value of `x` to be available and then calls `f` on the result,
|
||||
resulting in a new task which is then run for a result.
|
||||
|
||||
`prio`, if provided, is the priority of the task.
|
||||
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
|
||||
-/
|
||||
@[noinline, extern "lean_task_bind"]
|
||||
protected def bind {α : Type u} {β : Type v} (x : Task α) (f : α → Task β) (prio := Priority.default) : Task β :=
|
||||
protected def bind (x : Task α) (f : α → Task β) (prio := Priority.default) (sync := false) :
|
||||
Task β :=
|
||||
⟨(f x.get).get⟩
|
||||
|
||||
end Task
|
||||
@@ -522,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
|
||||
@@ -572,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
|
||||
|
||||
@@ -585,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
|
||||
|
||||
@@ -663,22 +791,31 @@ theorem Iff.refl (a : Prop) : a ↔ a :=
|
||||
protected theorem Iff.rfl {a : Prop} : a ↔ a :=
|
||||
Iff.refl 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 -/
|
||||
|
||||
@@ -878,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
|
||||
@@ -890,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}
|
||||
@@ -1169,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] theorem imp_self : (a → a) ↔ True := iff_true_intro id
|
||||
|
||||
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
|
||||
@@ -1680,40 +1930,104 @@ So, you are mainly losing the capability of type checking your development using
|
||||
-/
|
||||
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}
|
||||
|
||||
/--
|
||||
`IsAssociative op` says that `op` is an associative operation,
|
||||
i.e. `(a ∘ b) ∘ c = a ∘ (b ∘ c)`. It is used by the `ac_rfl` tactic.
|
||||
`Associative op` indicates `op` is an associative operation,
|
||||
i.e. `(a ∘ b) ∘ c = a ∘ (b ∘ c)`.
|
||||
-/
|
||||
class IsAssociative {α : Sort u} (op : α → α → α) where
|
||||
class Associative (op : α → α → α) : Prop where
|
||||
/-- An associative operation satisfies `(a ∘ b) ∘ c = a ∘ (b ∘ c)`. -/
|
||||
assoc : (a b c : α) → op (op a b) c = op a (op b c)
|
||||
|
||||
/--
|
||||
`IsCommutative op` says that `op` is a commutative operation,
|
||||
i.e. `a ∘ b = b ∘ a`. It is used by the `ac_rfl` tactic.
|
||||
`Commutative op` says that `op` is a commutative operation,
|
||||
i.e. `a ∘ b = b ∘ a`.
|
||||
-/
|
||||
class IsCommutative {α : Sort u} (op : α → α → α) where
|
||||
class Commutative (op : α → α → α) : Prop where
|
||||
/-- A commutative operation satisfies `a ∘ b = b ∘ a`. -/
|
||||
comm : (a b : α) → op a b = op b a
|
||||
|
||||
/--
|
||||
`IsIdempotent op` says that `op` is an idempotent operation,
|
||||
i.e. `a ∘ a = a`. It is used by the `ac_rfl` tactic
|
||||
(which also simplifies up to idempotence when available).
|
||||
`IdempotentOp op` indicates `op` is an idempotent binary operation.
|
||||
i.e. `a ∘ a = a`.
|
||||
-/
|
||||
class IsIdempotent {α : Sort u} (op : α → α → α) where
|
||||
class IdempotentOp (op : α → α → α) : Prop where
|
||||
/-- An idempotent operation satisfies `a ∘ a = a`. -/
|
||||
idempotent : (x : α) → op x x = x
|
||||
|
||||
/--
|
||||
`IsNeutral op e` says that `e` is a neutral operation for `op`,
|
||||
i.e. `a ∘ e = a = e ∘ a`. It is used by the `ac_rfl` tactic
|
||||
(which also simplifies neutral elements when available).
|
||||
-/
|
||||
class IsNeutral {α : Sort u} (op : α → α → α) (neutral : α) where
|
||||
/-- A neutral element can be cancelled on the left: `e ∘ a = a`. -/
|
||||
left_neutral : (a : α) → op neutral a = a
|
||||
/-- A neutral element can be cancelled on the right: `a ∘ e = a`. -/
|
||||
right_neutral : (a : α) → op a neutral = a
|
||||
`LeftIdentify op o` indicates `o` is a left identity of `op`.
|
||||
|
||||
end Lean
|
||||
This class does not require a proof that `o` is an identity, and
|
||||
is used primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class LeftIdentity (op : α → β → β) (o : outParam α) : Prop
|
||||
|
||||
/--
|
||||
`LawfulLeftIdentify op o` indicates `o` is a verified left identity of
|
||||
`op`.
|
||||
-/
|
||||
class LawfulLeftIdentity (op : α → β → β) (o : outParam α) extends LeftIdentity op o : Prop where
|
||||
/-- Left identity `o` is an identity. -/
|
||||
left_id : ∀ a, op o a = a
|
||||
|
||||
/--
|
||||
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class RightIdentity (op : α → β → α) (o : outParam β) : Prop
|
||||
|
||||
/--
|
||||
`LawfulRightIdentify op o` indicates `o` is a verified right identity of
|
||||
`op`.
|
||||
-/
|
||||
class LawfulRightIdentity (op : α → β → α) (o : outParam β) extends RightIdentity op o : Prop where
|
||||
/-- Right identity `o` is an identity. -/
|
||||
right_id : ∀ a, op a o = a
|
||||
|
||||
/--
|
||||
`Identity op o` indicates `o` is a left and right identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class Identity (op : α → α → α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop
|
||||
|
||||
/--
|
||||
`LawfulIdentity op o` indicates `o` is a verified left and right
|
||||
identity of `op`.
|
||||
-/
|
||||
class LawfulIdentity (op : α → α → α) (o : outParam α) extends Identity op o, LawfulLeftIdentity op o, LawfulRightIdentity op o : Prop
|
||||
|
||||
/--
|
||||
`LawfulCommIdentity` can simplify defining instances of `LawfulIdentity`
|
||||
on commutative functions by requiring only a left or right identity
|
||||
proof.
|
||||
|
||||
This class is intended for simplifying defining instances of
|
||||
`LawfulIdentity` and functions needed commutative operations with
|
||||
identity should just add a `LawfulIdentity` constraint.
|
||||
-/
|
||||
class LawfulCommIdentity (op : α → α → α) (o : outParam α) [hc : Commutative op] extends LawfulIdentity op o : Prop where
|
||||
left_id a := Eq.trans (hc.comm o a) (right_id a)
|
||||
right_id a := Eq.trans (hc.comm a o) (left_id a)
|
||||
|
||||
end Std
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Basic
|
||||
import Init.Data.Nat
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Char
|
||||
import Init.Data.String
|
||||
import Init.Data.List
|
||||
|
||||
@@ -14,15 +14,17 @@ inductive Expr
|
||||
| op (lhs rhs : Expr)
|
||||
deriving Inhabited, Repr, BEq
|
||||
|
||||
open Std
|
||||
|
||||
structure Variable {α : Sort u} (op : α → α → α) : Type u where
|
||||
value : α
|
||||
neutral : Option $ IsNeutral op value
|
||||
neutral : Option $ PLift (LawfulIdentity op value)
|
||||
|
||||
structure Context (α : Sort u) where
|
||||
op : α → α → α
|
||||
assoc : IsAssociative op
|
||||
comm : Option $ IsCommutative op
|
||||
idem : Option $ IsIdempotent op
|
||||
assoc : Associative op
|
||||
comm : Option $ PLift $ Commutative op
|
||||
idem : Option $ PLift $ IdempotentOp op
|
||||
vars : List (Variable op)
|
||||
arbitrary : α
|
||||
|
||||
@@ -128,7 +130,14 @@ theorem Context.mergeIdem_head2 (h : x ≠ y) : mergeIdem (x :: y :: ys) = x ::
|
||||
simp [mergeIdem, mergeIdem.loop, h]
|
||||
|
||||
theorem Context.evalList_mergeIdem (ctx : Context α) (h : ContextInformation.isIdem ctx) (e : List Nat) : evalList α ctx (mergeIdem e) = evalList α ctx e := by
|
||||
have h : IsIdempotent ctx.op := by simp [ContextInformation.isIdem, Option.isSome] at h; cases h₂ : ctx.idem <;> simp [h₂] at h; assumption
|
||||
have h : IdempotentOp ctx.op := by
|
||||
simp [ContextInformation.isIdem, Option.isSome] at h;
|
||||
match h₂ : ctx.idem with
|
||||
| none =>
|
||||
simp [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
@@ -141,18 +150,18 @@ theorem Context.evalList_mergeIdem (ctx : Context α) (h : ContextInformation.is
|
||||
rfl
|
||||
| cons z zs =>
|
||||
by_cases h₂ : x = y
|
||||
case inl =>
|
||||
case pos =>
|
||||
rw [h₂, mergeIdem_head, ih]
|
||||
simp [evalList, ←ctx.assoc.1, h.1, EvalInformation.evalOp]
|
||||
case inr =>
|
||||
case neg =>
|
||||
rw [mergeIdem_head2]
|
||||
by_cases h₃ : y = z
|
||||
case inl =>
|
||||
case pos =>
|
||||
simp [mergeIdem_head, h₃, evalList]
|
||||
cases h₄ : mergeIdem (z :: zs) with
|
||||
| nil => apply absurd h₄; apply mergeIdem_nonEmpty; simp
|
||||
| cons u us => simp_all [mergeIdem, mergeIdem.loop, evalList]
|
||||
case inr =>
|
||||
case neg =>
|
||||
simp [mergeIdem_head2, h₃, evalList] at *
|
||||
rw [ih]
|
||||
assumption
|
||||
@@ -169,7 +178,7 @@ theorem Context.sort_loop_nonEmpty (xs : List Nat) (h : xs ≠ []) : sort.loop x
|
||||
|
||||
theorem Context.evalList_insert
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (insert x xs) = evalList α ctx (x::xs) := by
|
||||
@@ -190,7 +199,7 @@ theorem Context.evalList_insert
|
||||
|
||||
theorem Context.evalList_sort_congr
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(h₂ : evalList α ctx a = evalList α ctx b)
|
||||
(h₃ : a ≠ [])
|
||||
(h₄ : b ≠ [])
|
||||
@@ -209,7 +218,7 @@ theorem Context.evalList_sort_congr
|
||||
|
||||
theorem Context.evalList_sort_loop_swap
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(xs ys : List Nat)
|
||||
: evalList α ctx (sort.loop xs (y::ys)) = evalList α ctx (sort.loop (y::xs) ys) := by
|
||||
induction ys generalizing y xs with
|
||||
@@ -224,7 +233,7 @@ theorem Context.evalList_sort_loop_swap
|
||||
|
||||
theorem Context.evalList_sort_cons
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (sort (x :: xs)) = evalList α ctx (x :: sort xs) := by
|
||||
@@ -247,7 +256,14 @@ theorem Context.evalList_sort_cons
|
||||
all_goals simp [insert_nonEmpty]
|
||||
|
||||
theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm ctx) (e : List Nat) : evalList α ctx (sort e) = evalList α ctx e := by
|
||||
have h : IsCommutative ctx.op := by simp [ContextInformation.isComm, Option.isSome] at h; cases h₂ : ctx.comm <;> simp [h₂] at h; assumption
|
||||
have h : Commutative ctx.op := by
|
||||
simp [ContextInformation.isComm, Option.isSome] at h
|
||||
match h₂ : ctx.comm with
|
||||
| none =>
|
||||
simp only [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
@@ -269,10 +285,12 @@ theorem Context.toList_nonEmpty (e : Expr) : e.toList ≠ [] := by
|
||||
theorem Context.unwrap_isNeutral
|
||||
{ctx : Context α}
|
||||
{x : Nat}
|
||||
: ContextInformation.isNeutral ctx x = true → IsNeutral (EvalInformation.evalOp ctx) (EvalInformation.evalVar (β := α) ctx x) := by
|
||||
: ContextInformation.isNeutral ctx x = true → LawfulIdentity (EvalInformation.evalOp ctx) (EvalInformation.evalVar (β := α) ctx x) := by
|
||||
simp [ContextInformation.isNeutral, Option.isSome, EvalInformation.evalOp, EvalInformation.evalVar]
|
||||
match (var ctx x).neutral with
|
||||
| some hn => intro; assumption
|
||||
| some hn =>
|
||||
intro
|
||||
exact hn.down
|
||||
| none => intro; contradiction
|
||||
|
||||
theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) : evalList α ctx (removeNeutrals ctx e) = evalList α ctx e := by
|
||||
@@ -283,10 +301,12 @@ theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) : eval
|
||||
case h_1 => rfl
|
||||
case h_2 h => split at h <;> simp_all
|
||||
| step x y ys ih =>
|
||||
cases h₁ : ContextInformation.isNeutral ctx x <;> cases h₂ : ContextInformation.isNeutral ctx y <;> cases h₃ : removeNeutrals.loop ctx ys
|
||||
cases h₁ : ContextInformation.isNeutral ctx x <;>
|
||||
cases h₂ : ContextInformation.isNeutral ctx y <;>
|
||||
cases h₃ : removeNeutrals.loop ctx ys
|
||||
<;> simp [removeNeutrals, removeNeutrals.loop, h₁, h₂, h₃, evalList, ←ih]
|
||||
<;> (try simp [unwrap_isNeutral h₂ |>.2])
|
||||
<;> (try simp [unwrap_isNeutral h₁ |>.1])
|
||||
<;> (try simp [unwrap_isNeutral h₂ |>.right_id])
|
||||
<;> (try simp [unwrap_isNeutral h₁ |>.left_id])
|
||||
|
||||
theorem Context.evalList_append
|
||||
(ctx : Context α)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,21 @@ def mkArray {α : Type u} (n : Nat) (v : α) : Array α := {
|
||||
data := List.replicate n v
|
||||
}
|
||||
|
||||
/--
|
||||
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
|
||||
```
|
||||
ofFn f = #[f 0, f 1, ... , f(n - 1)]
|
||||
``` -/
|
||||
def ofFn {n} (f : Fin n → α) : Array α := go 0 (mkEmpty n) where
|
||||
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
|
||||
go (i : Nat) (acc : Array α) : Array α :=
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
termination_by n - i
|
||||
|
||||
/-- The array `#[0, 1, ..., n - 1]`. -/
|
||||
def range (n : Nat) : Array Nat :=
|
||||
n.fold (flip Array.push) (mkEmpty n)
|
||||
|
||||
@[simp] theorem size_mkArray (n : Nat) (v : α) : (mkArray n v).size = n :=
|
||||
List.length_replicate ..
|
||||
|
||||
@@ -71,6 +86,12 @@ abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size =
|
||||
def uset (a : Array α) (i : USize) (v : α) (h : i.toNat < a.size) : Array α :=
|
||||
a.set ⟨i.toNat, h⟩ v
|
||||
|
||||
/--
|
||||
Swaps two entries in an array.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_fswap"]
|
||||
def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let v₁ := a.get i
|
||||
@@ -78,12 +99,18 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let a' := a.set i v₂
|
||||
a'.set (size_set a i v₂ ▸ j) v₁
|
||||
|
||||
/--
|
||||
Swaps two entries in an array, or panics if either index is out of bounds.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_swap"]
|
||||
def swap! (a : Array α) (i j : @& Nat) : Array α :=
|
||||
if h₁ : i < a.size then
|
||||
if h₂ : j < a.size then swap a ⟨i, h₁⟩ ⟨j, h₂⟩
|
||||
else panic! "index out of bounds"
|
||||
else panic! "index out of bounds"
|
||||
else a
|
||||
else a
|
||||
|
||||
@[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α :=
|
||||
let e := a.get i
|
||||
@@ -276,8 +303,8 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
termination_by as.size - i
|
||||
map 0 (mkEmpty as.size)
|
||||
termination_by map => as.size - i
|
||||
|
||||
@[inline]
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : Fin as.size → α → m β) : m (Array β) :=
|
||||
@@ -348,12 +375,12 @@ def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
loop (j+1)
|
||||
else
|
||||
pure false
|
||||
termination_by stop - j
|
||||
loop start
|
||||
if h : stop ≤ as.size then
|
||||
any stop h
|
||||
else
|
||||
any as.size (Nat.le_refl _)
|
||||
termination_by loop i j => stop - j
|
||||
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
|
||||
@@ -401,6 +428,10 @@ def map {α : Type u} {β : Type v} (f : α → β) (as : Array α) : Array β :
|
||||
def mapIdx {α : Type u} {β : Type v} (as : Array α) (f : Fin as.size → α → β) : Array β :=
|
||||
Id.run <| as.mapIdxM f
|
||||
|
||||
/-- Turns `#[a, b]` into `#[(a, 0), (b, 1)]`. -/
|
||||
def zipWithIndex (arr : Array α) : Array (α × Nat) :=
|
||||
arr.mapIdx fun i a => (a, i)
|
||||
|
||||
@[inline]
|
||||
def find? {α : Type} (as : Array α) (p : α → Bool) : Option α :=
|
||||
Id.run <| as.findM? p
|
||||
@@ -468,10 +499,18 @@ def elem [BEq α] (a : α) (as : Array α) : Bool :=
|
||||
else
|
||||
(true, r)
|
||||
|
||||
/-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/
|
||||
-- This function is exported to C, where it is called by `Array.data`
|
||||
-- (the projection) to implement this functionality.
|
||||
@[export lean_array_to_list]
|
||||
def toList (as : Array α) : List α :=
|
||||
as.foldr List.cons []
|
||||
|
||||
/-- Prepends an `Array α` onto the front of a list. Equivalent to `as.toList ++ l`. -/
|
||||
@[inline]
|
||||
def toListAppend (as : Array α) (l : List α) : List α :=
|
||||
as.foldr List.cons l
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec a _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
@@ -501,6 +540,13 @@ def concatMapM [Monad m] (f : α → m (Array β)) (as : Array α) : m (Array β
|
||||
def concatMap (f : α → Array β) (as : Array α) : Array β :=
|
||||
as.foldl (init := empty) fun bs a => bs ++ f a
|
||||
|
||||
/-- Joins array of array into a single array.
|
||||
|
||||
`flatten #[#[a₁, a₂, ⋯], #[b₁, b₂, ⋯], ⋯]` = `#[a₁, a₂, ⋯, b₁, b₂, ⋯]`
|
||||
-/
|
||||
def flatten (as : Array (Array α)) : Array α :=
|
||||
as.foldl (init := empty) fun r a => r ++ a
|
||||
|
||||
end Array
|
||||
|
||||
export Array (mkArray)
|
||||
@@ -520,7 +566,7 @@ def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) (
|
||||
p a[i] b[i] && isEqvAux a b hsz p (i+1)
|
||||
else
|
||||
true
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
@@ -624,7 +670,7 @@ def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size)
|
||||
if a.get idx == v then some idx
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
@@ -656,7 +702,7 @@ where
|
||||
loop as (i+1) ⟨j-1, this⟩
|
||||
else
|
||||
as
|
||||
termination_by _ => j - i
|
||||
termination_by j - i
|
||||
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
@@ -666,7 +712,7 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
as
|
||||
else
|
||||
as
|
||||
termination_by popWhile as => as.size
|
||||
termination_by as.size
|
||||
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec go (i : Nat) (r : Array α) : Array α :=
|
||||
@@ -678,8 +724,8 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
r
|
||||
else
|
||||
r
|
||||
termination_by as.size - i
|
||||
go 0 #[]
|
||||
termination_by go i r => as.size - i
|
||||
|
||||
def eraseIdxAux (i : Nat) (a : Array α) : Array α :=
|
||||
if h : i < a.size then
|
||||
@@ -689,7 +735,7 @@ def eraseIdxAux (i : Nat) (a : Array α) : Array α :=
|
||||
eraseIdxAux (i+1) a'
|
||||
else
|
||||
a.pop
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
eraseIdxAux (i.val + 1) a
|
||||
@@ -704,7 +750,7 @@ def eraseIdxSzAux (a : Array α) (i : Nat) (r : Array α) (heq : r.size = a.size
|
||||
eraseIdxSzAux a (i+1) (r.swap idx idx1) ((size_swap r idx idx1).trans heq)
|
||||
else
|
||||
⟨r.pop, (size_pop r).trans (heq ▸ rfl)⟩
|
||||
termination_by _ => r.size - i
|
||||
termination_by r.size - i
|
||||
|
||||
def eraseIdx' (a : Array α) (i : Fin a.size) : { r : Array α // r.size = a.size - 1 } :=
|
||||
eraseIdxSzAux a (i.val + 1) a rfl
|
||||
@@ -723,10 +769,10 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
|
||||
loop as ⟨j', by rw [size_swap]; exact j'.2⟩
|
||||
else
|
||||
as
|
||||
termination_by j.1
|
||||
let j := as.size
|
||||
let as := as.push a
|
||||
loop as ⟨j, size_push .. ▸ j.lt_succ_self⟩
|
||||
termination_by loop j => j.1
|
||||
|
||||
/-- Insert element `a` at position `i`. Panics if `i` is not `i ≤ as.size`. -/
|
||||
def insertAt! (as : Array α) (i : Nat) (a : α) : Array α :=
|
||||
@@ -776,7 +822,7 @@ def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : N
|
||||
false
|
||||
else
|
||||
true
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
/-- Return true iff `as` is a prefix of `bs`.
|
||||
That is, `bs = as ++ t` for some `t : List α`.-/
|
||||
@@ -797,7 +843,7 @@ private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
@@ -812,7 +858,7 @@ def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
cs
|
||||
else
|
||||
cs
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
@[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ :=
|
||||
zipWithAux f as bs 0 #[]
|
||||
|
||||
@@ -47,7 +47,7 @@ where
|
||||
have hlt : i < as.size := Nat.lt_of_le_of_ne hle h
|
||||
let b ← f as[i]
|
||||
go (i+1) ⟨acc.val.push b, by simp [acc.property]⟩ hlt
|
||||
termination_by go i _ _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
@[inline] private unsafe def mapMonoMImp [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
go 0 as
|
||||
|
||||
@@ -5,7 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Classical
|
||||
import Init.ByCases
|
||||
|
||||
namespace Array
|
||||
|
||||
@@ -20,7 +20,7 @@ theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size)
|
||||
· have heq : i = a.size := Nat.le_antisymm hi (Nat.ge_of_not_lt h)
|
||||
subst heq
|
||||
exact absurd (Nat.lt_of_lt_of_le high low) (Nat.lt_irrefl j)
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) → a = b := by
|
||||
simp [Array.isEqv]
|
||||
@@ -36,7 +36,7 @@ theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux
|
||||
split
|
||||
case inl h => simp [h, isEqvAux_self a (i+1)]
|
||||
case inr h => simp [h]
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
187
src/Init/Data/Array/Lemmas.lean
Normal file
187
src/Init/Data/Array/Lemmas.lean
Normal file
@@ -0,0 +1,187 @@
|
||||
/-
|
||||
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
|
||||
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⟩
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2022 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, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
@@ -20,32 +20,26 @@ theorem List.sizeOf_get_lt [SizeOf α] (as : List α) (i : Fin as.length) : size
|
||||
|
||||
namespace Array
|
||||
|
||||
instance [DecidableEq α] : Membership α (Array α) where
|
||||
mem a as := as.contains a
|
||||
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
|
||||
-- NB: This is defined as a structure rather than a plain def so that a lemma
|
||||
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
|
||||
structure Mem (a : α) (as : Array α) : Prop where
|
||||
val : a ∈ as.data
|
||||
|
||||
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; rename_i as
|
||||
simp [get]
|
||||
have ih := List.sizeOf_get_lt as i
|
||||
exact Nat.lt_trans ih (by simp_arith)
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_get_lt as i) (by simp_arith)
|
||||
|
||||
theorem sizeOf_lt_of_mem [DecidableEq α] [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a < sizeOf as := by
|
||||
simp [Membership.mem, contains, any, Id.run, BEq.beq, anyM] at h
|
||||
let rec aux (j : Nat) (h : anyM.loop (m := Id) (fun b => decide (a = b)) as as.size (Nat.le_refl ..) j = true) : sizeOf a < sizeOf as := by
|
||||
unfold anyM.loop at h
|
||||
split at h
|
||||
· simp [Bind.bind, pure] at h; split at h
|
||||
next he => subst a; apply sizeOf_get_lt
|
||||
next => have ih := aux (j+1) h; assumption
|
||||
· contradiction
|
||||
apply aux 0 h
|
||||
termination_by aux j _ => as.size - j
|
||||
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)
|
||||
|
||||
@[simp] theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
|
||||
cases as
|
||||
simp [get]
|
||||
apply Nat.lt_trans (List.sizeOf_get ..)
|
||||
simp_arith
|
||||
cases as with | _ as =>
|
||||
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
|
||||
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
|
||||
@@ -57,4 +51,17 @@ macro "array_get_dec" : tactic =>
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that `sizeOf a < sizeOf arr`
|
||||
provided that `a ∈ arr` which is useful for well founded recursions over a nested inductive like
|
||||
`inductive T | mk : Array T → T`. -/
|
||||
-- NB: This is analogue to tactic `sizeOf_list_dec`
|
||||
macro "array_mem_dec" : tactic =>
|
||||
`(tactic| first
|
||||
| apply Array.sizeOf_lt_of_mem; assumption; done
|
||||
| apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_mem_dec)
|
||||
|
||||
end Array
|
||||
|
||||
@@ -26,8 +26,8 @@ def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat ×
|
||||
else
|
||||
let as := as.swap! i hi
|
||||
(i, as)
|
||||
termination_by hi - j
|
||||
loop as lo lo
|
||||
termination_by _ => hi - j
|
||||
|
||||
@[inline] partial def qsort (as : Array α) (lt : α → α → Bool) (low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort (as : Array α) (low high : Nat) :=
|
||||
|
||||
@@ -81,7 +81,7 @@ def isEmpty (s : ByteArray) : Bool :=
|
||||
If `exact` is `false`, the capacity will be doubled when grown. -/
|
||||
@[extern "lean_byte_array_copy_slice"]
|
||||
def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray :=
|
||||
⟨dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + len) dest.data.size⟩
|
||||
⟨dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + min len (src.data.size - srcOff)) dest.data.size⟩
|
||||
|
||||
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
|
||||
a.copySlice b empty 0 (e - b)
|
||||
|
||||
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
|
||||
@@ -100,12 +100,14 @@ instance : ShiftLeft (Fin n) where
|
||||
instance : ShiftRight (Fin n) where
|
||||
shiftRight := Fin.shiftRight
|
||||
|
||||
instance : OfNat (Fin (no_index (n+1))) i where
|
||||
instance instOfNat : OfNat (Fin (no_index (n+1))) i where
|
||||
ofNat := Fin.ofNat i
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ opaque floatSpec : FloatSpec := {
|
||||
decLe := fun _ _ => inferInstanceAs (Decidable True)
|
||||
}
|
||||
|
||||
/-- Native floating point type, corresponding to the IEEE 754 *binary64* format
|
||||
(`double` in C or `f64` in Rust). -/
|
||||
structure Float where
|
||||
val : floatSpec.float
|
||||
|
||||
@@ -132,7 +134,7 @@ instance : ReprAtom Float := ⟨⟩
|
||||
@[extern "round"] opaque Float.round : Float → Float
|
||||
@[extern "fabs"] opaque Float.abs : Float → Float
|
||||
|
||||
instance : Pow Float Float := ⟨Float.pow⟩
|
||||
instance : HomogeneousPow Float := ⟨Float.pow⟩
|
||||
|
||||
instance : Min Float := minOfLe
|
||||
|
||||
|
||||
@@ -300,11 +300,18 @@ instance : MonadPrettyFormat (StateM State) where
|
||||
startTag _ := return ()
|
||||
endTags _ := return ()
|
||||
|
||||
/-- Pretty-print a `Format` object as a string with expected width `w`. -/
|
||||
/--
|
||||
Renders a `Format` to a string.
|
||||
* `width`: the total width
|
||||
* `indent`: the initial indentation to use for wrapped lines
|
||||
(subsequent wrapping may increase the indentation)
|
||||
* `column`: begin the first line wrap `column` characters earlier than usual
|
||||
(this is useful when the output String will be printed starting at `column`)
|
||||
-/
|
||||
@[export lean_format_pretty]
|
||||
def pretty (f : Format) (w : Nat := defWidth) : String :=
|
||||
let act: StateM State Unit := prettyM f w
|
||||
act {} |>.snd.out
|
||||
def pretty (f : Format) (width : Nat := defWidth) (indent : Nat := 0) (column := 0) : String :=
|
||||
let act : StateM State Unit := prettyM f width indent
|
||||
State.out <| act (State.mk "" column) |>.snd
|
||||
|
||||
end Format
|
||||
|
||||
|
||||
@@ -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 : OfNat Int n where
|
||||
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
|
||||
|
||||
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
|
||||
159
src/Init/Data/Int/DivMod.lean
Normal file
159
src/Init/Data/Int/DivMod.lean
Normal file
@@ -0,0 +1,159 @@
|
||||
/-
|
||||
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`.
|
||||
-/
|
||||
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`.
|
||||
-/
|
||||
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
|
||||
|
||||
end Int
|
||||
347
src/Init/Data/Int/DivModLemmas.lean
Normal file
347
src/Init/Data/Int/DivModLemmas.lean
Normal file
@@ -0,0 +1,347 @@
|
||||
/-
|
||||
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] 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] 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]⟩
|
||||
|
||||
|
||||
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
|
||||
|
||||
/-!
|
||||
# `bmod` ("balanced" mod)
|
||||
|
||||
We use balanced mod in the omega algorithm,
|
||||
to make ±1 coefficients appear in equations without them.
|
||||
-/
|
||||
|
||||
/--
|
||||
Balanced mod, taking values in the range [- m/2, (m - 1)/2].
|
||||
-/
|
||||
def bmod (x : Int) (m : Nat) : Int :=
|
||||
let r := x % m
|
||||
if r < (m + 1) / 2 then
|
||||
r
|
||||
else
|
||||
r - m
|
||||
|
||||
@[simp] theorem bmod_emod : bmod x m % m = x % m := by
|
||||
dsimp [bmod]
|
||||
split <;> simp [Int.sub_emod]
|
||||
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
|
||||
500
src/Init/Data/Int/Lemmas.lean
Normal file
500
src/Init/Data/Int/Lemmas.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.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
|
||||
|
||||
theorem ofNat_add (n m : Nat) : (↑(n + m) : Int) = n + m := rfl
|
||||
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 -/
|
||||
|
||||
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] 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]
|
||||
|
||||
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
|
||||
|
||||
/- ## 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]
|
||||
|
||||
/-! 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
|
||||
|
||||
end Int
|
||||
438
src/Init/Data/Int/Order.lean
Normal file
438
src/Init/Data/Int/Order.lean
Normal file
@@ -0,0 +1,438 @@
|
||||
/-
|
||||
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] 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] theorem ofNat_lt {n m : Nat} : (↑n : Int) < ↑m ↔ n < m := by
|
||||
rw [lt_iff_add_one_le, ← ofNat_succ, ofNat_le]; rfl
|
||||
|
||||
@[simp] 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 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⟩
|
||||
|
||||
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)]
|
||||
@@ -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
|
||||
|
||||
@@ -6,9 +6,48 @@ Author: Leonardo de Moura
|
||||
prelude
|
||||
import Init.SimpLemmas
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.Nat.Div
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
open Decidable List
|
||||
|
||||
/--
|
||||
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
|
||||
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
|
||||
list literals.
|
||||
|
||||
For lists of length at least 64, an alternative desugaring strategy is used
|
||||
which uses let bindings as intermediates as in
|
||||
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
|
||||
Note that this changes the order of evaluation, although it should not be observable
|
||||
unless you use side effecting operations like `dbg_trace`.
|
||||
-/
|
||||
syntax "[" withoutPosition(term,*,?) "]" : term
|
||||
|
||||
/--
|
||||
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
|
||||
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
|
||||
It uses binary partitioning to construct a tree of intermediate let bindings as in
|
||||
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
|
||||
-/
|
||||
syntax "%[" withoutPosition(term,*,? " | " term) "]" : term
|
||||
|
||||
namespace Lean
|
||||
|
||||
macro_rules
|
||||
| `([ $elems,* ]) => do
|
||||
-- NOTE: we do not have `TSepArray.getElems` yet at this point
|
||||
let rec expandListLit (i : Nat) (skip : Bool) (result : TSyntax `term) : MacroM Syntax := do
|
||||
match i, skip with
|
||||
| 0, _ => pure result
|
||||
| i+1, true => expandListLit i false result
|
||||
| i+1, false => expandListLit i true (← ``(List.cons $(⟨elems.elemsAndSeps.get! i⟩) $result))
|
||||
let size := elems.elemsAndSeps.size
|
||||
if size < 64 then
|
||||
expandListLit size (size % 2 == 0) (← ``(List.nil))
|
||||
else
|
||||
`(%[ $elems,* | List.nil ])
|
||||
end Lean
|
||||
|
||||
universe u v w
|
||||
|
||||
variable {α : Type u} {β : Type v} {γ : Type w}
|
||||
@@ -85,7 +124,8 @@ def appendTR (as bs : List α) : List α :=
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons a as ih =>
|
||||
simp [reverseAux, List.append, ih, reverseAux_reverseAux]
|
||||
rw [reverseAux, reverseAux_reverseAux]
|
||||
simp [List.append, ih, reverseAux]
|
||||
|
||||
instance : Append (List α) := ⟨List.append⟩
|
||||
|
||||
@@ -356,7 +396,7 @@ inductive Mem (a : α) : List α → Prop
|
||||
instance : Membership α (List α) where
|
||||
mem := Mem
|
||||
|
||||
theorem mem_of_elem_eq_true [DecidableEq α] {a : α} {as : List α} : elem a as = true → a ∈ as := by
|
||||
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true → a ∈ as := by
|
||||
match as with
|
||||
| [] => simp [elem]
|
||||
| a'::as =>
|
||||
@@ -365,12 +405,12 @@ theorem mem_of_elem_eq_true [DecidableEq α] {a : α} {as : List α} : elem a as
|
||||
next h => intros; simp [BEq.beq] at h; subst h; apply Mem.head
|
||||
next _ => intro h; exact Mem.tail _ (mem_of_elem_eq_true h)
|
||||
|
||||
theorem elem_eq_true_of_mem [DecidableEq α] {a : α} {as : List α} (h : a ∈ as) : elem a as = true := by
|
||||
theorem elem_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : List α} (h : a ∈ as) : elem a as = true := by
|
||||
induction h with
|
||||
| head _ => simp [elem]
|
||||
| tail _ _ ih => simp [elem]; split; rfl; assumption
|
||||
|
||||
instance [DecidableEq α] (a : α) (as : List α) : Decidable (a ∈ as) :=
|
||||
instance [BEq α] [LawfulBEq α] (a : α) (as : List α) : Decidable (a ∈ as) :=
|
||||
decidable_of_decidable_of_iff (Iff.intro mem_of_elem_eq_true elem_eq_true_of_mem)
|
||||
|
||||
theorem mem_append_of_mem_left {a : α} {as : List α} (bs : List α) : a ∈ as → a ∈ as ++ bs := by
|
||||
@@ -518,16 +558,22 @@ def takeWhile (p : α → Bool) : (xs : List α) → List α
|
||||
/--
|
||||
`O(|l|)`. Returns true if `p` is true for any element of `l`.
|
||||
* `any p [a, b, c] = p a || p b || p c`
|
||||
|
||||
Short-circuits upon encountering the first `true`.
|
||||
-/
|
||||
@[inline] def any (l : List α) (p : α → Bool) : Bool :=
|
||||
foldr (fun a r => p a || r) false l
|
||||
def any : List α -> (α → Bool) -> Bool
|
||||
| [], _ => false
|
||||
| h :: t, p => p h || any t p
|
||||
|
||||
/--
|
||||
`O(|l|)`. Returns true if `p` is true for every element of `l`.
|
||||
* `all p [a, b, c] = p a && p b && p c`
|
||||
|
||||
Short-circuits upon encountering the first `false`.
|
||||
-/
|
||||
@[inline] def all (l : List α) (p : α → Bool) : Bool :=
|
||||
foldr (fun a r => p a && r) true l
|
||||
def all : List α -> (α → Bool) -> Bool
|
||||
| [], _ => true
|
||||
| h :: t, p => p h && all t p
|
||||
|
||||
/--
|
||||
`O(|l|)`. Returns true if `true` is an element of the list of booleans `l`.
|
||||
@@ -557,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₃])`
|
||||
@@ -830,7 +897,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
|
||||
|
||||
630
src/Init/Data/List/Lemmas.lean
Normal file
630
src/Init/Data/List/Lemmas.lean
Normal file
@@ -0,0 +1,630 @@
|
||||
/-
|
||||
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.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
|
||||
|
||||
/-! ### 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
|
||||
|
||||
/-! ### 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
|
||||
|
||||
@[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]
|
||||
|
||||
/-! ### 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 [*, or_and_right]
|
||||
· exact or_congr_left (and_iff_left_of_imp fun | rfl => h).symm
|
||||
· exact (or_iff_right fun ⟨rfl, h'⟩ => h h').symm
|
||||
|
||||
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))
|
||||
@@ -6,7 +6,9 @@ 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
|
||||
|
||||
@@ -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 :=
|
||||
@@ -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 :=
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
@@ -629,7 +670,7 @@ protected theorem sub_lt_sub_left : ∀ {k m n : Nat}, k < m → k < n → m - n
|
||||
@[simp] protected theorem zero_sub (n : Nat) : 0 - n = 0 := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [ih, Nat.sub_succ]
|
||||
| succ n ih => simp only [ih, Nat.sub_succ]; decide
|
||||
|
||||
protected theorem sub_self_add (n m : Nat) : n - (n + m) = 0 := by
|
||||
show (n + 0) - (n + m) = 0
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
96
src/Init/Data/Nat/Dvd.lean
Normal file
96
src/Init/Data/Nat/Dvd.lean
Normal file
@@ -0,0 +1,96 @@
|
||||
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,18 +4,18 @@ 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
|
||||
|
||||
private def gcdF (x : Nat) : (∀ x₁, x₁ < x → Nat → Nat) → Nat → Nat :=
|
||||
match x with
|
||||
| 0 => fun _ y => y
|
||||
| succ x => fun f y => f (y % succ x) (mod_lt _ (zero_lt_succ _)) (succ x)
|
||||
|
||||
@[extern "lean_nat_gcd"]
|
||||
def gcd (a b : @& Nat) : Nat :=
|
||||
WellFounded.fix (measure id).wf gcdF a b
|
||||
def gcd (m n : @& Nat) : Nat :=
|
||||
if m = 0 then
|
||||
n
|
||||
else
|
||||
gcd (n % m) m
|
||||
termination_by m
|
||||
decreasing_by simp_wf; apply mod_lt _ (zero_lt_of_ne_zero _); assumption
|
||||
|
||||
@[simp] theorem gcd_zero_left (y : Nat) : gcd 0 y = y :=
|
||||
rfl
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
51
src/Init/Data/Nat/MinMax.lean
Normal file
51
src/Init/Data/Nat/MinMax.lean
Normal file
@@ -0,0 +1,51 @@
|
||||
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
|
||||
@@ -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]
|
||||
@@ -21,8 +23,8 @@ where
|
||||
go (power * 2) (Nat.mul_pos h (by decide))
|
||||
else
|
||||
power
|
||||
termination_by go p h => n - p
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
termination_by n - power
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
|
||||
def isPowerOfTwo (n : Nat) := ∃ k, n = 2 ^ k
|
||||
|
||||
@@ -48,7 +50,7 @@ where
|
||||
split
|
||||
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.mul2_isPowerOfTwo_of_isPowerOfTwo h₂)
|
||||
. assumption
|
||||
termination_by isPowerOfTwo_go p _ _ => n - p
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
termination_by n - power
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Meta
|
||||
import Init.Data.Float
|
||||
import Init.Data.Nat
|
||||
import Init.Data.Nat.Log2
|
||||
|
||||
/-- For decimal and scientific numbers (e.g., `1.23`, `3.12e10`).
|
||||
Examples:
|
||||
|
||||
@@ -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⟩ =>
|
||||
|
||||
@@ -76,10 +76,12 @@ macro_rules
|
||||
end Range
|
||||
end Std
|
||||
|
||||
theorem Membership.mem.upper {i : Nat} {r : Std.Range} (h : i ∈ r) : i < r.stop := by
|
||||
simp [Membership.mem] at h
|
||||
exact h.2
|
||||
theorem Membership.mem.upper {i : Nat} {r : Std.Range} (h : i ∈ r) : i < r.stop := h.2
|
||||
|
||||
theorem Membership.mem.lower {i : Nat} {r : Std.Range} (h : i ∈ r) : r.start ≤ i := by
|
||||
simp [Membership.mem] at h
|
||||
exact h.1
|
||||
theorem Membership.mem.lower {i : Nat} {r : Std.Range} (h : i ∈ r) : r.start ≤ i := h.1
|
||||
|
||||
theorem Membership.get_elem_helper {i n : Nat} {r : Std.Range} (h₁ : i ∈ r) (h₂ : r.stop = n) :
|
||||
i < n := h₂ ▸ h₁.2
|
||||
|
||||
macro_rules
|
||||
| `(tactic| get_elem_tactic_trivial) => `(tactic| apply Membership.get_elem_helper; assumption; rfl)
|
||||
|
||||
@@ -159,7 +159,7 @@ def posOfAux (s : String) (c : Char) (stopPos : Pos) (pos : Pos) : Pos :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next s pos)
|
||||
posOfAux s c stopPos (s.next pos)
|
||||
else pos
|
||||
termination_by _ => stopPos.1 - pos.1
|
||||
termination_by stopPos.1 - pos.1
|
||||
|
||||
@[inline] def posOf (s : String) (c : Char) : Pos :=
|
||||
posOfAux s c s.endPos 0
|
||||
@@ -171,7 +171,7 @@ def revPosOfAux (s : String) (c : Char) (pos : Pos) : Option Pos :=
|
||||
let pos := s.prev pos
|
||||
if s.get pos == c then some pos
|
||||
else revPosOfAux s c pos
|
||||
termination_by _ => pos.1
|
||||
termination_by pos.1
|
||||
|
||||
def revPosOf (s : String) (c : Char) : Option Pos :=
|
||||
revPosOfAux s c s.endPos
|
||||
@@ -183,7 +183,7 @@ def findAux (s : String) (p : Char → Bool) (stopPos : Pos) (pos : Pos) : Pos :
|
||||
have := Nat.sub_lt_sub_left h (lt_next s pos)
|
||||
findAux s p stopPos (s.next pos)
|
||||
else pos
|
||||
termination_by _ => stopPos.1 - pos.1
|
||||
termination_by stopPos.1 - pos.1
|
||||
|
||||
@[inline] def find (s : String) (p : Char → Bool) : Pos :=
|
||||
findAux s p s.endPos 0
|
||||
@@ -195,7 +195,7 @@ def revFindAux (s : String) (p : Char → Bool) (pos : Pos) : Option Pos :=
|
||||
let pos := s.prev pos
|
||||
if p (s.get pos) then some pos
|
||||
else revFindAux s p pos
|
||||
termination_by _ => pos.1
|
||||
termination_by pos.1
|
||||
|
||||
def revFind (s : String) (p : Char → Bool) : Option Pos :=
|
||||
revFindAux s p s.endPos
|
||||
@@ -213,8 +213,8 @@ def firstDiffPos (a b : String) : Pos :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next a i)
|
||||
loop (a.next i)
|
||||
else i
|
||||
termination_by stopPos.1 - i.1
|
||||
loop 0
|
||||
termination_by loop => stopPos.1 - i.1
|
||||
|
||||
@[extern "lean_string_utf8_extract"]
|
||||
def extract : (@& String) → (@& Pos) → (@& Pos) → String
|
||||
@@ -240,7 +240,7 @@ where
|
||||
splitAux s p i' i' (s.extract b i :: r)
|
||||
else
|
||||
splitAux s p b (s.next i) r
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
@[specialize] def split (s : String) (p : Char → Bool) : List String :=
|
||||
splitAux s p 0 0 []
|
||||
@@ -260,7 +260,7 @@ def splitOnAux (s sep : String) (b : Pos) (i : Pos) (j : Pos) (r : List String)
|
||||
splitOnAux s sep b i j r
|
||||
else
|
||||
splitOnAux s sep b (s.next i) 0 r
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
def splitOn (s : String) (sep : String := " ") : List String :=
|
||||
if sep == "" then [s] else splitOnAux s sep 0 0 0 []
|
||||
@@ -369,7 +369,7 @@ def offsetOfPosAux (s : String) (pos : Pos) (i : Pos) (offset : Nat) : Nat :=
|
||||
else
|
||||
have := Nat.sub_lt_sub_left (Nat.gt_of_not_le (mt decide_eq_true h)) (lt_next s _)
|
||||
offsetOfPosAux s pos (s.next i) (offset+1)
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
offsetOfPosAux s pos 0 0
|
||||
@@ -379,7 +379,7 @@ def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next s i)
|
||||
foldlAux f s stopPos (s.next i) (f a (s.get i))
|
||||
else a
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def foldl {α : Type u} (f : α → Char → α) (init : α) (s : String) : α :=
|
||||
foldlAux f s s.endPos 0 init
|
||||
@@ -392,7 +392,7 @@ termination_by _ => stopPos.1 - i.1
|
||||
let a := f (s.get i) a
|
||||
foldrAux f a s i begPos
|
||||
else a
|
||||
termination_by _ => i.1
|
||||
termination_by i.1
|
||||
|
||||
@[inline] def foldr {α : Type u} (f : Char → α → α) (init : α) (s : String) : α :=
|
||||
foldrAux f init s s.endPos 0
|
||||
@@ -404,7 +404,7 @@ termination_by _ => i.1
|
||||
have := Nat.sub_lt_sub_left h (lt_next s i)
|
||||
anyAux s stopPos p (s.next i)
|
||||
else false
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def any (s : String) (p : Char → Bool) : Bool :=
|
||||
anyAux s s.endPos p 0
|
||||
@@ -463,7 +463,7 @@ theorem mapAux_lemma (s : String) (i : Pos) (c : Char) (h : ¬s.atEnd i) :
|
||||
have := mapAux_lemma s i c h
|
||||
let s := s.set i c
|
||||
mapAux f (s.next i) s
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
@[inline] def map (f : Char → Char) (s : String) : String :=
|
||||
mapAux f 0 s
|
||||
@@ -490,7 +490,7 @@ where
|
||||
have := Nat.sub_lt_sub_left h (Nat.add_lt_add_left (one_le_csize c₁) off1.1)
|
||||
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
|
||||
else true
|
||||
termination_by loop => stop1.1 - off1.1
|
||||
termination_by stop1.1 - off1.1
|
||||
|
||||
/-- Return true iff `p` is a prefix of `s` -/
|
||||
def isPrefixOf (p : String) (s : String) : Bool :=
|
||||
@@ -512,8 +512,14 @@ def replace (s pattern replacement : String) : String :=
|
||||
else
|
||||
have := Nat.sub_lt_sub_left this (lt_next s pos)
|
||||
loop acc accStop (s.next pos)
|
||||
termination_by s.endPos.1 - pos.1
|
||||
loop "" 0 0
|
||||
termination_by loop => s.endPos.1 - pos.1
|
||||
|
||||
/-- Return the beginning of the line that contains character `pos`. -/
|
||||
def findLineStart (s : String) (pos : String.Pos) : String.Pos :=
|
||||
match s.revFindAux (· = '\n') pos with
|
||||
| none => 0
|
||||
| some n => ⟨n.byteIdx + 1⟩
|
||||
|
||||
end String
|
||||
|
||||
@@ -612,8 +618,8 @@ def splitOn (s : Substring) (sep : String := " ") : List Substring :=
|
||||
else
|
||||
s.extract b i :: r
|
||||
r.reverse
|
||||
termination_by s.bsize - i.1
|
||||
loop 0 0 0 []
|
||||
termination_by loop => s.bsize - i.1
|
||||
|
||||
@[inline] def foldl {α : Type u} (f : α → Char → α) (init : α) (s : Substring) : α :=
|
||||
match s with
|
||||
@@ -640,7 +646,7 @@ def contains (s : Substring) (c : Char) : Bool :=
|
||||
takeWhileAux s stopPos p (s.next i)
|
||||
else i
|
||||
else i
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def takeWhile : Substring → (Char → Bool) → Substring
|
||||
| ⟨s, b, e⟩, p =>
|
||||
@@ -661,7 +667,7 @@ termination_by _ => stopPos.1 - i.1
|
||||
if !p c then i
|
||||
else takeRightWhileAux s begPos p i'
|
||||
else i
|
||||
termination_by _ => i.1
|
||||
termination_by i.1
|
||||
|
||||
@[inline] def takeRightWhile : Substring → (Char → Bool) → Substring
|
||||
| ⟨s, b, e⟩, p =>
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.System.Platform
|
||||
|
||||
open Nat
|
||||
|
||||
@@ -39,7 +38,7 @@ def UInt8.shiftRight (a b : UInt8) : UInt8 := ⟨a.val >>> (modn b 8).val⟩
|
||||
def UInt8.lt (a b : UInt8) : Prop := a.val < b.val
|
||||
def UInt8.le (a b : UInt8) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
instance UInt8.instOfNat : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
instance : Add UInt8 := ⟨UInt8.add⟩
|
||||
instance : Sub UInt8 := ⟨UInt8.sub⟩
|
||||
instance : Mul UInt8 := ⟨UInt8.mul⟩
|
||||
@@ -110,8 +109,7 @@ def UInt16.shiftRight (a b : UInt16) : UInt16 := ⟨a.val >>> (modn b 16).val⟩
|
||||
def UInt16.lt (a b : UInt16) : Prop := a.val < b.val
|
||||
def UInt16.le (a b : UInt16) : Prop := a.val ≤ b.val
|
||||
|
||||
|
||||
instance : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
instance UInt16.instOfNat : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
instance : Add UInt16 := ⟨UInt16.add⟩
|
||||
instance : Sub UInt16 := ⟨UInt16.sub⟩
|
||||
instance : Mul UInt16 := ⟨UInt16.mul⟩
|
||||
@@ -152,6 +150,14 @@ instance : Min UInt16 := minOfLe
|
||||
def UInt32.ofNat (n : @& Nat) : UInt32 := ⟨Fin.ofNat n⟩
|
||||
@[extern "lean_uint32_of_nat"]
|
||||
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := ⟨⟨n, h⟩⟩
|
||||
/--
|
||||
Converts the given natural number to `UInt32`, but returns `2^32 - 1` for natural numbers `>= 2^32`.
|
||||
-/
|
||||
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
|
||||
if h : n < UInt32.size then
|
||||
UInt32.ofNat' n h
|
||||
else
|
||||
UInt32.ofNat' (UInt32.size - 1) (by decide)
|
||||
abbrev Nat.toUInt32 := UInt32.ofNat
|
||||
@[extern "lean_uint32_add"]
|
||||
def UInt32.add (a b : UInt32) : UInt32 := ⟨a.val + b.val⟩
|
||||
@@ -184,7 +190,7 @@ def UInt8.toUInt32 (a : UInt8) : UInt32 := a.toNat.toUInt32
|
||||
@[extern "lean_uint16_to_uint32"]
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := a.toNat.toUInt32
|
||||
|
||||
instance : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance UInt32.instOfNat : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
instance : Sub UInt32 := ⟨UInt32.sub⟩
|
||||
instance : Mul UInt32 := ⟨UInt32.mul⟩
|
||||
@@ -244,7 +250,7 @@ def UInt16.toUInt64 (a : UInt16) : UInt64 := a.toNat.toUInt64
|
||||
@[extern "lean_uint32_to_uint64"]
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := a.toNat.toUInt64
|
||||
|
||||
instance : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
instance : Sub UInt64 := ⟨UInt64.sub⟩
|
||||
instance : Mul UInt64 := ⟨UInt64.mul⟩
|
||||
@@ -285,7 +291,7 @@ instance : Max UInt64 := maxOfLe
|
||||
instance : Min UInt64 := minOfLe
|
||||
|
||||
theorem usize_size_gt_zero : USize.size > 0 :=
|
||||
Nat.pos_pow_of_pos System.Platform.numBits (Nat.zero_lt_succ _)
|
||||
Nat.zero_lt_succ ..
|
||||
|
||||
@[extern "lean_usize_of_nat"]
|
||||
def USize.ofNat (n : @& Nat) : USize := ⟨Fin.ofNat' n usize_size_gt_zero⟩
|
||||
@@ -322,7 +328,7 @@ def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
|
||||
def USize.lt (a b : USize) : Prop := a.val < b.val
|
||||
def USize.le (a b : USize) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : OfNat USize n := ⟨USize.ofNat n⟩
|
||||
instance USize.instOfNat : OfNat USize n := ⟨USize.ofNat n⟩
|
||||
instance : Add USize := ⟨USize.add⟩
|
||||
instance : Sub USize := ⟨USize.sub⟩
|
||||
instance : Mul USize := ⟨USize.mul⟩
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user