mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 02:44:12 +00:00
Compare commits
1035 Commits
align_mkAr
...
UIntX.ofIn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d8dc1a556 | ||
|
|
a5ddc8f4f9 | ||
|
|
72a63d87c9 | ||
|
|
985cd71f23 | ||
|
|
2ede81fe10 | ||
|
|
4d6ad8b0fb | ||
|
|
c99eade8cf | ||
|
|
3cea6eb7ad | ||
|
|
5b9adfed13 | ||
|
|
07e7a43668 | ||
|
|
388b6f045b | ||
|
|
5a6f45a324 | ||
|
|
e6ce55ffd4 | ||
|
|
1b40c46ab1 | ||
|
|
0b54a76e32 | ||
|
|
4bb8d37e37 | ||
|
|
e76eb6fbd2 | ||
|
|
ca3f43907b | ||
|
|
106b772659 | ||
|
|
e8446c81c8 | ||
|
|
bfed223306 | ||
|
|
a35c62d0ad | ||
|
|
e86644f329 | ||
|
|
d1dad44227 | ||
|
|
ba1c1258d7 | ||
|
|
2d8c642711 | ||
|
|
a3b83f7ca9 | ||
|
|
5a849dee9b | ||
|
|
a6f4802d66 | ||
|
|
acd6b13d76 | ||
|
|
b0acdef433 | ||
|
|
0f2ede45d5 | ||
|
|
ab4febd1df | ||
|
|
f8691bcb62 | ||
|
|
9c7f50a42c | ||
|
|
cd0b54ce5d | ||
|
|
8a373cbebe | ||
|
|
8e4c38968f | ||
|
|
c97092bef8 | ||
|
|
2ea675369f | ||
|
|
23b23c1236 | ||
|
|
ca839f6d6c | ||
|
|
5f684b4777 | ||
|
|
c3ff4334cd | ||
|
|
f4b54a2b18 | ||
|
|
2c203ac681 | ||
|
|
333f7573d7 | ||
|
|
a228380626 | ||
|
|
4dce16cd86 | ||
|
|
007bd18bcb | ||
|
|
c851cdb21e | ||
|
|
e2c3ea7ba5 | ||
|
|
851a63bd01 | ||
|
|
34385b8ee8 | ||
|
|
3b78ada5d8 | ||
|
|
7947457305 | ||
|
|
82c71fcc75 | ||
|
|
258bb22f0a | ||
|
|
674c7ef1d0 | ||
|
|
575e0307bf | ||
|
|
da55b2e19b | ||
|
|
11d21af15e | ||
|
|
407a59d697 | ||
|
|
906edd4529 | ||
|
|
092ece5d49 | ||
|
|
edf88cc5be | ||
|
|
5f2f010d66 | ||
|
|
29303b37b8 | ||
|
|
551d6cf28b | ||
|
|
83067d67d6 | ||
|
|
314f6c73b7 | ||
|
|
680cb0ad5a | ||
|
|
e59d070af1 | ||
|
|
bb6bfdba37 | ||
|
|
626075ca34 | ||
|
|
5e13f81e30 | ||
|
|
196d899c02 | ||
|
|
6a22951e90 | ||
|
|
fd0d585916 | ||
|
|
bdd8d6fcac | ||
|
|
01f3bbb2de | ||
|
|
12ec466aa6 | ||
|
|
c658648ee8 | ||
|
|
9c87db2d77 | ||
|
|
33e456dd3c | ||
|
|
dedfbaf521 | ||
|
|
bd24ca3093 | ||
|
|
fe986b4533 | ||
|
|
336b68ec20 | ||
|
|
5df4e48dc9 | ||
|
|
1ee7e1a9d8 | ||
|
|
85f94abe19 | ||
|
|
2979830120 | ||
|
|
27084f6646 | ||
|
|
cdc2731401 | ||
|
|
6c42cb353a | ||
|
|
8ff05f9760 | ||
|
|
73d08f663d | ||
|
|
b6f18e8e2f | ||
|
|
8b1caa3bc2 | ||
|
|
6a45bd5f77 | ||
|
|
9c6c54107f | ||
|
|
daa41939fe | ||
|
|
2063fd3976 | ||
|
|
55b0d390c6 | ||
|
|
32cd701994 | ||
|
|
911ea07a73 | ||
|
|
fcb0ab8490 | ||
|
|
50cec261fc | ||
|
|
cdedcf6b48 | ||
|
|
7fefa8660e | ||
|
|
34142685a9 | ||
|
|
e7fc50acb1 | ||
|
|
c30c71a278 | ||
|
|
bb07a732e7 | ||
|
|
d6303a8e7f | ||
|
|
1d47360099 | ||
|
|
edb02104d2 | ||
|
|
6faab78384 | ||
|
|
1b5a52a5e9 | ||
|
|
797b0e2c62 | ||
|
|
9753d3ca4a | ||
|
|
169c541751 | ||
|
|
35894b119c | ||
|
|
2edfe2e9cf | ||
|
|
107eb84584 | ||
|
|
96ddeea84e | ||
|
|
c8ee006f91 | ||
|
|
5a50a8d278 | ||
|
|
e00dd3b25a | ||
|
|
3b85d26af7 | ||
|
|
866c8073ea | ||
|
|
5ebac3fa50 | ||
|
|
bc6288a48c | ||
|
|
3f98f6bc07 | ||
|
|
176e8bc077 | ||
|
|
12a21e79c7 | ||
|
|
5a432f69b7 | ||
|
|
56ba3f245b | ||
|
|
e37bbdbf23 | ||
|
|
2fc77e3242 | ||
|
|
0853d40dae | ||
|
|
014e5d9a66 | ||
|
|
c7f8df2dc0 | ||
|
|
101f3f2c0f | ||
|
|
068e0b7215 | ||
|
|
359f5bfda9 | ||
|
|
a558a5a1eb | ||
|
|
1bf2d8eba2 | ||
|
|
5348ce9632 | ||
|
|
cde191da26 | ||
|
|
032a9e817d | ||
|
|
fa2d28e2da | ||
|
|
465d6b4f4b | ||
|
|
2d28331cb6 | ||
|
|
bb23713542 | ||
|
|
5fb990fcbd | ||
|
|
d7f5d9a67a | ||
|
|
4e51487b1c | ||
|
|
c33c2c5fbd | ||
|
|
e4968ae854 | ||
|
|
060b2fe46f | ||
|
|
17c18752ff | ||
|
|
7bd9375804 | ||
|
|
e46cc64d1e | ||
|
|
d95a2ee35e | ||
|
|
725979a91a | ||
|
|
3e3ff31864 | ||
|
|
d0d31e509f | ||
|
|
c2185020c5 | ||
|
|
d2c49d701f | ||
|
|
cf54e5e5d2 | ||
|
|
7d9d622057 | ||
|
|
183463ce24 | ||
|
|
69160750f2 | ||
|
|
9466c5db25 | ||
|
|
8d5417a255 | ||
|
|
e9fda1a3e4 | ||
|
|
5ad6edc8d0 | ||
|
|
74b1c29a48 | ||
|
|
149b6423f8 | ||
|
|
b2da85971d | ||
|
|
0d1d8b6944 | ||
|
|
daa4fd9955 | ||
|
|
670c7f1822 | ||
|
|
a43626cfde | ||
|
|
9b2fa72274 | ||
|
|
1599237883 | ||
|
|
39b64ddc92 | ||
|
|
c768b83542 | ||
|
|
7f4e4557a7 | ||
|
|
3b40e0e588 | ||
|
|
0eb46541e3 | ||
|
|
44365811cc | ||
|
|
69a03ba00b | ||
|
|
bd0b138f7c | ||
|
|
18ac05258b | ||
|
|
6bdf9e46ab | ||
|
|
b26516e33c | ||
|
|
1465c23e12 | ||
|
|
748e8da728 | ||
|
|
2706082c49 | ||
|
|
b0e58d3387 | ||
|
|
ad547b56f5 | ||
|
|
92439acee5 | ||
|
|
3c2d81d3c0 | ||
|
|
0a96b4cf72 | ||
|
|
635af865bf | ||
|
|
7d651d559a | ||
|
|
5e8cd72413 | ||
|
|
34c0535844 | ||
|
|
1e40151e2c | ||
|
|
608a5899dc | ||
|
|
1036512a1c | ||
|
|
407a92a827 | ||
|
|
884fa444a1 | ||
|
|
1e040672c0 | ||
|
|
5fa0e50440 | ||
|
|
7c41aad194 | ||
|
|
88c534875a | ||
|
|
5302211c2d | ||
|
|
414ba28cef | ||
|
|
d24dfa1031 | ||
|
|
f241cc832b | ||
|
|
e663eb1b7a | ||
|
|
06d6dbff5d | ||
|
|
66e0a5440b | ||
|
|
7f362c8e8a | ||
|
|
cde237daea | ||
|
|
b97a7ef4cb | ||
|
|
eb0c015e7c | ||
|
|
b768e44ba7 | ||
|
|
385c6db4ce | ||
|
|
aef6c6d518 | ||
|
|
d57cbdfb95 | ||
|
|
7240d910d3 | ||
|
|
6931e91bf0 | ||
|
|
501bd64a89 | ||
|
|
2b11c8d9a4 | ||
|
|
770af38c14 | ||
|
|
7b787c81f3 | ||
|
|
bd01461b5f | ||
|
|
1afd678100 | ||
|
|
677d26a581 | ||
|
|
f673facdbe | ||
|
|
9fc991da33 | ||
|
|
3d0f41e323 | ||
|
|
7e1ee70b7c | ||
|
|
131b458236 | ||
|
|
74ffa1e413 | ||
|
|
42bbc4b6e2 | ||
|
|
7c62881a95 | ||
|
|
c66cb00c0f | ||
|
|
c066b5cf1c | ||
|
|
3221ca1704 | ||
|
|
c279c088c8 | ||
|
|
086d45f27c | ||
|
|
637d8b2a2d | ||
|
|
d8cbf1cefc | ||
|
|
edbb84d23b | ||
|
|
756fd66745 | ||
|
|
99f296a2e7 | ||
|
|
d2c35fd39d | ||
|
|
cbfb9e482f | ||
|
|
1fb4a32c8d | ||
|
|
f42a28f718 | ||
|
|
160ca476a1 | ||
|
|
17f67df257 | ||
|
|
10f0adc9f9 | ||
|
|
a67de7ebda | ||
|
|
08af091a1c | ||
|
|
22b327f077 | ||
|
|
497ac70c38 | ||
|
|
a5348f4bdc | ||
|
|
d7d1754e69 | ||
|
|
720f6fca94 | ||
|
|
a634b96f6d | ||
|
|
9821bd9707 | ||
|
|
0f781136e7 | ||
|
|
41a2e9af19 | ||
|
|
bf241f9e86 | ||
|
|
a97813e11f | ||
|
|
1b0168d7b3 | ||
|
|
dc57365e95 | ||
|
|
174145929f | ||
|
|
75300d30d3 | ||
|
|
2946ba04d5 | ||
|
|
3857603dbb | ||
|
|
389537cf0e | ||
|
|
134d11f1a3 | ||
|
|
404a931219 | ||
|
|
e288e9266b | ||
|
|
53fcae031e | ||
|
|
d66abc0fc0 | ||
|
|
6a202f5acb | ||
|
|
4e83f23955 | ||
|
|
5d91ed01b7 | ||
|
|
ce138e1cec | ||
|
|
0e598c96c9 | ||
|
|
dad9b18d49 | ||
|
|
a638e2e207 | ||
|
|
a0acbd77ea | ||
|
|
a26084c433 | ||
|
|
798da80459 | ||
|
|
5513f6a468 | ||
|
|
70fb253739 | ||
|
|
4b406b6d5f | ||
|
|
1a3614616d | ||
|
|
c53b0c99de | ||
|
|
5a5e83c26c | ||
|
|
5e0648fe98 | ||
|
|
49819dad16 | ||
|
|
594587541c | ||
|
|
6df6011641 | ||
|
|
e77b528ef5 | ||
|
|
6153474c00 | ||
|
|
654c3781c4 | ||
|
|
d32a7b250a | ||
|
|
53abb99a81 | ||
|
|
e7cde1180b | ||
|
|
318c782ea7 | ||
|
|
0da54f517a | ||
|
|
1284d43ad7 | ||
|
|
71b2b67a12 | ||
|
|
84a4e37f1b | ||
|
|
6f16a535f8 | ||
|
|
6cbb8876d6 | ||
|
|
ae81567fbe | ||
|
|
b7354aacaa | ||
|
|
1dc3626ff7 | ||
|
|
a788e6aa67 | ||
|
|
0f06393149 | ||
|
|
141e52685c | ||
|
|
10b7c4e46e | ||
|
|
41c58002f1 | ||
|
|
d5f01f2db1 | ||
|
|
c8aae00847 | ||
|
|
1bbd2c183b | ||
|
|
b55a5b0826 | ||
|
|
eeca0ce96b | ||
|
|
2cb89823f3 | ||
|
|
297be24c0d | ||
|
|
e59f487bf0 | ||
|
|
e1d15946f7 | ||
|
|
5c333d88c0 | ||
|
|
07ee2eea21 | ||
|
|
af82d75e86 | ||
|
|
25179352b4 | ||
|
|
06c57826ae | ||
|
|
044e3b1b56 | ||
|
|
96f9ee2a41 | ||
|
|
0f3d426591 | ||
|
|
a014ae1001 | ||
|
|
137f559520 | ||
|
|
3d6d51d2c6 | ||
|
|
3786ad6d0c | ||
|
|
b16769f5a0 | ||
|
|
4262ea14d6 | ||
|
|
816da7120e | ||
|
|
38ed354cdb | ||
|
|
56ac94b591 | ||
|
|
b78352ec9d | ||
|
|
1feae7abe1 | ||
|
|
a2cb435aa1 | ||
|
|
c3f61ba3a2 | ||
|
|
8850f9e9aa | ||
|
|
2363d2fa87 | ||
|
|
d1d2f215ad | ||
|
|
eb58f46ce7 | ||
|
|
1a2345b47f | ||
|
|
c1d145e9d7 | ||
|
|
3a308324f6 | ||
|
|
bc2561f538 | ||
|
|
ed89c2611e | ||
|
|
2952cf81e6 | ||
|
|
589eff6187 | ||
|
|
7c5b423659 | ||
|
|
b1bd2c931c | ||
|
|
ce614bd830 | ||
|
|
1731f2f850 | ||
|
|
bfe7b1fb34 | ||
|
|
0a14ec0978 | ||
|
|
bb47469d1a | ||
|
|
e7e57d40c4 | ||
|
|
7c0b72e2c5 | ||
|
|
8fc8e8ed19 | ||
|
|
96947280df | ||
|
|
0af15f9b1d | ||
|
|
dab4908317 | ||
|
|
aca1d54514 | ||
|
|
817772e97b | ||
|
|
af8ec41014 | ||
|
|
51794c384a | ||
|
|
acfc11ae42 | ||
|
|
9d39942189 | ||
|
|
829522ba55 | ||
|
|
d538e1cd90 | ||
|
|
77609dcdc7 | ||
|
|
22b6b49a43 | ||
|
|
f3c507ec57 | ||
|
|
e0fa6a1792 | ||
|
|
9a435b4f4a | ||
|
|
80b1ce8cad | ||
|
|
2ac0e4c061 | ||
|
|
cdfec6971f | ||
|
|
7365600cf8 | ||
|
|
754bab442a | ||
|
|
4593ff50f0 | ||
|
|
6ecce365e9 | ||
|
|
1d17119710 | ||
|
|
9233d7a4d7 | ||
|
|
060e137599 | ||
|
|
7bfa8f6296 | ||
|
|
84c7e5db1f | ||
|
|
c797525d2a | ||
|
|
0714a7150b | ||
|
|
9c36901728 | ||
|
|
da2d877019 | ||
|
|
ffc7ba0829 | ||
|
|
09161f6fdd | ||
|
|
8dc3c53240 | ||
|
|
dd91d7e2e2 | ||
|
|
599444e27e | ||
|
|
1a0d2b6fc1 | ||
|
|
8d0093b43f | ||
|
|
d07897fc36 | ||
|
|
bfe8e5a958 | ||
|
|
b9f8a859e7 | ||
|
|
0d3ae7fde5 | ||
|
|
1bfccf88da | ||
|
|
565c6f3eb2 | ||
|
|
77ae842496 | ||
|
|
250b977616 | ||
|
|
a8a5c6cff1 | ||
|
|
555f3d86fb | ||
|
|
dc5eb40ca3 | ||
|
|
20571a938b | ||
|
|
e9f2e1861e | ||
|
|
905b2eedcd | ||
|
|
00a4503c4f | ||
|
|
11aff52fb1 | ||
|
|
ec127a780e | ||
|
|
b958109d06 | ||
|
|
d0f4e7c590 | ||
|
|
20d191bc8e | ||
|
|
24db5b598b | ||
|
|
141e519009 | ||
|
|
c5cec10788 | ||
|
|
950ab377c6 | ||
|
|
0c898742f6 | ||
|
|
ca0d822619 | ||
|
|
e2a80875c9 | ||
|
|
061ebe1dca | ||
|
|
7a8c8a4fb3 | ||
|
|
3ff10c6cdd | ||
|
|
9ae2ac39c9 | ||
|
|
2c8fb9d3fc | ||
|
|
dc7358b4df | ||
|
|
44a518b331 | ||
|
|
68f3fc6d5d | ||
|
|
72c4630aab | ||
|
|
db0abe89cf | ||
|
|
2b44a4f0d9 | ||
|
|
72f4098156 | ||
|
|
f0f7c3ff01 | ||
|
|
5536281238 | ||
|
|
8de6233326 | ||
|
|
f312170f21 | ||
|
|
6d1bda6ff2 | ||
|
|
f45c19b428 | ||
|
|
e2ee629022 | ||
|
|
64731b71aa | ||
|
|
23b5baa5ec | ||
|
|
f58e893e63 | ||
|
|
a856518265 | ||
|
|
45806017e5 | ||
|
|
058e63a3d6 | ||
|
|
e8e6c4716f | ||
|
|
3ce8c73315 | ||
|
|
88edd13642 | ||
|
|
c70e614a5b | ||
|
|
aa8faae576 | ||
|
|
2f8901d6d0 | ||
|
|
9ff8c5ac2d | ||
|
|
48491e5262 | ||
|
|
9f5cc7262b | ||
|
|
957beb02bc | ||
|
|
017a1f2b94 | ||
|
|
f8f1b2212a | ||
|
|
dab6a161bd | ||
|
|
8e47d29bf9 | ||
|
|
e337129108 | ||
|
|
d3eb2fe13c | ||
|
|
d2239a5770 | ||
|
|
a244b06882 | ||
|
|
0a55f4bf36 | ||
|
|
e7a411a66d | ||
|
|
783671261d | ||
|
|
01d951c3fc | ||
|
|
6cf3402f1c | ||
|
|
e3c6909ad5 | ||
|
|
255810db64 | ||
|
|
f094652481 | ||
|
|
3eb07cac44 | ||
|
|
58034bf237 | ||
|
|
7ba7ea4e16 | ||
|
|
4877e84031 | ||
|
|
9c47f395c8 | ||
|
|
3f98b4835c | ||
|
|
a86145b6bb | ||
|
|
c4d3a74f32 | ||
|
|
c74865fbe2 | ||
|
|
93a908469c | ||
|
|
903fe29863 | ||
|
|
84da113355 | ||
|
|
75df4c0b52 | ||
|
|
ad5a746cdd | ||
|
|
2bd3ce5463 | ||
|
|
2b752ec245 | ||
|
|
909ee719aa | ||
|
|
7dd5e957da | ||
|
|
d67e0eea47 | ||
|
|
10bfeba2d9 | ||
|
|
4285f8ba05 | ||
|
|
d8be3ef7a8 | ||
|
|
c924768879 | ||
|
|
c1e76e8976 | ||
|
|
60a9f8e492 | ||
|
|
604133d189 | ||
|
|
d3781bb787 | ||
|
|
87e8da5230 | ||
|
|
727c696d9f | ||
|
|
cf2b7f4c1b | ||
|
|
cd4383b6f3 | ||
|
|
0d9859370a | ||
|
|
c292ae2e0e | ||
|
|
3113847806 | ||
|
|
d275455674 | ||
|
|
a4d10742d3 | ||
|
|
777fba495a | ||
|
|
2e66341f69 | ||
|
|
2e44585ce9 | ||
|
|
e2f0e14b04 | ||
|
|
e801dc96ca | ||
|
|
56a3ac1814 | ||
|
|
6c62f720c8 | ||
|
|
a57efd0a88 | ||
|
|
7e2d6e2254 | ||
|
|
4603e1a6ad | ||
|
|
550d2918b8 | ||
|
|
eb5ad2c03a | ||
|
|
769fe4ebf6 | ||
|
|
8130fdc474 | ||
|
|
41bba59868 | ||
|
|
115f06c32a | ||
|
|
1e1e17cb35 | ||
|
|
831e8d768b | ||
|
|
b4b878b2d0 | ||
|
|
2377f35426 | ||
|
|
c7f706baeb | ||
|
|
c3402b85ab | ||
|
|
a68b986616 | ||
|
|
a2dc17055b | ||
|
|
c9c85c7d83 | ||
|
|
d615e615d9 | ||
|
|
a84639f63e | ||
|
|
d9ab758af5 | ||
|
|
5cbeb22564 | ||
|
|
77e0fa4efe | ||
|
|
69efb78319 | ||
|
|
32a9392a11 | ||
|
|
af741abbf5 | ||
|
|
36723d38b9 | ||
|
|
3ebce4e190 | ||
|
|
c934e6c247 | ||
|
|
57c8ab269b | ||
|
|
e7dc0d31f4 | ||
|
|
1819dc88ff | ||
|
|
e1fade23ec | ||
|
|
27e1391e6d | ||
|
|
da32bdd79c | ||
|
|
b863ca9ae9 | ||
|
|
c3b01fbd53 | ||
|
|
ad1e04c826 | ||
|
|
c8dc66b6c1 | ||
|
|
d234b78cc0 | ||
|
|
1ae084b5f8 | ||
|
|
ddeb5ac535 | ||
|
|
6ff5c4c278 | ||
|
|
087f0b4a69 | ||
|
|
a7bdc55244 | ||
|
|
647573d269 | ||
|
|
788a7ec502 | ||
|
|
3aef45c45b | ||
|
|
1f5c66db79 | ||
|
|
d42d6c5246 | ||
|
|
d1aba29b57 | ||
|
|
0c35ca2e39 | ||
|
|
6e77bee098 | ||
|
|
1ee21c17fc | ||
|
|
aea58113cb | ||
|
|
36c798964e | ||
|
|
6c609028b3 | ||
|
|
a3a99d3875 | ||
|
|
970732ea11 | ||
|
|
2eb478787f | ||
|
|
b49ec19167 | ||
|
|
adcba851f0 | ||
|
|
cc94cff735 | ||
|
|
2960b384af | ||
|
|
2c2b3641f1 | ||
|
|
746e3a9f42 | ||
|
|
6a4225bf04 | ||
|
|
c86073830f | ||
|
|
8672186a4e | ||
|
|
5bee3288ac | ||
|
|
eeb74ecf4d | ||
|
|
36704e33bd | ||
|
|
8a2e21cfc4 | ||
|
|
3deda3c6df | ||
|
|
e288e9c57e | ||
|
|
26dba92ce9 | ||
|
|
1cbd2bd199 | ||
|
|
a41fb49e25 | ||
|
|
dfce31e2a2 | ||
|
|
1d9b19189a | ||
|
|
d3c36bd7cf | ||
|
|
950fbc9d8f | ||
|
|
167e0ab301 | ||
|
|
2fed93462d | ||
|
|
2d4c0017b8 | ||
|
|
3a22035dad | ||
|
|
010c6c36f1 | ||
|
|
a3b76aa825 | ||
|
|
9d5f565119 | ||
|
|
f3baff8dce | ||
|
|
a26c937650 | ||
|
|
0929cb3902 | ||
|
|
403e942f37 | ||
|
|
d9e7ded5af | ||
|
|
4e10e4e02e | ||
|
|
7557542bc2 | ||
|
|
219f36f499 | ||
|
|
b5bf7d4b87 | ||
|
|
d6b3da5e72 | ||
|
|
6a59926592 | ||
|
|
b5b407343a | ||
|
|
97fb0b82bb | ||
|
|
ca253ae4cf | ||
|
|
4b307914fc | ||
|
|
2cdf4b14e1 | ||
|
|
1a374ceab2 | ||
|
|
2a7b1162af | ||
|
|
16e9700224 | ||
|
|
5a8b4459c8 | ||
|
|
3825c48405 | ||
|
|
f07e72fad7 | ||
|
|
3599e43284 | ||
|
|
88fb7c0199 | ||
|
|
2649d1510e | ||
|
|
5d7cf08260 | ||
|
|
88664e4a99 | ||
|
|
9d1fb9f4fa | ||
|
|
6a17e62523 | ||
|
|
1ce7047bf5 | ||
|
|
ef759d874f | ||
|
|
6f5bb3e896 | ||
|
|
96c6f9dc96 | ||
|
|
f50b863868 | ||
|
|
dd3652ecdc | ||
|
|
a9efbf04f4 | ||
|
|
3a76ac5620 | ||
|
|
747ea91c3a | ||
|
|
ecdc2d57f2 | ||
|
|
f4afcfc923 | ||
|
|
9cce0ce8d9 | ||
|
|
57aadf8af9 | ||
|
|
1babe9fc67 | ||
|
|
dd1a4188a0 | ||
|
|
ed42d068d4 | ||
|
|
784444c7a9 | ||
|
|
05fb67af90 | ||
|
|
22d1d04059 | ||
|
|
36ac6eb912 | ||
|
|
47548aa171 | ||
|
|
b26b781992 | ||
|
|
c9c3366521 | ||
|
|
2c2a3a65b2 | ||
|
|
8cefb2cf65 | ||
|
|
80c8837f49 | ||
|
|
40c6dfa3ae | ||
|
|
cc76c46244 | ||
|
|
b38da34db2 | ||
|
|
4a900cc65c | ||
|
|
a3fd2eb0fe | ||
|
|
6ac530aa1a | ||
|
|
04fe72fee0 | ||
|
|
a833afa935 | ||
|
|
7c9454edd2 | ||
|
|
1ecb4a43ae | ||
|
|
ae9d12aeaa | ||
|
|
e617ce7e4f | ||
|
|
b9894b40af | ||
|
|
9ff4d53d0b | ||
|
|
1e262c2c0e | ||
|
|
b08fc5dfda | ||
|
|
761c88f10e | ||
|
|
07b0e5b7fe | ||
|
|
f7e207a824 | ||
|
|
f61e2989a2 | ||
|
|
bdf4b792a8 | ||
|
|
d3af1268a7 | ||
|
|
01be97309e | ||
|
|
3cf6fb2405 | ||
|
|
2a67a49f31 | ||
|
|
fb2e5e5555 | ||
|
|
b87c01b1c0 | ||
|
|
0f1133fe69 | ||
|
|
f348a082da | ||
|
|
befee896b3 | ||
|
|
e7fa5891ea | ||
|
|
3927445973 | ||
|
|
7d1d761148 | ||
|
|
7790420cae | ||
|
|
4016a80f66 | ||
|
|
feb8cc2d4a | ||
|
|
5eed373feb | ||
|
|
895cdce9bc | ||
|
|
3411518548 | ||
|
|
13b4b11657 | ||
|
|
fa05bccd58 | ||
|
|
c307e8a04f | ||
|
|
2aca375cd9 | ||
|
|
46ae4c0d7c | ||
|
|
6f445a1c05 | ||
|
|
80cf782bc6 | ||
|
|
1622f578c9 | ||
|
|
47814f9da1 | ||
|
|
0d95bf68cc | ||
|
|
d61f506da2 | ||
|
|
7f3e170509 | ||
|
|
bcffbdd3a1 | ||
|
|
e14c593003 | ||
|
|
bcde913a96 | ||
|
|
33b45132a4 | ||
|
|
ef4c6ed83c | ||
|
|
cd3eb9125c | ||
|
|
f6c5aed7ef | ||
|
|
dd293d1fbd | ||
|
|
4989a60af3 | ||
|
|
7c809a94af | ||
|
|
5eca093a89 | ||
|
|
6d46e31ad8 | ||
|
|
605b9e63c9 | ||
|
|
0d1907c1df | ||
|
|
2b67ef451a | ||
|
|
bfe2d28c50 | ||
|
|
de24063c4b | ||
|
|
7c79f05cd4 | ||
|
|
1248a55d32 | ||
|
|
ac9708051a | ||
|
|
af385d7c10 | ||
|
|
92f0d31ed7 | ||
|
|
0376cae739 | ||
|
|
c92425f98d | ||
|
|
52198837df | ||
|
|
eab09084a3 | ||
|
|
45d39422bc | ||
|
|
06d022b9c0 | ||
|
|
dcd70cbfba | ||
|
|
07c880f7ff | ||
|
|
2e6206bbeb | ||
|
|
4540a6436f | ||
|
|
dc001a01e5 | ||
|
|
a00cc12436 | ||
|
|
947cd742bf | ||
|
|
ee42e8cf24 | ||
|
|
b01ca8ee23 | ||
|
|
fd4599fd7a | ||
|
|
fbeec32c2b | ||
|
|
de99c8015a | ||
|
|
49297f12a5 | ||
|
|
8fd107c10f | ||
|
|
13b1f56f88 | ||
|
|
20e67945ea | ||
|
|
60aeb79a75 | ||
|
|
f1ed830b9a | ||
|
|
255d931e0c | ||
|
|
53ed233f38 | ||
|
|
0ed493e748 | ||
|
|
1f956ad1af | ||
|
|
ebba1e04d0 | ||
|
|
33baaccb20 | ||
|
|
63ac27e9b9 | ||
|
|
897e9c5388 | ||
|
|
95aee36fab | ||
|
|
8304bfe237 | ||
|
|
3b41e43264 | ||
|
|
da2b91558e | ||
|
|
6d63f6305e | ||
|
|
23bd9dfb09 | ||
|
|
ba2b9f63ad | ||
|
|
0d7e126a01 | ||
|
|
2385abc282 | ||
|
|
baeb325611 | ||
|
|
2c6eacee30 | ||
|
|
412389f71f | ||
|
|
b385949dd4 | ||
|
|
80f824ce6b | ||
|
|
cd722567ed | ||
|
|
6f8c13ba39 | ||
|
|
cb6adb0259 | ||
|
|
8b2a9cd74d | ||
|
|
b81dd3e7ad | ||
|
|
2477bb9705 | ||
|
|
99f514dc5e | ||
|
|
838dcc496f | ||
|
|
800c60d77a | ||
|
|
a40bcee14f | ||
|
|
142874d863 | ||
|
|
c6cb2f52f0 | ||
|
|
d01e038210 | ||
|
|
a4ad409ae0 | ||
|
|
a0776c33f0 | ||
|
|
ffa1e9e9ae | ||
|
|
030daffba6 | ||
|
|
8f5418dbda | ||
|
|
5930f430f4 | ||
|
|
0f5dceda4b | ||
|
|
13e2a0291c | ||
|
|
8edaddd70c | ||
|
|
eab91e68c5 | ||
|
|
a5a525f6a1 | ||
|
|
bc54db2af1 | ||
|
|
6e7b76c097 | ||
|
|
a1d522ab14 | ||
|
|
809ae9aac3 | ||
|
|
832d7c500d | ||
|
|
f6df23f2a7 | ||
|
|
1abac9aca6 | ||
|
|
40d9f49d68 | ||
|
|
15f1aeed6e | ||
|
|
c193195a05 | ||
|
|
1f6abcaf6c | ||
|
|
89d897a34d | ||
|
|
3fb264b569 | ||
|
|
d68c2ce28b | ||
|
|
64b5bedc8c | ||
|
|
38086a83cb | ||
|
|
deb3299263 | ||
|
|
2b0e75748b | ||
|
|
ca96ea331e | ||
|
|
66471ba6e2 | ||
|
|
425c7a12d0 | ||
|
|
1776758971 | ||
|
|
5286b21126 | ||
|
|
5900f39638 | ||
|
|
b3a8d5b04e | ||
|
|
a3f7d44593 | ||
|
|
7bd12c71c8 | ||
|
|
9b5813eeda | ||
|
|
fe3a78d262 | ||
|
|
6c2573fc38 | ||
|
|
ad48761032 | ||
|
|
0a42a47ea8 | ||
|
|
d70a596887 | ||
|
|
3331ed9bb1 | ||
|
|
b3be4ea66e | ||
|
|
b329c4b5db | ||
|
|
a2155e0741 | ||
|
|
40eefb1df6 | ||
|
|
146f6e6b2f | ||
|
|
52a27697be | ||
|
|
270934cbb6 | ||
|
|
731551d670 | ||
|
|
2fedd7144a | ||
|
|
5bd75695f4 | ||
|
|
cd62b8cd80 | ||
|
|
dc445d7af6 | ||
|
|
e7d8948fa6 | ||
|
|
e922edfc21 | ||
|
|
5b1c6b558a | ||
|
|
21e8a99eff | ||
|
|
49fe87e0d1 | ||
|
|
61c843a3c7 | ||
|
|
ca3c7571e5 | ||
|
|
5075153c15 | ||
|
|
c7dec60428 | ||
|
|
41fe7bc71a | ||
|
|
2c00f8fe2f | ||
|
|
68653297d1 | ||
|
|
729d6e5d5c | ||
|
|
c6677e0b6f | ||
|
|
0c43f05047 | ||
|
|
3c8cf7a905 | ||
|
|
51b56b20ec | ||
|
|
5c0231f508 | ||
|
|
a35bf7ee4c | ||
|
|
bc234f9f8d | ||
|
|
08ec2541c7 | ||
|
|
e05131122b | ||
|
|
e4749eb6b5 | ||
|
|
84311122ac | ||
|
|
c93012faa1 | ||
|
|
aa65107523 | ||
|
|
07e2b7d913 | ||
|
|
9b5fadd275 | ||
|
|
7e8af0fc9d | ||
|
|
f10d0d07d9 | ||
|
|
312759e369 | ||
|
|
1d9439752c | ||
|
|
9f5a9a071a | ||
|
|
26bc8c5b2a | ||
|
|
eea2d49078 | ||
|
|
f9d3deaafe | ||
|
|
e4364e747f | ||
|
|
6aa6407af1 | ||
|
|
9247206c0e | ||
|
|
c7c1e091c9 | ||
|
|
20c616503a | ||
|
|
104b3519d7 | ||
|
|
d8fcfead97 | ||
|
|
d0b947bf52 | ||
|
|
5f0fea60a6 | ||
|
|
3e54597db4 | ||
|
|
eb1c9b9ab2 | ||
|
|
4d66e7bdc0 | ||
|
|
f8660485d7 | ||
|
|
64766f8724 | ||
|
|
f64bce6ef1 | ||
|
|
0160aa1a89 | ||
|
|
3418d6db8e | ||
|
|
3aea0fd810 | ||
|
|
4ca98dcca2 | ||
|
|
55b0bed5df | ||
|
|
d86a408944 | ||
|
|
69a73a18fb | ||
|
|
98bd162ad4 | ||
|
|
ba95dbc36b | ||
|
|
6278839534 | ||
|
|
849a252b20 | ||
|
|
ca56c5ecc0 | ||
|
|
d10666731c | ||
|
|
6dbb54d221 | ||
|
|
cc260dd231 | ||
|
|
9565334c0e | ||
|
|
2fa38e6ceb | ||
|
|
056d1dbeef | ||
|
|
e8bbba06b7 | ||
|
|
58c7a4f15e | ||
|
|
c8be581bc8 | ||
|
|
c6e244d811 | ||
|
|
044bf85fe9 | ||
|
|
1059e25ca2 | ||
|
|
c70f4064b4 | ||
|
|
757899a7d1 | ||
|
|
a901e34362 | ||
|
|
bab10cc2b5 | ||
|
|
d26dbe73d5 | ||
|
|
214093e6c4 | ||
|
|
ebda2d4d25 | ||
|
|
7e03920bbb | ||
|
|
d033804190 | ||
|
|
56733b953e | ||
|
|
c073da20ce | ||
|
|
d8bcd6a32e | ||
|
|
f35a602070 | ||
|
|
14841ad1ed | ||
|
|
5f3c0daf3d | ||
|
|
6befda831d | ||
|
|
6595ca8f29 | ||
|
|
91e261da38 | ||
|
|
6ebce42142 | ||
|
|
b6db90a316 | ||
|
|
7706b876f6 | ||
|
|
9b74c07767 | ||
|
|
533af01dab | ||
|
|
de31faa470 | ||
|
|
3881f21df1 | ||
|
|
c9a03c7613 | ||
|
|
0c2fb34c82 | ||
|
|
eb30249b11 | ||
|
|
31929c0acd | ||
|
|
3569797377 | ||
|
|
7b813d4f5d | ||
|
|
edeae18f5e | ||
|
|
91bae2e064 | ||
|
|
f9e904af50 | ||
|
|
8375d00d8c | ||
|
|
16bd7ea455 | ||
|
|
c54287fb0d | ||
|
|
e3771e3ad6 | ||
|
|
4935829abe | ||
|
|
778333c667 | ||
|
|
189f5d41fb | ||
|
|
c07f64a621 | ||
|
|
22117f21e3 | ||
|
|
1d03cd6a6b | ||
|
|
ac6a29ee83 | ||
|
|
57f0006c9b | ||
|
|
e40e0892c1 | ||
|
|
1fcdd7ad9a | ||
|
|
9b7bd58c14 | ||
|
|
a062eea204 | ||
|
|
645bdea23c | ||
|
|
35bbb48916 | ||
|
|
b289b660c7 | ||
|
|
75c104ce06 | ||
|
|
74bd40d34d | ||
|
|
4213862b0e | ||
|
|
4d8bc22228 | ||
|
|
7ee938290b | ||
|
|
478d42105f | ||
|
|
5998ba545b | ||
|
|
8a8417f6e1 | ||
|
|
26941793ff | ||
|
|
70050c3798 | ||
|
|
50a0a97b49 | ||
|
|
5fb2e892c8 | ||
|
|
3770808b58 | ||
|
|
5e63dd292f | ||
|
|
98e3d6f663 | ||
|
|
d4070d4bfb | ||
|
|
4d4c0941be | ||
|
|
9b629cc81f | ||
|
|
f374ef154e | ||
|
|
e3fd954318 | ||
|
|
b7815b5684 | ||
|
|
7f0ae22e43 | ||
|
|
35a4da28ac | ||
|
|
60142c967c | ||
|
|
17c0187252 | ||
|
|
e42f7d9fc3 | ||
|
|
906aa1be4b | ||
|
|
f01527142e |
20
.github/workflows/awaiting-mathlib.yml
vendored
Normal file
20
.github/workflows/awaiting-mathlib.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Check awaiting-mathlib label
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
check-awaiting-mathlib:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check awaiting-mathlib label
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { labels } = context.payload.pull_request;
|
||||
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
|
||||
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
|
||||
}
|
||||
251
.github/workflows/build-template.yml
vendored
Normal file
251
.github/workflows/build-template.yml
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
# instantiated by ci.yml
|
||||
name: build-template
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
check-level:
|
||||
type: string
|
||||
required: true
|
||||
config:
|
||||
type: string
|
||||
required: true
|
||||
nightly:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_MAJOR:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_MINOR:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_VERSION_PATCH:
|
||||
type: string
|
||||
required: true
|
||||
LEAN_SPECIAL_VERSION_DESC:
|
||||
type: string
|
||||
required: true
|
||||
RELEASE_TAG:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{fromJson(inputs.config)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
# must be inside workspace
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPRESS: true
|
||||
# current cache limit
|
||||
CCACHE_MAXSIZE: 600M
|
||||
# squelch error message about missing nixpkgs channel
|
||||
NIX_BUILD_SHELL: bash
|
||||
LSAN_OPTIONS: max_leaks=10
|
||||
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
|
||||
CXX: c++
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
if: runner.os == 'Linux' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: clang64
|
||||
# `:` means do not prefix with msystem
|
||||
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp libuv
|
||||
if: runner.os == 'macOS'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# the default is to use a virtual merge commit between the PR and master: just use the PR
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Open Nix shell once
|
||||
run: true
|
||||
if: runner.os == 'Linux'
|
||||
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
|
||||
# master (as the workflow files themselves are always taken from the merge)
|
||||
# (needs to be after "Install *" to use the right shell)
|
||||
- name: CI Merge Checkout
|
||||
run: |
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 3.1.44
|
||||
actions-cache-folder: emsdk
|
||||
if: matrix.wasm
|
||||
- name: Install 32bit c libs
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 pkgconf:i386
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
id: restore-cache
|
||||
if: matrix.name != 'Linux Lake'
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
# NOTE: must be in sync with `save` below
|
||||
path: |
|
||||
.ccache
|
||||
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
|
||||
build/stage1/**/*.olean
|
||||
build/stage1/**/*.ilean
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*' || '' }}
|
||||
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-build-v3
|
||||
# open nix-shell once for initial setup
|
||||
- name: Setup
|
||||
run: |
|
||||
ccache --zero-stats
|
||||
if: runner.os == 'Linux'
|
||||
- name: Set up NPROC
|
||||
run: |
|
||||
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
[ -d build ] || mkdir build
|
||||
cd build
|
||||
# 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 '${{ inputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.nightly }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.release }}' && -n '${{ inputs.RELEASE_TAG }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ inputs.LEAN_VERSION_MAJOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ inputs.LEAN_VERSION_MINOR }})
|
||||
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ inputs.LEAN_VERSION_PATCH }})
|
||||
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
time make -j$NPROC
|
||||
- name: Install
|
||||
run: |
|
||||
make -C build install
|
||||
- name: Check Binaries
|
||||
run: ${{ matrix.binary-check }} lean-*/bin/* || true
|
||||
- name: Count binary symbols
|
||||
run: |
|
||||
for f in lean-*/bin/*; do
|
||||
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
|
||||
done
|
||||
if: matrix.name == 'Windows'
|
||||
- name: List Install Tree
|
||||
run: |
|
||||
# omit contents of Init/, ...
|
||||
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
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 '${{ inputs.nightly }}' || -n '${{ inputs.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
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.release
|
||||
with:
|
||||
name: build-${{ matrix.name }}
|
||||
path: pack/*
|
||||
- name: Lean stats
|
||||
run: |
|
||||
build/stage1/bin/lean --stats src/Lean.lean
|
||||
if: ${{ !matrix.cross }}
|
||||
- name: Test
|
||||
id: test
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
|
||||
if: (matrix.wasm || !matrix.cross) && inputs.check-level >= 1
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: build/stage1/test-results.xml
|
||||
# prefix `if` above with `always` so it's run even if tests failed
|
||||
if: always() && steps.test.conclusion != 'skipped'
|
||||
- name: Check Test Binary
|
||||
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
|
||||
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
|
||||
- name: Build Stage 2
|
||||
run: |
|
||||
make -C build -j$NPROC stage2
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
make -C build -j$NPROC check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
# Necessary for some timing metrics but does not work on Namespace runners
|
||||
# and we just want to test that the benchmarks run at all here
|
||||
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
|
||||
cd tests/bench
|
||||
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check rebootstrap
|
||||
run: |
|
||||
# clean rebuild in case of Makefile changes
|
||||
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
|
||||
if: matrix.name == 'Linux' && inputs.check-level >= 1
|
||||
- name: CCache stats
|
||||
if: always()
|
||||
run: ccache -s
|
||||
- name: Show stacktrace for coredumps
|
||||
if: failure() && runner.os == 'Linux'
|
||||
run: |
|
||||
for c in $(find . -name core); do
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
echo bt | $GDB/bin/gdb -q $progbin $c || true
|
||||
done
|
||||
- name: Save Cache
|
||||
if: always() && steps.restore-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
# NOTE: must be in sync with `restore` above
|
||||
path: |
|
||||
.ccache
|
||||
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
|
||||
build/stage1/**/*.olean
|
||||
build/stage1/**/*.ilean
|
||||
build/stage1/**/*.c
|
||||
build/stage1/**/*.c.o*' || '' }}
|
||||
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||
4
.github/workflows/check-stage0.yml
vendored
4
.github/workflows/check-stage0.yml
vendored
@@ -20,9 +20,7 @@ jobs:
|
||||
|
||||
- name: Identify stage0 changes
|
||||
run: |
|
||||
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0 |
|
||||
grep -v -x -F $'stage0/src/stdlib_flags.h\nstage0/src/lean.mk.in' \
|
||||
> "$RUNNER_TEMP/stage0" || true
|
||||
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0/stdlib > "$RUNNER_TEMP/stage0" || true
|
||||
if test -s "$RUNNER_TEMP/stage0"
|
||||
then
|
||||
echo "CHANGES=yes" >> "$GITHUB_ENV"
|
||||
|
||||
259
.github/workflows/ci.yml
vendored
259
.github/workflows/ci.yml
vendored
@@ -36,7 +36,9 @@ jobs:
|
||||
# 2: PRs with `release-ci` label, releases (incl. nightlies)
|
||||
check-level: ${{ steps.set-level.outputs.check-level }}
|
||||
# The build matrix, dynamically generated here
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
# secondary build jobs that should not block the CI success/merge queue
|
||||
matrix-secondary: ${{ steps.set-matrix.outputs.matrix-secondary }}
|
||||
# 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?
|
||||
@@ -135,9 +137,9 @@ jobs:
|
||||
console.log(`level: ${level}`);
|
||||
// use large runners where available (original repo)
|
||||
let large = ${{ github.repository == 'leanprover/lean4' }};
|
||||
const isPr = "${{ github.event_name }}" == "pull_request";
|
||||
let matrix = [
|
||||
{
|
||||
// portable release build: use channel with older glibc (2.27)
|
||||
"name": "Linux LLVM",
|
||||
"os": "ubuntu-latest",
|
||||
"release": false,
|
||||
@@ -152,6 +154,7 @@ jobs:
|
||||
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
|
||||
},
|
||||
{
|
||||
// portable release build: use channel with older glibc (2.26)
|
||||
"name": "Linux release",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
|
||||
"release": true,
|
||||
@@ -163,6 +166,19 @@ jobs:
|
||||
// foreign code may be linked against more recent glibc
|
||||
"CTEST_OPTIONS": "-E 'foreign'"
|
||||
},
|
||||
// deactivated due to bugs
|
||||
/*
|
||||
{
|
||||
"name": "Linux Lake",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
|
||||
// just a secondary PR build job for now
|
||||
"check-level": isPr ? 0 : 3,
|
||||
"secondary": true,
|
||||
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
|
||||
// TODO: why does this fail?
|
||||
"CTEST_OPTIONS": "-E 'scopedMacros'"
|
||||
},
|
||||
*/
|
||||
{
|
||||
"name": "Linux",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
|
||||
@@ -171,12 +187,12 @@ jobs:
|
||||
"check-level": 1,
|
||||
},
|
||||
{
|
||||
"name": "Linux Debug",
|
||||
"name": "Linux Reldebug",
|
||||
"os": "ubuntu-latest",
|
||||
"check-level": 2,
|
||||
"CMAKE_PRESET": "debug",
|
||||
// exclude seriously slow tests
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
|
||||
"CMAKE_PRESET": "reldebug",
|
||||
// exclude seriously slow/stackoverflowing tests
|
||||
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress|3807'"
|
||||
},
|
||||
// TODO: suddenly started failing in CI
|
||||
/*{
|
||||
@@ -204,12 +220,18 @@ jobs:
|
||||
"os": "macos-14",
|
||||
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"release": true,
|
||||
"check-level": 0,
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
|
||||
"binary-check": "otool -L",
|
||||
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
|
||||
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
|
||||
// Special handling for MacOS aarch64, we want:
|
||||
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
|
||||
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
|
||||
// little value in the merge queue
|
||||
// 3. To run it in release (obviously)
|
||||
"check-level": isPr ? 0 : 2,
|
||||
"secondary": isPr,
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
@@ -260,196 +282,41 @@ jobs:
|
||||
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
|
||||
// }
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
|
||||
return matrix.filter((job) => level >= job["check-level"])
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
|
||||
matrix = matrix.filter((job) => level >= job["check-level"]);
|
||||
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
|
||||
core.setOutput('matrix-secondary', matrix.filter((job) => job["secondary"]));
|
||||
|
||||
build:
|
||||
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 develop -c bash -euxo pipefail {0}' }}
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
# must be inside workspace
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPRESS: true
|
||||
# current cache limit
|
||||
CCACHE_MAXSIZE: 200M
|
||||
# squelch error message about missing nixpkgs channel
|
||||
NIX_BUILD_SHELL: bash
|
||||
LSAN_OPTIONS: max_leaks=10
|
||||
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
|
||||
CXX: c++
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
if: runner.os == 'Linux' && !matrix.cmultilib
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: clang64
|
||||
# `:` means do not prefix with msystem
|
||||
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
brew install ccache tree zstd coreutils gmp libuv
|
||||
if: runner.os == 'macOS'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# the default is to use a virtual merge commit between the PR and master: just use the PR
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
|
||||
# master (as the workflow files themselves are always taken from the merge)
|
||||
# (needs to be after "Install *" to use the right shell)
|
||||
- name: CI Merge Checkout
|
||||
run: |
|
||||
git fetch --depth=1 origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD flake.nix flake.lock
|
||||
if: github.event_name == 'pull_request'
|
||||
# (needs to be after "Checkout" so files don't get overridden)
|
||||
- name: Setup emsdk
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 3.1.44
|
||||
actions-cache-folder: emsdk
|
||||
if: matrix.wasm
|
||||
- name: Install 32bit c libs
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 pkgconf:i386
|
||||
if: matrix.cmultilib
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-build-v3
|
||||
save-always: true
|
||||
# open nix-shell once for initial setup
|
||||
- name: Setup
|
||||
run: |
|
||||
ccache --zero-stats
|
||||
if: runner.os == 'Linux'
|
||||
- name: Set up NPROC
|
||||
run: |
|
||||
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
# 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.configure.outputs.nightly }}' ]]; then
|
||||
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.nightly }})
|
||||
fi
|
||||
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.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
|
||||
fi
|
||||
# contortion to support empty OPTIONS with old macOS bash
|
||||
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
|
||||
time make -j$NPROC
|
||||
- name: Install
|
||||
run: |
|
||||
make -C build install
|
||||
- name: Check Binaries
|
||||
run: ${{ matrix.binary-check }} lean-*/bin/* || true
|
||||
- name: Count binary symbols
|
||||
run: |
|
||||
for f in lean-*/bin/*; do
|
||||
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
|
||||
done
|
||||
if: matrix.name == 'Windows'
|
||||
- name: List Install Tree
|
||||
run: |
|
||||
# omit contents of Init/, ...
|
||||
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
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.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
|
||||
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.release
|
||||
with:
|
||||
name: build-${{ matrix.name }}
|
||||
path: pack/*
|
||||
- name: Lean stats
|
||||
run: |
|
||||
build/stage1/bin/lean --stats src/Lean.lean
|
||||
if: ${{ !matrix.cross }}
|
||||
- name: Test
|
||||
id: test
|
||||
run: |
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
|
||||
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.check-level >= 1
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: build/stage1/test-results.xml
|
||||
# prefix `if` above with `always` so it's run even if tests failed
|
||||
if: always() && steps.test.conclusion != 'skipped'
|
||||
- name: Check Test Binary
|
||||
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
|
||||
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
|
||||
- name: Build Stage 2
|
||||
run: |
|
||||
make -C build -j$NPROC stage2
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
make -C build -j$NPROC check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
# Necessary for some timing metrics but does not work on Namespace runners
|
||||
# and we just want to test that the benchmarks run at all here
|
||||
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
|
||||
cd tests/bench
|
||||
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check rebootstrap
|
||||
run: |
|
||||
# clean rebuild in case of Makefile changes
|
||||
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
|
||||
if: matrix.name == 'Linux' && needs.configure.outputs.check-level >= 1
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
needs: [configure]
|
||||
uses: ./.github/workflows/build-template.yml
|
||||
with:
|
||||
config: ${{needs.configure.outputs.matrix}}
|
||||
check-level: ${{ needs.configure.outputs.check-level }}
|
||||
nightly: ${{ needs.configure.outputs.nightly }}
|
||||
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
|
||||
secrets: inherit
|
||||
|
||||
# build jobs that should not be considered by `all-done` below
|
||||
build-secondary:
|
||||
needs: [configure]
|
||||
if: needs.configure.outputs.matrix-secondary != '[]'
|
||||
uses: ./.github/workflows/build-template.yml
|
||||
with:
|
||||
config: ${{needs.configure.outputs.matrix-secondary}}
|
||||
check-level: ${{ needs.configure.outputs.check-level }}
|
||||
nightly: ${{ needs.configure.outputs.nightly }}
|
||||
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
|
||||
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
|
||||
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
|
||||
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
|
||||
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
|
||||
secrets: inherit
|
||||
|
||||
# This job collects results from all the matrix jobs
|
||||
# This can be made the "required" job, instead of listing each
|
||||
@@ -481,8 +348,6 @@ jobs:
|
||||
|
||||
# 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:
|
||||
# unofficial releases don't need them, and official release notes will be written by a human.
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
29
.github/workflows/pr-release.yml
vendored
29
.github/workflows/pr-release.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v7 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
|
||||
- name: 'Setup jq'
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: dcarbone/install-jq-action@v3.0.1
|
||||
uses: dcarbone/install-jq-action@v3.1.1
|
||||
|
||||
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
|
||||
- name: Check merge-base and nightly-testing-YYYY-MM-DD
|
||||
@@ -155,6 +155,20 @@ jobs:
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
# Check if force-mathlib-ci label is present
|
||||
LABELS="$(curl --retry 3 --location --silent \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
|
||||
| jq -r '.[].name')"
|
||||
|
||||
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
|
||||
echo "force-mathlib-ci label detected, forcing CI despite issues"
|
||||
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
|
||||
FORCE_CI=true
|
||||
else
|
||||
MESSAGE="$MESSAGE You can force Mathlib CI using the \`force-mathlib-ci\` label."
|
||||
fi
|
||||
|
||||
echo "Checking existing messages"
|
||||
|
||||
@@ -201,7 +215,12 @@ jobs:
|
||||
else
|
||||
echo "The message already exists in the comment body."
|
||||
fi
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
if [[ "$FORCE_CI" == "true" ]]; then
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
@@ -252,7 +271,7 @@ jobs:
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
|
||||
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
@@ -316,7 +335,7 @@ jobs:
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,6 +22,7 @@ settings.json
|
||||
.gdb_history
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
script/__pycache__
|
||||
*.produced.out
|
||||
CMakeSettings.json
|
||||
CppProperties.json
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
option(USE_MIMALLOC "use mimalloc" ON)
|
||||
|
||||
# store all variables passed on the command line into CL_ARGS so we can pass them to the stage builds
|
||||
# https://stackoverflow.com/a/48555098/161659
|
||||
# MUST be done before call to 'project'
|
||||
@@ -14,13 +17,12 @@ foreach(var ${vars})
|
||||
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
|
||||
# must forward options that generate incompatible .olean format
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
if("${var}" MATCHES "LLVM*")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
if("${var}" MATCHES "PKG_CONFIG*")
|
||||
elseif("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
elseif("${var}" MATCHES "USE_MIMALLOC")
|
||||
list(APPEND CL_ARGS "-D${var}=${${var}}")
|
||||
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
|
||||
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
|
||||
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
|
||||
endif()
|
||||
@@ -47,28 +49,41 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
|
||||
endif()
|
||||
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
|
||||
ExternalProject_add(cadical
|
||||
PREFIX cadical
|
||||
GIT_REPOSITORY https://github.com/arminbiere/cadical
|
||||
GIT_TAG rel-1.9.5
|
||||
GIT_TAG rel-2.1.2
|
||||
CONFIGURE_COMMAND ""
|
||||
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
BUILD_IN_SOURCE ON
|
||||
INSTALL_COMMAND "")
|
||||
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
|
||||
set(EXTRA_DEPENDS "cadical")
|
||||
list(APPEND EXTRA_DEPENDS cadical)
|
||||
endif()
|
||||
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
|
||||
endif()
|
||||
|
||||
if (USE_MIMALLOC)
|
||||
ExternalProject_add(mimalloc
|
||||
PREFIX mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc
|
||||
GIT_TAG v2.2.3
|
||||
# just download, we compile it as part of each stage as it is small
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
list(APPEND EXTRA_DEPENDS mimalloc)
|
||||
endif()
|
||||
|
||||
ExternalProject_add(stage0
|
||||
SOURCE_DIR "${LEAN_SOURCE_DIR}/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)
|
||||
# (however, 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
|
||||
@@ -82,6 +97,7 @@ ExternalProject_add(stage1
|
||||
BUILD_ALWAYS ON
|
||||
INSTALL_COMMAND ""
|
||||
DEPENDS stage0
|
||||
STEP_TARGETS configure
|
||||
)
|
||||
ExternalProject_add(stage2
|
||||
SOURCE_DIR "${LEAN_SOURCE_DIR}"
|
||||
|
||||
@@ -16,26 +16,39 @@
|
||||
"name": "debug",
|
||||
"displayName": "Debug build config",
|
||||
"cacheVariables": {
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/debug"
|
||||
},
|
||||
{
|
||||
"name": "reldebug",
|
||||
"displayName": "Release with debug info build config",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/reldebug"
|
||||
},
|
||||
{
|
||||
"name": "sanitize",
|
||||
"displayName": "Sanitize build config",
|
||||
"cacheVariables": {
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined",
|
||||
"LEANC_EXTRA_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined -DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
|
||||
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"SMALL_ALLOCATOR": "OFF",
|
||||
"BSYMBOLIC": "OFF"
|
||||
"USE_MIMALLOC": "OFF",
|
||||
"BSYMBOLIC": "OFF",
|
||||
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/sanitize"
|
||||
},
|
||||
{
|
||||
"name": "sandebug",
|
||||
"inherits": ["debug", "sanitize"],
|
||||
"inherits": ["sanitize", "debug"],
|
||||
"displayName": "Sanitize+debug build config",
|
||||
"binaryDir": "${sourceDir}/build/sandebug"
|
||||
}
|
||||
@@ -49,6 +62,10 @@
|
||||
"name": "debug",
|
||||
"configurePreset": "debug"
|
||||
},
|
||||
{
|
||||
"name": "reldebug",
|
||||
"configurePreset": "reldebug"
|
||||
},
|
||||
{
|
||||
"name": "sanitize",
|
||||
"configurePreset": "sanitize"
|
||||
@@ -69,6 +86,11 @@
|
||||
"configurePreset": "debug",
|
||||
"inherits": "release"
|
||||
},
|
||||
{
|
||||
"name": "reldebug",
|
||||
"configurePreset": "reldebug",
|
||||
"inherits": "release"
|
||||
},
|
||||
{
|
||||
"name": "sanitize",
|
||||
"configurePreset": "sanitize",
|
||||
|
||||
5306
RELEASES.md
5306
RELEASES.md
File diff suppressed because it is too large
Load Diff
@@ -590,9 +590,9 @@ This table should be read as follows:
|
||||
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
|
||||
|
||||
|
||||
Lean will print the termination argument it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
Lean will print the termination measure it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
|
||||
If Lean does not find the termination argument, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
If Lean does not find the termination measure, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
```
|
||||
termination_by e
|
||||
```
|
||||
@@ -672,7 +672,7 @@ def num_consts_lst : List Term → Nat
|
||||
end
|
||||
```
|
||||
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination argument (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination measure (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
|
||||
```
|
||||
mutual
|
||||
@@ -764,11 +764,12 @@ Structures and Records
|
||||
The ``structure`` command in Lean is used to define an inductive data type with a single constructor and to define its projections at the same time. The syntax is as follows:
|
||||
|
||||
```
|
||||
structure Foo (a : α) extends Bar, Baz : Sort u :=
|
||||
structure Foo (a : α) : Sort u extends Bar, Baz :=
|
||||
constructor :: (field₁ : β₁) ... (fieldₙ : βₙ)
|
||||
```
|
||||
|
||||
Here ``(a : α)`` is a telescope, that is, the parameters to the inductive definition. The name ``constructor`` followed by the double colon is optional; if it is not present, the name ``mk`` is used by default. The keyword ``extends`` followed by a list of previously defined structures is also optional; if it is present, an instance of each of these structures is included among the fields to ``Foo``, and the types ``βᵢ`` can refer to their fields as well. The output type, ``Sort u``, can be omitted, in which case Lean infers to smallest non-``Prop`` sort possible. Finally, ``(field₁ : β₁) ... (fieldₙ : βₙ)`` is a telescope relative to ``(a : α)`` and the fields in ``bar`` and ``baz``.
|
||||
Here ``(a : α)`` is a telescope, that is, the parameters to the inductive definition. The name ``constructor`` followed by the double colon is optional; if it is not present, the name ``mk`` is used by default. The keyword ``extends`` followed by a list of previously defined structures is also optional; if it is present, an instance of each of these structures is included among the fields to ``Foo``, and the types ``βᵢ`` can refer to their fields as well. The output type, ``Sort u``, can be omitted, in which case Lean infers to smallest non-``Prop`` sort possible (unless all the fields are ``Prop``, in which case it infers ``Prop``).
|
||||
Finally, ``(field₁ : β₁) ... (fieldₙ : βₙ)`` is a telescope relative to ``(a : α)`` and the fields in ``bar`` and ``baz``.
|
||||
|
||||
The declaration above is syntactic sugar for an inductive type declaration, and so results in the addition of the following constants to the environment:
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ lean_object * initialize_C(uint8_t builtin, lean_object *);
|
||||
...
|
||||
|
||||
lean_initialize_runtime_module();
|
||||
//lean_initialize(); // necessary if you (indirectly) access the `Lean` package
|
||||
//lean_initialize(); // necessary (and replaces `lean_initialize_runtime_module`) if you (indirectly) access the `Lean` package
|
||||
|
||||
lean_object * res;
|
||||
// use same default as for Lean executables
|
||||
|
||||
@@ -5,124 +5,90 @@ See below for the checklist for release candidates.
|
||||
|
||||
We'll use `v4.6.0` as the intended release version as a running example.
|
||||
|
||||
- Run `script/release_checklist.py v4.6.0` to check the status of the release.
|
||||
This script is idempotent, and should be safe to run at any stage of the release process.
|
||||
Note that as of v4.19.0, this script takes some autonomous actions, which can be prevented via `--dry-run`.
|
||||
- `git checkout releases/v4.6.0`
|
||||
(This branch should already exist, from the release candidates.)
|
||||
- `git pull`
|
||||
- In `src/CMakeLists.txt`, verify you see
|
||||
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)`
|
||||
- (both of these should already be in place from the release candidates)
|
||||
- (all of these should already be in place from the release candidates)
|
||||
- `git tag v4.6.0`
|
||||
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`,
|
||||
looking for the `v4.6.0` tag.
|
||||
- This step can take up to an hour.
|
||||
- This step can take up to two hours.
|
||||
- If you are intending to cut the next release candidate on the same day,
|
||||
you may want to start on the release candidate checklist now.
|
||||
- Next we need to prepare the release notes.
|
||||
- If the stable release is identical to the last release candidate (this should usually be the case),
|
||||
you can reuse the release notes that are already in the Lean Language Reference.
|
||||
- If you want to regenerate the release notes,
|
||||
run `script/release_notes.py --since v4.5.0` on the `releases/v4.6.0` branch,
|
||||
and see the section "Writing the release notes" below for more information.
|
||||
- Release notes live in https://github.com/leanprover/reference-manual, in e.g. `Manual/Releases/v4.6.0.lean`.
|
||||
It's best if you update these at the same time as a you update the `lean-toolchain` for the `reference-manual` repository, see below.
|
||||
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
|
||||
- Edit the release notes on Github to select the "Set as the latest release".
|
||||
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
|
||||
now that we have a written `RELEASES.md` section.
|
||||
Do a quick sanity check.
|
||||
- Verify on Github that "Set as the latest release" is checked.
|
||||
- Next, we will move a curated list of downstream repos to the latest stable release.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
|
||||
- Update the toolchain file
|
||||
- In the Lakefile, if there are dependencies on specific version tags of dependencies that you've already pushed as part of this process, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so it will work and be saved in the manifest
|
||||
- In order to have the access rights to push to these repositories and merge PRs,
|
||||
you will need to be a member of the `lean-release-managers` team at both `leanprover-community` and `leanprover`.
|
||||
Contact Kim Morrison (@kim-em) to arrange access.
|
||||
- For each of the repositories listed in `script/release_repos.yml`,
|
||||
- Run `script/release_steps.py v4.6.0 <repo>` (e.g. replacing `<repo>` with `batteries`), which will walk you through the following steps:
|
||||
- Create a new branch off `master`/`main` (as specified in the `branch` field), called `bump_to_v4.6.0`.
|
||||
- Update the contents of `lean-toolchain` to `leanprover/lean4:v4.6.0`.
|
||||
- In the `lakefile.toml` or `lakefile.lean`, if there are dependencies on specific version tags of dependencies, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so `lake update` will take care of modifying the manifest.
|
||||
- Run `lake update`
|
||||
- The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
- Commit the changes as `chore: bump toolchain to v4.6.0` and push.
|
||||
- Create a PR with title "chore: bump toolchain to v4.6.0".
|
||||
- Merge the PR once CI completes.
|
||||
- Create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the `stable` branch and push it.
|
||||
- We do this for the repositories:
|
||||
- [Batteries](https://github.com/leanprover-community/batteries)
|
||||
- No dependencies
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [lean4checker](https://github.com/leanprover/lean4checker)
|
||||
- No dependencies
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [doc-gen4](https://github.com/leanprover/doc-gen4)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Verso](https://github.com/leanprover/verso)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- The `SubVerso` dependency should be compatible with _every_ Lean release simultaneously, rather than following this workflow
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Cli](https://github.com/leanprover/lean4-cli)
|
||||
- No dependencies
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [ProofWidgets4](https://github.com/leanprover-community/ProofWidgets4)
|
||||
- Dependencies: `Batteries`
|
||||
- Note on versions and branches:
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag, following the version convention of the repository
|
||||
- [Aesop](https://github.com/leanprover-community/aesop)
|
||||
- Dependencies: `Batteries`
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [import-graph](https://github.com/leanprover-community/import-graph)
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [plausible](https://github.com/leanprover-community/plausible)
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Mathlib](https://github.com/leanprover-community/mathlib4)
|
||||
- Dependencies: `Aesop`, `ProofWidgets4`, `lean4checker`, `Batteries`, `doc-gen4`, `import-graph`
|
||||
- Toolchain bump PR notes:
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/lean4checker.yml` update the line
|
||||
`git checkout v4.6.0` to the appropriate tag.
|
||||
- Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably
|
||||
- Create and push the tag
|
||||
- Create a new branch from the tag, push it, and open a pull request against `stable`.
|
||||
Coordinate with a Mathlib maintainer to get this merged.
|
||||
- [REPL](https://github.com/leanprover-community/repl)
|
||||
- Dependencies: `Mathlib` (for test code)
|
||||
- Note that there are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- Run `scripts/release_checklist.py v4.6.0` to check that everything is in order.
|
||||
- The `v4.6.0` section of `RELEASES.md` is out of sync between
|
||||
`releases/v4.6.0` and `master`. This should be reconciled:
|
||||
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
|
||||
and commit this to `master`.
|
||||
- Merge the release announcement PR for the Lean website - it will be deployed automatically
|
||||
- Re-running `script/release_checklist.py` will then create the tag `v4.6.0` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
|
||||
- `script/release_checklist.py` will then merge the tag `v4.6.0` into the `stable` branch and push it (unless `stable-branch: false` in the `release_repos.yml` file).
|
||||
- Special notes on repositories with exceptional requirements:
|
||||
- `doc-gen4` has addition dependencies which we do not update at each toolchain release, although occasionally these break and need to be updated manually.
|
||||
- `verso`:
|
||||
- The `subverso` dependency is unusual in that it needs to be compatible with _every_ Lean release simultaneously.
|
||||
Usually you don't need to do anything.
|
||||
If you think something is wrong here please contact David Thrane Christiansen (@david-christiansen)
|
||||
- Warnings during `lake update` and `lake build` are expected.
|
||||
- `reference-manual`: the release notes generated by `script/release_notes.py` as described above must be included in
|
||||
`Manual/Releases/v4.6.0.lean`, and `import` and `include` statements adding in `Manual/Releases.lean`.
|
||||
- `ProofWidgets4` uses a non-standard sequential version tagging scheme, e.g. `v0.0.29`, which does not refer to the toolchain being used.
|
||||
You will need to identify the next available version number from https://github.com/leanprover-community/ProofWidgets4/releases,
|
||||
and push a new tag after merging the PR to `main`.
|
||||
- `mathlib4`:
|
||||
- The `lakefile.toml` should always refer to dependencies via their `main` or `master` branch,
|
||||
not a toolchain tag
|
||||
(with the exception of `ProofWidgets4`, which *must* use a sequential version tag).
|
||||
- Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably
|
||||
- `repl`:
|
||||
There are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- An awkward situtation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
|
||||
to a nightly toolchain that comes *after* the stable toolchain we are
|
||||
targeting. In this case it is necessary to create a branch `releases/v4.6.0` from the last commit which was on
|
||||
an earlier toolchain, move that branch to the stable toolchain, and create the toolchain tag from that branch.
|
||||
- Run `script/release_checklist.py v4.6.0` one last time to check that everything is in order.
|
||||
- Finally, make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Link to the blog post from the Zulip announcement.
|
||||
If there is a blog post, link to that from the zulip announcement.
|
||||
- Make sure that whoever is handling social media knows the release is out.
|
||||
|
||||
## Optimistic(?) time estimates:
|
||||
- Initial checks and push the tag: 30 minutes.
|
||||
- Waiting for the release: 60 minutes.
|
||||
- Fixing release notes: 10 minutes.
|
||||
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
|
||||
## Time estimates:
|
||||
- Initial checks and push the tag: 10 minutes.
|
||||
- Waiting for the release: 120 minutes.
|
||||
- Preparing release notes: 10 minutes.
|
||||
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 60 minutes.
|
||||
- Waiting for Mathlib CI and bors: 120 minutes.
|
||||
- Finalizing Mathlib tags and stable branch, and updating REPL: 15 minutes.
|
||||
- Posting announcement and/or blog post: 20 minutes.
|
||||
- Finalizing Mathlib tags and stable branch, and updating REPL: 20 minutes.
|
||||
- Posting announcement and/or blog post: 30 minutes.
|
||||
|
||||
# Creating a release candidate.
|
||||
|
||||
@@ -132,6 +98,10 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
|
||||
- Decide which nightly release you want to turn into a release candidate.
|
||||
We will use `nightly-2024-02-29` in this example.
|
||||
- It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
|
||||
- Throughout this process you can use `script/release_checklist.py v4.7.0-rc1` to track progress.
|
||||
This script will also try to do some steps autonomously. It is idempotent and safe to run at any point.
|
||||
You can prevent it taking any actions using `--dry-run`.
|
||||
- It is essential that Batteries and Mathlib already have reviewed branches compatible with this nightly.
|
||||
- Check that both Batteries and Mathlib's `bump/v4.7.0` branch contain `nightly-2024-02-29`
|
||||
in their `lean-toolchain`.
|
||||
@@ -142,76 +112,64 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
git fetch nightly tag nightly-2024-02-29
|
||||
git checkout nightly-2024-02-29
|
||||
git checkout -b releases/v4.7.0
|
||||
git push --set-upstream origin releases/v4.18.0
|
||||
```
|
||||
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
|
||||
- It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
|
||||
- In `src/CMakeLists.txt`,
|
||||
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
|
||||
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
|
||||
- change the `LEAN_VERSION_IS_RELEASE` line to `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
|
||||
- Commit your changes to `src/CMakeLists.txt`, and push.
|
||||
- `git tag v4.7.0-rc1`
|
||||
- `git push origin v4.7.0-rc1`
|
||||
- Now wait, while CI runs.
|
||||
- The CI setup parses the tag to discover the `-rc1` special description, and passes it to `cmake` using a `-D` option. The `-rc1` doesn't need to be placed in the configuration file.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
|
||||
- This step can take up to an hour.
|
||||
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
|
||||
- Verify that the release is marked as a prerelease (this should have been done automatically by the CI release job).
|
||||
- In the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
|
||||
This will add a list of all the commits since the last stable version.
|
||||
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
|
||||
- This step can take up to two hours.
|
||||
- Verify that the release appears at https://github.com/leanprover/lean4/releases/, marked as a prerelease (this should have been done automatically by the CI release job).
|
||||
- Next we need to prepare the release notes.
|
||||
- Run `script/release_notes.py --since v4.6.0` on the `releases/v4.7.0` branch,
|
||||
which will report diagnostic messages on `stderr`
|
||||
(including reporting commits that it couldn't associate with a PR, and hence will be omitted)
|
||||
and then a chunk of markdown on `stdout`.
|
||||
See the section "Writing the release notes" below for more information.
|
||||
- Release notes live in https://github.com/leanprover/reference-manual, in e.g. `Manual/Releases/v4.7.0.lean`.
|
||||
It's best if you update these at the same time as a you update the `lean-toolchain` for the `reference-manual` repository, see below.
|
||||
- Next, we will move a curated list of downstream repos to the release candidate.
|
||||
- This assumes that for each repository either:
|
||||
* There is already a *reviewed* branch `bump/v4.7.0` containing the required adaptations.
|
||||
The preparation of this branch is beyond the scope of this document.
|
||||
* The repository does not need any changes to move to the new version.
|
||||
- For each of the target repositories:
|
||||
- If the repository does not need any changes (i.e. `bump/v4.7.0` does not exist) then create
|
||||
a new PR updating `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1` and running `lake update`.
|
||||
- Otherwise:
|
||||
- Checkout the `bump/v4.7.0` branch.
|
||||
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
|
||||
- `git merge origin/master`
|
||||
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
|
||||
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
|
||||
back to `master` or `main`, and run `lake update` for those dependencies.
|
||||
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
|
||||
- `git commit`
|
||||
- `git push`
|
||||
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
|
||||
or notify the maintainers that it is ready to go.
|
||||
- Once the PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
|
||||
- We do this for the same list of repositories as for stable releases, see above.
|
||||
* Note that sometimes there are *unreviewed* but necessary changes on the `nightly-testing` branch of the repository.
|
||||
If so, you will need to merge these into the `bump_to_v4.7.0-rc1` branch manually.
|
||||
- For each of the repositories listed in `script/release_repos.yml`,
|
||||
- Run `script/release_steps.py v4.7.0-rc1 <repo>` (e.g. replacing `<repo>` with `batteries`), which will walk you through the following steps:
|
||||
- Create a new branch off `master`/`main` (as specified in the `branch` field), called `bump_to_v4.7.0-rc1`.
|
||||
- Merge `origin/bump/v4.7.0` if relevant (i.e. `bump-branch: true` appears in `release_repos.yml`).
|
||||
- Update the contents of `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`.
|
||||
- In the `lakefile.toml` or `lakefile.lean`, if there are dependencies on `nightly-testing`, `bump/v4.7.0`, or specific version tags, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so `lake update` will take care of modifying the manifest.
|
||||
- Run `lake update`
|
||||
- Run `lake build && if lake check-test; then lake test; fi` to check things are working.
|
||||
- Commit the changes as `chore: bump toolchain to v4.7.0-rc1` and push.
|
||||
- Create a PR with title "chore: bump toolchain to v4.7.0-rc1".
|
||||
- Merge the PR once CI completes.
|
||||
- Re-running `script/release_checklist.py` will then create the tag `v4.7.0-rc1` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
|
||||
- We do this for the same list of repositories as for stable releases, see above for notes about special cases.
|
||||
As above, there are dependencies between these, and so the process above is iterative.
|
||||
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
|
||||
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
|
||||
- It is essential for Mathlib and Batteries CI that you then create the next `bump/v4.8.0` branch
|
||||
for the next development cycle.
|
||||
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
|
||||
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
|
||||
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
|
||||
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
|
||||
- Run `script/release_checklist.py v4.7.0-rc1` one last time to check that everything is in order.
|
||||
- Make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.7.0-rc1`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
|
||||
- Uses branch name `dev_cycle_v4.8`.
|
||||
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
|
||||
- Replaces the "release notes will be copied" text in the `v4.6.0` section of `RELEASES.md` with the
|
||||
finalized release notes from the `releases/v4.6.0` branch.
|
||||
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
|
||||
```
|
||||
Release candidate, release notes will be copied from the branch `releases/v4.7.0` once completed.
|
||||
```
|
||||
and inserts the following section before that section:
|
||||
```
|
||||
v4.8.0
|
||||
----------
|
||||
Development in progress.
|
||||
```
|
||||
- Removes all the entries from the `./releases_drafts/` folder.
|
||||
- Titled "chore: begin development cycle for v4.8.0"
|
||||
|
||||
|
||||
## Time estimates:
|
||||
Slightly longer than the corresponding steps for a stable release.
|
||||
Similar process, but more things go wrong.
|
||||
@@ -252,10 +210,16 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
|
||||
|
||||
Release notes are automatically generated from the commit history, using `script/release_notes.py`.
|
||||
|
||||
Run this as `script/release_notes.py v4.6.0`, where `v4.6.0` is the *previous* release version. This will generate output
|
||||
for all commits since that tag. Note that there is output on both stderr, which should be manually reviewed,
|
||||
and on stdout, which should be manually copied to `RELEASES.md`.
|
||||
Run this as `script/release_notes.py --since v4.6.0`, where `v4.6.0` is the *previous* release version.
|
||||
This script should be run on the `releases/v4.7.0` branch.
|
||||
This will generate output for all commits since that tag.
|
||||
Note that there is output on both stderr, which should be manually reviewed,
|
||||
and on stdout, which should be manually copied into the `reference-manual` repository, in the file `Manual/Releases/v4.7.0.lean`.
|
||||
|
||||
The output on stderr should mostly be about commits for which the script could not find an associated PR,
|
||||
usually because a PR was rebase-merged because it contained an update to stage0.
|
||||
Some judgement is required here: ignore commits which look minor,
|
||||
but manually add items to the release notes for significant PRs that were rebase-merged.
|
||||
|
||||
There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
|
||||
See `./releases_drafts/README.md` for more information.
|
||||
|
||||
See `./releases_drafts/README.md` for more information.
|
||||
|
||||
@@ -179,7 +179,7 @@ local macro "have_eq " lhs:term:max rhs:term:max : tactic =>
|
||||
`(tactic|
|
||||
(have h : $lhs = $rhs :=
|
||||
-- TODO: replace with linarith
|
||||
by simp_arith at *; apply Nat.le_antisymm <;> assumption
|
||||
by simp +arith at *; apply Nat.le_antisymm <;> assumption
|
||||
try subst $lhs))
|
||||
|
||||
/-!
|
||||
|
||||
@@ -61,7 +61,7 @@ Parts of atomic names can be escaped by enclosing them in pairs of French double
|
||||
letterlike_symbols: [℀-⅏]
|
||||
escaped_ident_part: "«" [^«»\r\n\t]* "»"
|
||||
atomic_ident_rest: atomic_ident_start | [0-9'ⁿ] | subscript
|
||||
subscript: [₀-₉ₐ-ₜᵢ-ᵪ]
|
||||
subscript: [₀-₉ₐ-ₜᵢ-ᵪⱼ]
|
||||
```
|
||||
|
||||
String Literals
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
Platforms built & tested by our CI, available as binary releases via elan (see below)
|
||||
|
||||
* x86-64 Linux with glibc 2.27+
|
||||
* x86-64 Linux with glibc 2.26+
|
||||
* x86-64 macOS 10.15+
|
||||
* aarch64 (Apple Silicon) macOS 10.15+
|
||||
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022
|
||||
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022, Windows Server 2025
|
||||
|
||||
### Tier 2
|
||||
|
||||
|
||||
9
doc/std/README.md
Normal file
9
doc/std/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# The Lean standard library
|
||||
|
||||
This directory contains development information about the Lean standard library. The user-facing documentation of the standard library
|
||||
is part of the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/).
|
||||
|
||||
Here you will find
|
||||
* the [standard library vision document](./vision.md), including the call for contributions,
|
||||
* the [standard library style guide](./style.md), and
|
||||
* the [standard library naming conventions](./naming.md).
|
||||
3
doc/std/naming-tree.svg
Normal file
3
doc/std/naming-tree.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 68 KiB |
260
doc/std/naming.md
Normal file
260
doc/std/naming.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# Standard library naming conventions
|
||||
|
||||
The easiest way to access a result in the standard library is to correctly guess the name of the declaration (possibly with the help of identifier autocompletion). This is faster and has lower friction than more sophisticated search tools, so easily guessable names (which are still reasonably short) make Lean users more productive.
|
||||
|
||||
The guide that follows contains very few hard rules, many heuristics and a selection of examples. It cannot and does not present a deterministic algorithm for choosing good names in all situations. It is intended as a living document that gets clarified and expanded as situations arise during code reviews for the standard library. If applying one of the suggestions in this guide leads to nonsensical results in a certain situation, it is
|
||||
probably safe to ignore the suggestion (or even better, suggest a way to improve the suggestion).
|
||||
|
||||
## Prelude
|
||||
|
||||
Identifiers use a mix of `UpperCamelCase`, `lowerCamelCase` and `snake_case`, used for types, data, and theorems, respectively.
|
||||
|
||||
Structure fields should be named such that the projections have the correct names.
|
||||
|
||||
## Naming convention for types
|
||||
|
||||
When defining a type, i.e., a (possibly 0-ary) function whose codomain is Sort u for some u, it should be named in UpperCamelCase. Examples include `List`, and `List.IsPrefix`.
|
||||
|
||||
When defining a predicate, prefix the name by `Is`, like in `List.IsPrefix`. The `Is` prefix may be omitted if
|
||||
* the resulting name would be ungrammatical, or
|
||||
* the predicate depends on additional data in a way where the `Is` prefix would be confusing (like `List.Pairwise`), or
|
||||
* the name is an adjective (like `Std.Time.Month.Ordinal.Valid`)
|
||||
|
||||
## Namespaces and generalized projection notation
|
||||
|
||||
Almost always, definitions and theorems relating to a type should be placed in a namespace with the same name as the type. For example, operations and theorems about lists should be placed in the `List` namespace, and operations and theorems about `Std.Time.PlainDate` should be placed in the `Std.Time.PlainDate` namespace.
|
||||
|
||||
Declarations in the root namespace will be relatively rare. The most common type of declaration in the root namespace are declarations about data and properties exported by notation type classes, as long as they are not about a specific type implementing that type class. For example, we have
|
||||
|
||||
```lean
|
||||
theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b ↔ a = b := sorry
|
||||
```
|
||||
|
||||
in the root namespace, but
|
||||
|
||||
```lean
|
||||
theorem List.cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
|
||||
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
|
||||
```
|
||||
|
||||
belongs in the `List` namespace.
|
||||
|
||||
Subtleties arise when multiple namespaces are in play. Generally, place your theorem in the most specific namespace that appears in one of the hypotheses of the theorem. The following names are both correct according to this convention:
|
||||
|
||||
```lean
|
||||
theorem List.Sublist.reverse : l₁ <+ l₂ → l₁.reverse <+ l₂.reverse := sorry
|
||||
theorem List.reverse_sublist : l₁.reverse <+ l₂.reverse ↔ l₁ <+ l₂ := sorry
|
||||
```
|
||||
|
||||
Notice that the second theorem does not have a hypothesis of type `List.Sublist l` for some `l`, so the name `List.Sublist.reverse_iff` would be incorrect.
|
||||
|
||||
The advantage of placing results in a namespace like `List.Sublist` is that it enables generalized projection notation, i.e., given `h : l₁ <+ l₂`,
|
||||
one can write `h.reverse` to obtain a proof of `l₁.reverse <+ l₂.reverse`. Thinking about which dot notations are convenient can act as a guideline
|
||||
for deciding where to place a theorem, and is, on occasion, a good reason to duplicate a theorem into multiple namespaces.
|
||||
|
||||
### The `Std` namespace
|
||||
|
||||
New types that are added will usually be placed in the `Std` namespace and in the `Std/` source directory, unless there are good reasons to place
|
||||
them elsewhere.
|
||||
|
||||
Inside the `Std` namespace, all internal declarations should be `private` or else have a name component that clearly marks them as internal, preferably
|
||||
`Internal`.
|
||||
|
||||
|
||||
## Naming convention for data
|
||||
|
||||
When defining data, i.e., a (possibly 0-ary) function whose codomain is not Sort u, but has type Type u for some u, it should be named in lowerCamelCase. Examples include `List.append` and `List.isPrefixOf`.
|
||||
If your data is morally fully specified by its type, then use the naming procedure for theorems described below and convert the result to lower camel case.
|
||||
|
||||
If your function returns an `Option`, consider adding `?` as a suffix. If your function may panic, consider adding `!` as a suffix. In many cases, there will be multiple variants of a function; one returning an option, one that may panic and possibly one that takes a proof argument.
|
||||
|
||||
## Naming algorithm for theorems and some definitions
|
||||
|
||||
There is, in principle, a general algorithm for naming a theorem. The problem with this algorithm is that it produces very long and unwieldy names which need to be shortened. So choosing a name for a declaration can be thought of as consisting of a mechanical part and a creative part.
|
||||
|
||||
Usually the first part is to decide which namespace the result should live in, according to the guidelines described above.
|
||||
|
||||
Next, consider the type of your declaration as a tree. Inner nodes of this tree are function types or function applications. Leaves of the tree are 0-ary functions or bound variables.
|
||||
|
||||
As an example, consider the following result from the standard library:
|
||||
|
||||
```lean
|
||||
example {α : Type u} {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
|
||||
[Inhabited β] {m : Std.HashMap α β} {a : α} {h' : a ∈ m} : m[a]? = some (m[a]'h') :=
|
||||
sorry
|
||||
```
|
||||
|
||||
The correct namespace is clearly `Std.HashMap`. The corresponding tree looks like this:
|
||||
|
||||

|
||||
|
||||
The preferred spelling of a notation can be looked up by hovering over the notation.
|
||||
|
||||
Now traverse the tree and build a name according to the following rules:
|
||||
|
||||
* When encountering a function type, first turn the result type into a name, then all of the argument types from left to right, and join the names using `_of_`.
|
||||
* When encountering a function that is neither an infix notation nor a structure projection, first put the function name and then the arguments, joined by an underscore.
|
||||
* When encountering an infix notation, join the arguments using the name of the notation, separated by underscores.
|
||||
* When encountering a structure projection, proceed as for normal functions, but put the name of the projection last.
|
||||
* When encountering a name, put it in lower camel case.
|
||||
* Skip bound variables and proofs.
|
||||
* Type class arguments are also generally skipped.
|
||||
|
||||
When encountering namespaces names, concatenate them in lower camel case.
|
||||
|
||||
Applying this algorithm to our example yields the name `Std.HashMap.getElem?_eq_optionSome_getElem_of_mem`.
|
||||
|
||||
From there, the name should be shortened, using the following heuristics:
|
||||
|
||||
* The namespace of functions can be omitted if it is clear from context or if the namespace is the current one. This is almost always the case.
|
||||
* For infix operators, it is possible to leave out the RHS or the name of the notation and the RHS if they are clear from context.
|
||||
* Hypotheses can be left out if it is clear that they are required or if they appear in the conclusion.
|
||||
|
||||
Based on this, here are some possible names for our example:
|
||||
|
||||
1. `Std.HashMap.getElem?_eq`
|
||||
2. `Std.HashMap.getElem?_eq_of_mem`
|
||||
3. `Std.HashMap.getElem?_eq_some`
|
||||
4. `Std.HashMap.getElem?_eq_some_of_mem`
|
||||
5. `Std.HashMap.getElem?_eq_some_getElem`
|
||||
6. `Std.Hashmap.getElem?_eq_some_getElem_of_mem`
|
||||
|
||||
Choosing a good name among these then requires considering the context of the lemma. In this case it turns out that the first four options are underspecified as there is also a lemma relating `m[a]?` and `m[a]!` which could have the same name. This leaves the last two options, the first of which is shorter, and this is how the lemma is called in the Lean standard library.
|
||||
|
||||
Here are some additional examples:
|
||||
|
||||
```lean
|
||||
example {x y : List α} (h : x <+: y) (hx : x ≠ []) :
|
||||
x.head hx = y.head (h.ne_nil hx) := sorry
|
||||
```
|
||||
|
||||
Since we have an `IsPrefix` parameter, this should live in the `List.IsPrefix` namespace, and the algorithm suggests `List.IsPrefix.head_eq_head_of_ne_nil`, which is shortened to `List.IsPrefix.head`. Note here the difference between the namespace name (`IsPrefix`) and the recommended spelling of the corresponding notation (`prefix`).
|
||||
|
||||
```lean
|
||||
example : l₁ <+: l₂ → reverse l₁ <:+ reverse l₂ := sorry
|
||||
```
|
||||
|
||||
Again, this result should be in the `List.IsPrefix` namespace; the algorithm suggests `List.IsPrefix.reverse_prefix_reverse`, which becomes `List.IsPrefix.reverse`.
|
||||
|
||||
The following examples show how the traversal order often matters.
|
||||
|
||||
```lean
|
||||
theorem Nat.mul_zero (n : Nat) : n * 0 = 0 := sorry
|
||||
theorem Nat.zero_mul (n : Nat) : 0 * n = 0 := sorry
|
||||
```
|
||||
|
||||
Here we see that one name may be a prefix of another name:
|
||||
|
||||
```lean
|
||||
theorem Int.mul_ne_zero {a b : Int} (a0 : a ≠ 0) (b0 : b ≠ 0) : a * b ≠ 0 := sorry
|
||||
theorem Int.mul_ne_zero_iff {a b : Int} : a * b ≠ 0 ↔ a ≠ 0 ∧ b ≠ 0 := sorry
|
||||
```
|
||||
|
||||
It is usually a good idea to include the `iff` in a theorem name even if the name would still be unique without the name. For example,
|
||||
|
||||
```lean
|
||||
theorem List.head?_eq_none_iff : l.head? = none ↔ l = [] := sorry
|
||||
```
|
||||
|
||||
is a good name: if the lemma was simply called `List.head?_eq_none`, users might try to `apply` it when the goal is `l.head? = none`, leading
|
||||
to confusion.
|
||||
|
||||
The more common you expect (or want) a theorem to be, the shorter you should try to make the name. For example, we have both
|
||||
|
||||
```lean
|
||||
theorem Std.HashMap.getElem?_eq_none_of_contains_eq_false {a : α} : m.contains a = false → m[a]? = none := sorry
|
||||
theorem Std.HashMap.getElem?_eq_none {a : α} : ¬a ∈ m → m[a]? = none := sorry
|
||||
```
|
||||
|
||||
As users of the hash map are encouraged to use ∈ rather than contains, the second lemma gets the shorter name.
|
||||
|
||||
## Special cases
|
||||
|
||||
There are certain special “keywords” that may appear in identifiers.
|
||||
|
||||
| Keyword | Meaning | Example |
|
||||
| :---- | :---- | :---- |
|
||||
| `def` | Unfold a definition. Avoid this for public APIs. | `Nat.max_def` |
|
||||
| `refl` | Theorems of the form `a R a`, where R is a reflexive relation and `a` is an explicit parameter | `Nat.le_refl` |
|
||||
| `rfl` | Like `refl`, but with `a` implicit | `Nat.le_rfl` |
|
||||
| `irrefl` | Theorems of the form `¬a R a`, where R is an irreflexive relation | `Nat.lt_irrefl` |
|
||||
| `symm` | Theorems of the form `a R b → b R a`, where R is a symmetric relation (compare `comm` below) | `Eq.symm` |
|
||||
| `trans` | Theorems of the form `a R b → b R c → a R c`, where R is a transitive relation (R may carry data) | `Eq.trans` |
|
||||
| `antisymmm` | Theorems of the form `a R b → b R a → a = b`, where R is an antisymmetric relation | `Nat.le_antisymm` |
|
||||
| `congr` | Theorems of the form `a R b → f a S f b`, where R and S are usually equivalence relations | `Std.HashMap.mem_congr` |
|
||||
| `comm` | Theorems of the form `f a b = f b a` (compare `symm` above) | `Eq.comm`, `Nat.add_comm` |
|
||||
| `assoc` | Theorems of the form `g (f a b) c = f a (g b c)` (note the order! In most cases, we have f = g) | `Nat.add_sub_assoc` |
|
||||
| `distrib` | Theorems of the form `f (g a b) = g (f a) (f b)` | `Nat.add_left_distrib` |
|
||||
| `self` | May be used if a variable appears multiple times in the conclusion | `List.mem_cons_self` |
|
||||
| `inj` | Theorems of the form `f a = f b ↔ a = b`. | `Int.neg_inj`, `Nat.add_left_inj` |
|
||||
| `cancel` | Theorems which have one of the forms `f a = f b → a = b` or `g (f a) = a`, where `f` and `g` usually involve a binary operator | `Nat.add_sub_cancel` |
|
||||
| `cancel_iff` | Same as `inj`, but with different conventions for left and right (see below) | `Nat.add_right_cancel_iff` |
|
||||
| `ext` | Theorems of the form `f a = f b → a = b`, where `f` usually involves some kind of projection | `List.ext_getElem`
|
||||
| `mono` | Theorems of the form `a R b → f a R f b`, where `R` is a transitive relation | `List.countP_mono_left`
|
||||
|
||||
### Left and right
|
||||
|
||||
The keywords left and right are useful to disambiguate symmetric variants of theorems.
|
||||
|
||||
```lean
|
||||
theorem imp_congr_left (h : a ↔ b) : (a → c) ↔ (b → c) := sorry
|
||||
theorem imp_congr_right (h : a → (b ↔ c)) : (a → b) ↔ (a → c) := sorry
|
||||
```
|
||||
|
||||
It is not always obvious which version of a theorem should be “left” and which should be “right”.
|
||||
Heuristically, the theorem should name the side which is “more variable”, but there are exceptions. For some of the special keywords discussed in this section, there are conventions which should be followed, as laid out in the following examples:
|
||||
|
||||
```lean
|
||||
theorem Nat.left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := sorry
|
||||
theorem Nat.right_distrib (n m k : Nat) : (n + m) * k = n * k + m * k := sorry
|
||||
theorem Nat.add_left_cancel {n m k : Nat} : n + m = n + k → m = k := sorry
|
||||
theorem Nat.add_right_cancel {n m k : Nat} : n + m = k + m → n = k := sorry
|
||||
theorem Nat.add_left_cancel_iff {m k n : Nat} : n + m = n + k ↔ m = k := sorry
|
||||
theorem Nat.add_right_cancel_iff {m k n : Nat} : m + n = k + n ↔ m = k := sorry
|
||||
theorem Nat.add_left_inj {m k n : Nat} : m + n = k + n ↔ m = k := sorry
|
||||
theorem Nat.add_right_inj {m k n : Nat} : n + m = n + k ↔ m = k := sorry
|
||||
```
|
||||
|
||||
Note in particular that the convention is opposite for `cancel_iff` and `inj`.
|
||||
|
||||
```lean
|
||||
theorem Nat.add_sub_self_left (a b : Nat) : (a + b) - a = b := sorry
|
||||
theorem Nat.add_sub_self_right (a b : Nat) : (a + b) - b = a := sorry
|
||||
theorem Nat.add_sub_cancel (n m : Nat) : (n + m) - m = n := sorry
|
||||
```
|
||||
|
||||
## Primed names
|
||||
|
||||
Avoid disambiguating variants of a concept by appending the `'` character (e.g., introducing both `BitVec.sshiftRight` and `BitVec.sshiftRight'`), as it is impossible to tell the difference without looking at the type signature, the documentation or even the code, and even if you know what the two variants are there is no way to tell which is which. Prefer descriptive pairs `BitVec.sshiftRightNat`/`BitVec.sshiftRight`.
|
||||
|
||||
## Acronyms
|
||||
|
||||
For acronyms which are three letters or shorter, all letters should use the same case as dictated by the convention. For example, `IO` is a correct name for a type and the name `IO.Ref` may become `IORef` when used as part of a definition name and `ioRef` when used as part of a theorem name.
|
||||
|
||||
For acronyms which are at least four letters long, switch to lower case starting from the second letter. For example, `Json` is a correct name for a type, as is `JsonRPC`.
|
||||
|
||||
If an acronym is typically spelled using mixed case, this mixed spelling may be used in identifiers (for example `Std.Net.IPv4Addr`).
|
||||
|
||||
## Simp sets
|
||||
|
||||
Simp sets centered around a conversion function should be called `source_to_target`. For example, a simp set for the `BitVec.toNat` function, which goes from `BitVec` to
|
||||
`Nat`, should be called `bitvec_to_nat`.
|
||||
|
||||
## Variable names
|
||||
|
||||
We make the following recommendations for variable names, but without insisting on them:
|
||||
* Simple hypotheses should be named `h`, `h'`, or using a numerical sequence `h₁`, `h₂`, etc.
|
||||
* Another common name for a simple hypothesis is `w` (for "witness").
|
||||
* `List`s should be named `l`, `l'`, `l₁`, etc, or `as`, `bs`, etc.
|
||||
(Use of `as`, `bs` is encouraged when the lists are of different types, e.g. `as : List α` and `bs : List β`.)
|
||||
`xs`, `ys`, `zs` are allowed, but it is better if these are reserved for `Array` and `Vector`.
|
||||
A list of lists may be named `L`.
|
||||
* `Array`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the arrays are of different types, e.g. `as : Array α` and `bs : Array β`.
|
||||
An array of arrays may be named `xss`.
|
||||
* `Vector`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the vectors are of different types, e.g. `as : Vector α n` and `bs : Vector β n`.
|
||||
A vector of vectors may be named `xss`.
|
||||
* A common exception for `List` / `Array` / `Vector` is to use `acc` for an accumulator in a recursive function.
|
||||
* `i`, `j`, `k` are preferred for numerical indices.
|
||||
Descriptive names such as `start`, `stop`, `lo`, and `hi` are encouraged when they increase readability.
|
||||
* `n`, `m` are preferred for sizes, e.g. in `Vector α n` or `xs.size = n`.
|
||||
* `w` is preferred for the width of a `BitVec`.
|
||||
522
doc/std/style.md
Normal file
522
doc/std/style.md
Normal file
@@ -0,0 +1,522 @@
|
||||
# Standard library style
|
||||
|
||||
Please take some time to familiarize yourself with the stylistic conventions of
|
||||
the project and the specific part of the library you are planning to contribute
|
||||
to. While the Lean compiler may not enforce strict formatting rules,
|
||||
consistently formatted code is much easier for others to read and maintain.
|
||||
Attention to formatting is more than a cosmetic concern—it reflects the same
|
||||
level of precision and care required to meet the deeper standards of the Lean 4
|
||||
standard library.
|
||||
|
||||
Below we will give specific formatting prescriptions for various language constructs. Note that this style guide only applies to the Lean standard library, even though some examples in the guide are taken from other parts of the Lean code base.
|
||||
|
||||
## Basic whitespace rules
|
||||
|
||||
Syntactic elements (like `:`, `:=`, `|`, `::`) are surrounded by single spaces, with the exception of `,` and `;`, which are followed by a space but not preceded by one. Delimiters (like `()`, `{}`) do not have spaces on the inside, with the exceptions of subtype notation and structure instance notation.
|
||||
|
||||
Examples of correctly formatted function parameters:
|
||||
|
||||
* `{α : Type u}`
|
||||
* `[BEq α]`
|
||||
* `(cmp : α → α → Ordering)`
|
||||
* `(hab : a = b)`
|
||||
* `{d : { l : List ((n : Nat) × Vector Nat n) // l.length % 2 = 0 }}`
|
||||
|
||||
Examples of correctly formatted terms:
|
||||
|
||||
* `1 :: [2, 3]`
|
||||
* `letI : Ord α := ⟨cmp⟩; True`
|
||||
* `(⟨2, 3⟩ : Nat × Nat)`
|
||||
* `((2, 3) : Nat × Nat)`
|
||||
* `{ x with fst := f (4 + f 0), snd := 4, .. }`
|
||||
* `match 1 with | 0 => 0 | _ => 0`
|
||||
* `fun ⟨a, b⟩ _ _ => by cases hab <;> apply id; rw [hbc]`
|
||||
|
||||
Configure your editor to remove trailing whitespace. If you have set up Visual Studio Code for Lean development in the recommended way then the correct setting is applied automatically.
|
||||
|
||||
## Splitting terms across multiple lines
|
||||
|
||||
When splitting a term across multiple lines, increase indentation by two spaces starting from the second line. When splitting a function application, try to split at argument boundaries. If an argument itself needs to be split, increase indentation further as appropriate.
|
||||
|
||||
When splitting at an infix operator, the operator goes at the end of the first line, not at the beginning of the second line. When splitting at an infix operator, you may or may not increase indentation depth, depending on what is more readable.
|
||||
|
||||
When splitting an `if`-`then`-`else` expression, the `then` keyword wants to stay with the condition and the `else` keyword wants to stay with the alternative term. Otherwise, indent as if the `if` and `else` keywords were arguments to the same function.
|
||||
|
||||
When splitting a comma-separated bracketed sequence (i.e., anonymous constructor application, list/array/vector literal, tuple) it is allowed to indent subsequent lines for alignment, but indenting by two spaces is also allowed.
|
||||
|
||||
Do not orphan parentheses.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
|
||||
v₁.name.isPrefixOf v₂.name &&
|
||||
v₁.scopes == v₂.scopes &&
|
||||
v₁.mainModule == v₂.mainModule &&
|
||||
v₁.imported == v₂.imported
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem eraseP_eq_iff {p} {l : List α} :
|
||||
l.eraseP p = l' ↔
|
||||
((∀ a ∈ l, ¬ p a) ∧ l = l') ∨
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬ p b) ∧ p a ∧
|
||||
l = l₁ ++ a :: l₂ ∧ l' = l₁ ++ l₂ :=
|
||||
sorry
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
example : Nat :=
|
||||
functionWithAVeryLongNameSoThatSomeArgumentsWillNotFit firstArgument secondArgument
|
||||
(firstArgumentWithAnEquallyLongNameAndThatFunctionDoesHaveMoreArguments firstArgument
|
||||
secondArgument)
|
||||
secondArgument
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} (h : m.WF) :
|
||||
(m.alter k f).size =
|
||||
if m.contains k && (f (m.get? k)).isNone then
|
||||
m.size - 1
|
||||
else if !m.contains k && (f (m.get? k)).isSome then
|
||||
m.size + 1
|
||||
else
|
||||
m.size := by
|
||||
simp_to_raw using Raw₀.size_alter
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} (h : m.WF) :
|
||||
(m.alter k f).get? k' =
|
||||
if h : k == k' then
|
||||
cast (congrArg (Option ∘ β) (eq_of_beq h)) (f (m.get? k))
|
||||
else m.get? k' := by
|
||||
simp_to_raw using Raw₀.get?_alter
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
example : Nat × Nat :=
|
||||
⟨imagineThisWasALongTerm,
|
||||
imagineThisWasAnotherLongTerm⟩
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
example : Nat × Nat :=
|
||||
⟨imagineThisWasALongTerm,
|
||||
imagineThisWasAnotherLongTerm⟩
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
example : Vector Nat :=
|
||||
#v[imagineThisWasALongTerm,
|
||||
imagineThisWasAnotherLongTerm]
|
||||
```
|
||||
|
||||
## Basic file structure
|
||||
|
||||
Every file should start with a copyright header, imports (in the standard library, this always includes a `prelude` declaration) and a module documentation string. There should not be a blank line between the copyright header and the imports. There should be a blank line between the imports and the module documentation string.
|
||||
|
||||
If you explicitly declare universe variables, do so at the top of the file, after the module documentation.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
/-
|
||||
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,
|
||||
Yury Kudryashov
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Find
|
||||
|
||||
/-!
|
||||
**# Lemmas about `List.eraseP` and `List.erase`.**
|
||||
-/
|
||||
|
||||
universe u u'
|
||||
```
|
||||
|
||||
Syntax that is not supposed to be user-facing must be scoped. New public syntax must always be discussed explicitly in an RFC.
|
||||
|
||||
## Top-level commands and declarations
|
||||
|
||||
All top-level commands are unindented. Sectioning commands like `section` and `namespace` do not increase the indentation level.
|
||||
|
||||
Attributes may be placed on the same line as the rest of the command or on a separate line.
|
||||
|
||||
Multi-line declaration headers are indented by four spaces starting from the second line. The colon that indicates the type of a declaration may not be placed at the start of a line or on its own line.
|
||||
|
||||
Declaration bodies are indented by two spaces. Short declaration bodies may be placed on the same line as the declaration type.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem eraseP_eq_iff {p} {l : List α} :
|
||||
l.eraseP p = l' ↔
|
||||
((∀ a ∈ l, ¬ p a) ∧ l = l') ∨
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬ p b) ∧ p a ∧
|
||||
l = l₁ ++ a :: l₂ ∧ l' = l₁ ++ l₂ :=
|
||||
sorry
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
@[simp]
|
||||
theorem eraseP_nil : [].eraseP p = [] := rfl
|
||||
```
|
||||
|
||||
### Documentation comments
|
||||
|
||||
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
|
||||
|
||||
Declarations should be documented as required by the `docBlame` linter, which may be activated in a file using
|
||||
`set_option linter.missingDocs true` (we allow these to stay in the file).
|
||||
|
||||
Single-line documentation comments should go on the same line as `/--`/`-/`, while multi-line documentation strings
|
||||
should have these delimiters on their own line, with the documentation comment itself unindented.
|
||||
|
||||
Documentation comments must be written in the indicative mood. Use American orthography.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
/-- Carries out a monadic action on each mapping in the hash map in some order. -/
|
||||
@[inline] def forM (f : (a : α) → β a → m PUnit) (b : Raw α β) : m PUnit :=
|
||||
b.buckets.forM (AssocList.forM f)
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
/--
|
||||
Monadically computes a value by folding the given function over the mappings in the hash
|
||||
map in some order.
|
||||
-/
|
||||
@[inline] def foldM (f : δ → (a : α) → β a → m δ) (init : δ) (b : Raw α β) : m δ :=
|
||||
b.buckets.foldlM (fun acc l => l.foldlM f acc) init
|
||||
```
|
||||
|
||||
### Where clauses
|
||||
|
||||
The `where` keyword should be unindented, and all declarations bound by it should be indented with two spaces.
|
||||
|
||||
Blank lines before and after `where` and between declarations bound by `where` are optional and should be chosen
|
||||
to maximize readability.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
@[simp] theorem partition_eq_filter_filter (p : α → Bool) (l : List α) :
|
||||
partition p l = (filter p l, filter (not ∘ p) l) := by
|
||||
simp [partition, aux]
|
||||
where
|
||||
aux (l) {as bs} : partition.loop p l (as, bs) =
|
||||
(as.reverse ++ filter p l, bs.reverse ++ filter (not ∘ p) l) :=
|
||||
match l with
|
||||
| [] => by simp [partition.loop, filter]
|
||||
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
|
||||
```
|
||||
|
||||
### Termination arguments
|
||||
|
||||
The `termination_by`, `decreasing_by`, `partial_fixpoint` keywords should be unindented. The associated terms should be indented like declaration bodies.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
@[inline] def multiShortOption (handle : Char → m PUnit) (opt : String) : m PUnit := do
|
||||
let rec loop (p : String.Pos) := do
|
||||
if h : opt.atEnd p then
|
||||
return
|
||||
else
|
||||
handle (opt.get' p h)
|
||||
loop (opt.next' p h)
|
||||
termination_by opt.utf8ByteSize - p.byteIdx
|
||||
decreasing_by
|
||||
simp [String.atEnd] at h
|
||||
apply Nat.sub_lt_sub_left h
|
||||
simp [String.lt_next opt p]
|
||||
loop ⟨1⟩
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def substrEq (s1 : String) (off1 : String.Pos) (s2 : String) (off2 : String.Pos) (sz : Nat) : Bool :=
|
||||
off1.byteIdx + sz ≤ s1.endPos.byteIdx && off2.byteIdx + sz ≤ s2.endPos.byteIdx && loop off1 off2 { byteIdx := off1.byteIdx + sz }
|
||||
where
|
||||
loop (off1 off2 stop1 : Pos) :=
|
||||
if _h : off1.byteIdx < stop1.byteIdx then
|
||||
let c₁ := s1.get off1
|
||||
let c₂ := s2.get off2
|
||||
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
|
||||
else true
|
||||
termination_by stop1.1 - off1.1
|
||||
decreasing_by
|
||||
have := Nat.sub_lt_sub_left _h (Nat.add_lt_add_left c₁.utf8Size_pos off1.1)
|
||||
decreasing_tactic
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
|
||||
rw [div_eq, mod_eq]
|
||||
have h : Decidable (0 < n ∧ n ≤ m) := inferInstance
|
||||
cases h with
|
||||
| isFalse h => simp [h]
|
||||
| isTrue h =>
|
||||
simp [h]
|
||||
have ih := div_add_mod (m - n) n
|
||||
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
|
||||
```
|
||||
|
||||
### Deriving
|
||||
|
||||
The `deriving` clause should be unindented.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
structure Iterator where
|
||||
array : ByteArray
|
||||
idx : Nat
|
||||
deriving Inhabited
|
||||
```
|
||||
|
||||
## Notation and Unicode
|
||||
|
||||
We generally prefer to use notation as available. We usually prefer the Unicode versions of notations over non-Unicode alternatives.
|
||||
|
||||
There are some rules and exceptions regarding specific notations which are listed below:
|
||||
|
||||
* Sigma types: use `(a : α) × β a` instead of `Σ a, β a` or `Sigma β`.
|
||||
* Function arrows: use `fun a => f x` instead of `fun x ↦ f x` or `λ x => f x` or any other variant.
|
||||
|
||||
## Language constructs
|
||||
|
||||
### Pattern matching, induction etc.
|
||||
|
||||
Match arms are indented at the indentation level that the match statement would have if it was on its own line. If the match is implicit, then the arms should be indented as if the match was explicitly given. The content of match arms is indented two spaces, so that it appears on the same level as the match pattern.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def alter [BEq α] {β : Type v} (a : α) (f : Option β → Option β) :
|
||||
AssocList α (fun _ => β) → AssocList α (fun _ => β)
|
||||
| nil => match f none with
|
||||
| none => nil
|
||||
| some b => AssocList.cons a b nil
|
||||
| cons k v l =>
|
||||
if k == a then
|
||||
match f v with
|
||||
| none => l
|
||||
| some b => cons a b l
|
||||
else
|
||||
cons k v (alter a f l)
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a ∈ xs) :
|
||||
∃ as bs, xs = as ++ a :: bs ∧ a ∉ as := by
|
||||
induction xs with
|
||||
| nil => cases h
|
||||
| cons x xs ih =>
|
||||
simp at h
|
||||
cases h with
|
||||
| inl h => exact ⟨[], xs, by simp_all⟩
|
||||
| inr h =>
|
||||
by_cases h' : a = x
|
||||
· subst h'
|
||||
exact ⟨[], xs, by simp⟩
|
||||
· obtain ⟨as, bs, rfl, h⟩ := ih h
|
||||
exact ⟨x :: as, bs, rfl, by simp_all⟩
|
||||
```
|
||||
|
||||
Aligning match arms is allowed, but not required.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
|
||||
match h₁?, h₂? with
|
||||
| none, none => return none
|
||||
| none, some h => return h
|
||||
| some h, none => return h
|
||||
| some h₁, some h₂ => mkEqTrans h₁ h₂
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
|
||||
match h₁?, h₂? with
|
||||
| none, none => return none
|
||||
| none, some h => return h
|
||||
| some h, none => return h
|
||||
| some h₁, some h₂ => mkEqTrans h₁ h₂
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
|
||||
match h₁?, h₂? with
|
||||
| none, none => return none
|
||||
| none, some h => return h
|
||||
| some h, none => return h
|
||||
| some h₁, some h₂ => mkEqTrans h₁ h₂
|
||||
```
|
||||
|
||||
### Structures
|
||||
|
||||
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
|
||||
|
||||
When using structure instance syntax over multiple lines, the opening brace should go on the preceding line, while the closing brace should go on its own line. The rest of the syntax should be indented by one level. During structure updates, the `with` clause goes on the same line as the opening brace. Aligning at the assignment symbol is allowed but not required.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
|
||||
IO AddConstAsyncResult := do
|
||||
let sigPromise ← IO.Promise.new
|
||||
let infoPromise ← IO.Promise.new
|
||||
let extensionsPromise ← IO.Promise.new
|
||||
let checkedEnvPromise ← IO.Promise.new
|
||||
let asyncConst := {
|
||||
constInfo := {
|
||||
name := constName
|
||||
kind
|
||||
sig := sigPromise.result
|
||||
constInfo := infoPromise.result
|
||||
}
|
||||
exts? := guard reportExts *> some extensionsPromise.result
|
||||
}
|
||||
return {
|
||||
constName, kind
|
||||
mainEnv := { env with
|
||||
asyncConsts := env.asyncConsts.add asyncConst
|
||||
checked := checkedEnvPromise.result }
|
||||
asyncEnv := { env with
|
||||
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
|
||||
}
|
||||
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
instance [Inhabited α] : Inhabited (Descr α β σ) where
|
||||
default := {
|
||||
name := default
|
||||
mkInitial := default
|
||||
ofOLeanEntry := default
|
||||
toOLeanEntry := default
|
||||
addEntry := fun s _ => s
|
||||
}
|
||||
```
|
||||
|
||||
### Declaring structures
|
||||
|
||||
When defining structure types, do not parenthesize structure fields.
|
||||
|
||||
When declaring a structure type with a custom constructor name, put the custom name on its own line, indented like the
|
||||
structure fields, and add a documentation comment.
|
||||
|
||||
Correct:
|
||||
|
||||
```lean
|
||||
/--
|
||||
A bitvector of the specified width.
|
||||
|
||||
This is represented as the underlying `Nat` number in both the runtime
|
||||
and the kernel, inheriting all the special support for `Nat`.
|
||||
-/
|
||||
structure BitVec (w : Nat) where
|
||||
/--
|
||||
Constructs a `BitVec w` from a number less than `2^w`.
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector.
|
||||
-/
|
||||
ofFin ::
|
||||
/--
|
||||
Interprets a bitvector as a number less than `2^w`.
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector.
|
||||
-/
|
||||
toFin : Fin (2 ^ w)
|
||||
```
|
||||
|
||||
## Tactic proofs
|
||||
|
||||
Tactic proofs are the most common thing to break during any kind of upgrade, so it is important to write them in a way that minimizes the likelihood of proofs breaking and that makes it easy to debug breakages if they do occur.
|
||||
|
||||
If there are multiple goals, either use a tactic combinator (like `all_goals`) to operate on all of them or a clearly specified subset, or use focus dots to work on goals one at a time. Using structured proofs (e.g., `induction … with`) is encouraged but not mandatory.
|
||||
|
||||
Squeeze non-terminal `simp`s (i.e., calls to `simp` which do not close the goal). Squeezing terminal `simp`s is generally discouraged, although there are exceptions (for example if squeezing yields a noticeable performance improvement).
|
||||
|
||||
Do not over-golf proofs in ways that are likely to lead to hard-to-debug breakage. Examples of things to avoid include complex multi-goal manipulation using lots of tactic combinators, complex uses of the substitution operator (`▸`) and clever point-free expressions (possibly involving anonymous function notation for multiple arguments).
|
||||
|
||||
Do not under-golf proofs: for routine tasks, use the most powerful tactics available.
|
||||
|
||||
Do not use `erw`. Avoid using `rfl` after `simp` or `rw`, as this usually indicates a missing lemma that should be used instead of `rfl`.
|
||||
|
||||
Use `(d)simp` or `rw` instead of `delta` or `unfold`. Use `refine` instead of `refine’`. Use `haveI` and `letI` only if they are actually required.
|
||||
|
||||
Prefer highly automated tactics (like `grind` and `omega`) over low-level proofs, unless the automated tactic requires unacceptable additional imports or has bad performance. If you decide against using a highly automated tactic, leave a comment explaining the decision.
|
||||
|
||||
## `do` notation
|
||||
|
||||
The `do` keyword goes on the same line as the corresponding `:=` (or `=>`, or similar). `Id.run do` should be treated as if it was a bare `do`.
|
||||
|
||||
Use early `return` statements to reduce nesting depth and make the non-exceptional control flow of a function easier to see.
|
||||
|
||||
Alternatives for `let` matches may be placed in the same line or in the next line, indented by two spaces. If the term that is
|
||||
being matched on is itself more than one line and there is an alternative present, consider breaking immediately after `←` and indent
|
||||
as far as necessary to ensure readability.
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
|
||||
let some decl ← findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
|
||||
return decl
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
|
||||
let some decl ←
|
||||
findFunDecl? fvarId
|
||||
| throwError "unknown local function {fvarId.name}"
|
||||
return decl
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
|
||||
let some decl ← findFunDecl?
|
||||
fvarId
|
||||
| throwError "unknown local function {fvarId.name}"
|
||||
return decl
|
||||
```
|
||||
|
||||
Correct:
|
||||
```lean
|
||||
def tagUntaggedGoals (parentTag : Name) (newSuffix : Name) (newGoals : List MVarId) : TacticM Unit := do
|
||||
let mctx ← getMCtx
|
||||
let mut numAnonymous := 0
|
||||
for g in newGoals do
|
||||
if mctx.isAnonymousMVar g then
|
||||
numAnonymous := numAnonymous + 1
|
||||
modifyMCtx fun mctx => Id.run do
|
||||
let mut mctx := mctx
|
||||
let mut idx := 1
|
||||
for g in newGoals do
|
||||
if mctx.isAnonymousMVar g then
|
||||
if numAnonymous == 1 then
|
||||
mctx := mctx.setMVarUserName g parentTag
|
||||
else
|
||||
mctx := mctx.setMVarUserName g (parentTag ++ newSuffix.appendIndexAfter idx)
|
||||
idx := idx + 1
|
||||
pure mctx
|
||||
```
|
||||
|
||||
98
doc/std/vision.md
Normal file
98
doc/std/vision.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# The Lean 4 standard library
|
||||
|
||||
Maintainer team (in alphabetical order): Henrik Böving, Markus Himmel
|
||||
(community contact & external contribution coordinator), Kim Morrison, Paul
|
||||
Reichert, Sofia Rodrigues.
|
||||
|
||||
The Lean 4 standard library is a core part of the Lean distribution, providing
|
||||
essential building blocks for functional programming, verified software
|
||||
development, and software verification. Unlike the standard libraries of most
|
||||
other languages, many of its components are formally verified and can be used
|
||||
as part of verified applications.
|
||||
|
||||
The standard library is a public API that contains the components listed in the
|
||||
standard library outline below. Not all public APIs in the Lean distribution
|
||||
are part of the standard library, and the standard library does not correspond
|
||||
to a certain directory within the Lean source repository (like `Std`). For
|
||||
example, the metaprogramming framework is not part of the standard library, but
|
||||
basic types like `True` and `Nat` are.
|
||||
|
||||
The standard library is under active development. Our guiding principles are:
|
||||
|
||||
* Provide comprehensive, verified building blocks for real-world software.
|
||||
* Build a public API of the highest quality with excellent internal consistency.
|
||||
* Carefully optimize components that may be used in performance-critical software.
|
||||
* Ensure smooth adoption and maintenance for users.
|
||||
* Offer excellent documentation, example projects, and guides.
|
||||
* Provide a reliable and extensible basis that libraries for software
|
||||
development, software verification and mathematics can build on.
|
||||
|
||||
The standard library is principally developed by the Lean FRO. Community
|
||||
contributions are welcome. If you would like to contribute, please refer to the
|
||||
call for contributions below.
|
||||
|
||||
### Standard library outline
|
||||
|
||||
1. Core types and operations
|
||||
1. Basic types
|
||||
2. Numeric types, including floating point numbers
|
||||
3. Containers
|
||||
4. Strings and formatting
|
||||
2. Language constructs
|
||||
1. Ranges and iterators
|
||||
2. Comparison, ordering, hashing and related type classes
|
||||
3. Basic monad infrastructure
|
||||
3. Libraries
|
||||
1. Random numbers
|
||||
2. Dates and times
|
||||
4. Operating system abstractions
|
||||
1. Concurrency and parallelism primitives
|
||||
2. Asynchronous I/O
|
||||
3. FFI helpers
|
||||
4. Environment, file system, processes
|
||||
5. Locales
|
||||
|
||||
The material covered in the first three sections (core types and operations,
|
||||
language constructs and libraries) will be verified, with the exception of
|
||||
floating point numbers and the parts of the libraries that interface with the
|
||||
operating system (e.g., sources of operating system randomness or time zone
|
||||
database access).
|
||||
|
||||
### Call for contributions
|
||||
|
||||
Thank you for taking interest in contributing to the Lean standard library\!
|
||||
There are two main ways for community members to contribute to the Lean
|
||||
standard library: by contributing experience reports or by contributing code
|
||||
and lemmas.
|
||||
|
||||
**If you are using Lean for software verification or verified software
|
||||
development:** hearing about your experiences using Lean and its standard
|
||||
library for software verification is extremely valuable to us. We are committed
|
||||
to building a standard library suitable for real-world applications and your
|
||||
input will directly influence the continued evolution of the Lean standard
|
||||
library. Please reach out to the standard library maintainer team via Zulip
|
||||
(either in a public thread in the \#lean4 channel or via direct message). Even
|
||||
just a link to your code helps. Thanks\!
|
||||
|
||||
**If you have code that you believe could enhance the Lean 4 standard
|
||||
library:** we encourage you to initiate a discussion in the \#lean4 channel on
|
||||
Zulip. This is the most effective way to receive preliminary feedback on your
|
||||
contribution. The Lean standard library has a very precise scope and it has
|
||||
very high quality standards, so at the moment we are mostly interested in
|
||||
contributions that expand upon existing material rather than introducing novel
|
||||
concepts.
|
||||
|
||||
**If you would like to contribute code to the standard library but don’t know
|
||||
what to work on:** we are always excited to meet motivated community members
|
||||
who would like to contribute, and there is always impactful work that is
|
||||
suitable for new contributors. Please reach out to Markus Himmel on Zulip to
|
||||
discuss possible contributions.
|
||||
|
||||
As laid out in the [project-wide External Contribution
|
||||
Guidelines](../../CONTRIBUTING.md),
|
||||
PRs are much more likely to be merged if they are preceded by an RFC or if you
|
||||
discussed your planned contribution with a member of the standard library
|
||||
maintainer team. When in doubt, introducing yourself is always a good idea.
|
||||
|
||||
All code in the standard library is expected to strictly adhere to the
|
||||
[standard library coding conventions](./style.md).
|
||||
28
flake.lock
generated
28
flake.lock
generated
@@ -36,17 +36,17 @@
|
||||
},
|
||||
"nixpkgs-cadical": {
|
||||
"locked": {
|
||||
"lastModified": 1722221733,
|
||||
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
|
||||
"lastModified": 1740791350,
|
||||
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
|
||||
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -67,12 +67,30 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-older": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1523316493,
|
||||
"narHash": "sha256-5qJS+i5ECICPAKA6FhPLIWkhPKDnOZsZbh2PHYF1Kbs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-cadical": "nixpkgs-cadical",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
"nixpkgs-old": "nixpkgs-old",
|
||||
"nixpkgs-older": "nixpkgs-older"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
|
||||
16
flake.nix
16
flake.nix
@@ -5,17 +5,20 @@
|
||||
# old nixpkgs used for portable release with older glibc (2.27)
|
||||
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
|
||||
inputs.nixpkgs-old.flake = false;
|
||||
# for cadical 1.9.5; sync with CMakeLists.txt
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
|
||||
# old nixpkgs used for portable release with older glibc (2.26)
|
||||
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
|
||||
inputs.nixpkgs-older.flake = false;
|
||||
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
|
||||
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
pkgs = import inputs.nixpkgs { inherit system; };
|
||||
# An old nixpkgs for creating releases with an old glibc
|
||||
pkgsDist-old = import nixpkgs-old { inherit system; };
|
||||
pkgsDist-old = import inputs.nixpkgs-older { inherit system; };
|
||||
# An old nixpkgs for creating releases with an old glibc
|
||||
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
|
||||
pkgsDist-old-aarch = import inputs.nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
|
||||
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
|
||||
cadical = if pkgs.stdenv.isLinux then
|
||||
# use statically-linked cadical on Linux to avoid glibc versioning troubles
|
||||
@@ -60,6 +63,7 @@
|
||||
GLIBC_DEV = pkgsDist.glibc.dev;
|
||||
GCC_LIB = pkgsDist.gcc.cc.lib;
|
||||
ZLIB = pkgsDist.zlib;
|
||||
# for CI coredumps
|
||||
GDB = pkgsDist.gdb;
|
||||
});
|
||||
in {
|
||||
|
||||
@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
'';
|
||||
} // args // {
|
||||
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
|
||||
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
cmakeFlags = ["-DSMALL_ALLOCATOR=ON" "-DUSE_MIMALLOC=OFF"] ++ (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
|
||||
preConfigure = args.preConfigure or "" + ''
|
||||
# ignore absence of submodule
|
||||
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
|
||||
|
||||
@@ -159,7 +159,7 @@ with builtins; let
|
||||
dir=$(dirname $relpath)
|
||||
mkdir -p $dir $out/$dir $ilean/$dir $c/$dir
|
||||
if [ -d $src ]; then cp -r $src/. .; else cp $src $leanPath; fi
|
||||
lean -o $out/$oleanPath -i $ilean/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
|
||||
lean -o $out/$oleanPath -i $out/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
|
||||
'';
|
||||
}) // {
|
||||
inherit deps;
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
Draft release notes
|
||||
-------------------
|
||||
|
||||
This folder contains drafts of release notes for inclusion in `RELEASES.md`.
|
||||
This folder contains drafts of release notes for the upcoming version.
|
||||
During the process to create a release candidate, we look through all the commits that make up the release
|
||||
to prepare the release notes, and in that process we take these drafts into account.
|
||||
|
||||
Guidelines:
|
||||
- You should prefer adding release notes to commit messages over adding anything to this folder.
|
||||
A release note should briefly explain the impact of a change from a user's point of view.
|
||||
Please mark these parts out with words such as **release notes** and/or **breaking changes**.
|
||||
- It is not necessary to add anything to this folder. It is meant for larger features that span multiple PRs,
|
||||
- Write good commit messages
|
||||
The first paragraph should briefly explain the impact of a change from a user's point of view.
|
||||
(Recall: the first paragraph, which should begin with "This PR",
|
||||
is automatically incorporated into the release notes by `script/release_notes.py`.
|
||||
See `doc/dev/release_checklist.md` for more details.).
|
||||
- This folder is only needed for larger features that span multiple PRs,
|
||||
or for anything that would be helpful when preparing the release notes that might be missed
|
||||
by someone reading through the change log.
|
||||
- If the PR that adds a feature simultaneously adds a draft release note, including the PR number is not required
|
||||
since it can be obtained from the git history for the file.
|
||||
|
||||
When release notes are prepared, all the draft release notes are deleted from this folder.
|
||||
For release candidates beyond the first one, you can either update `RELEASE.md` directly
|
||||
or continue to add drafts.
|
||||
|
||||
When a release is finalized, we will copy the completed release notes from `RELEASE.md` to the `master` branch.
|
||||
When notes from this folder are incorporated into the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/releases/#release-notes),
|
||||
they should then be deleted from here.
|
||||
|
||||
6
releases_drafts/environment.md
Normal file
6
releases_drafts/environment.md
Normal file
@@ -0,0 +1,6 @@
|
||||
**Breaking Changes**
|
||||
|
||||
* The functions `Lean.Environment.importModules` and `Lean.Environment.finalizeImport` have been extended with a new parameter `loadExts : Bool := false` that enables environment extension state loading.
|
||||
Their previous behavior corresponds to setting the flag to `true` but is only safe to do in combination with `enableInitializersExecution`; see also the `importModules` docstring.
|
||||
The new default value `false` ensures the functions can be used correctly multiple times within the same process when environment extension access is not needed.
|
||||
The wrapper function `Lean.Environment.withImportModules` now always calls `importModules` with `loadExts := false` as it is incompatible with extension loading.
|
||||
70
script/benchReelabRss.lean
Normal file
70
script/benchReelabRss.lean
Normal file
@@ -0,0 +1,70 @@
|
||||
import Lean.Data.Lsp
|
||||
open Lean
|
||||
open Lean.Lsp
|
||||
open Lean.JsonRpc
|
||||
|
||||
/-!
|
||||
Tests language server memory use by repeatedly re-elaborate a given file.
|
||||
|
||||
NOTE: only works on Linux for now.
|
||||
|
||||
HACK: The line that is to be prepended with a space is hard-coded below to be sufficiently far down
|
||||
not to touch the imports for usual files.
|
||||
-/
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
|
||||
let uri := s!"file:///{file}"
|
||||
Ipc.runWith leanCmd (#["--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
|
||||
-- for use with heaptrack:
|
||||
--Ipc.runWith "heaptrack" (#[leanCmd, "--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
|
||||
-- -- heaptrack has no quiet mode??
|
||||
-- let _ ← (← Ipc.stdout).getLine
|
||||
-- let _ ← (← Ipc.stdout).getLine
|
||||
let capabilities := {
|
||||
textDocument? := some {
|
||||
completion? := some {
|
||||
completionItem? := some {
|
||||
insertReplaceSupport? := true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ipc.writeRequest ⟨0, "initialize", { capabilities : InitializeParams }⟩
|
||||
|
||||
let text ← IO.FS.readFile file
|
||||
let mut requestNo : Nat := 1
|
||||
let mut versionNo : Nat := 1
|
||||
Ipc.writeNotification ⟨"textDocument/didOpen", {
|
||||
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }⟩
|
||||
for i in [0:iters.toNat!] do
|
||||
if i > 0 then
|
||||
versionNo := versionNo + 1
|
||||
let pos := { line := 19, character := 0 }
|
||||
let params : DidChangeTextDocumentParams := {
|
||||
textDocument := {
|
||||
uri := uri
|
||||
version? := versionNo
|
||||
}
|
||||
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
|
||||
start := pos
|
||||
«end» := pos
|
||||
} " "]
|
||||
}
|
||||
let params := toJson params
|
||||
Ipc.writeNotification ⟨"textDocument/didChange", params⟩
|
||||
requestNo := requestNo + 1
|
||||
|
||||
let diags ← Ipc.collectDiagnostics requestNo uri versionNo
|
||||
if let some diags := diags then
|
||||
for diag in diags.param.diagnostics do
|
||||
IO.eprintln diag.message
|
||||
requestNo := requestNo + 1
|
||||
|
||||
let status ← IO.FS.readFile s!"/proc/{(← read).pid}/status"
|
||||
for line in status.splitOn "\n" |>.filter (·.startsWith "RssAnon") do
|
||||
IO.eprintln line
|
||||
|
||||
let _ ← Ipc.collectDiagnostics requestNo uri versionNo
|
||||
(← Ipc.stdin).writeLspMessage (Message.notification "exit" none)
|
||||
discard <| Ipc.waitForExit
|
||||
167
script/merge_remote.py
Executable file
167
script/merge_remote.py
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Merge a tag into a branch on a GitHub repository.
|
||||
|
||||
This script checks if a specified tag can be merged cleanly into a branch and performs
|
||||
the merge if possible. If the merge cannot be done cleanly, it prints a helpful message.
|
||||
|
||||
Usage:
|
||||
python3 merge_remote.py <org/repo> <branch> <tag>
|
||||
|
||||
Arguments:
|
||||
org/repo: GitHub repository in the format 'organization/repository'
|
||||
branch: The target branch to merge into
|
||||
tag: The tag to merge from
|
||||
|
||||
Example:
|
||||
python3 merge_remote.py leanprover/mathlib4 stable v4.6.0
|
||||
|
||||
The script uses the GitHub CLI (`gh`), so make sure it's installed and authenticated.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
def run_command(command, check=True, capture_output=True):
|
||||
"""Run a shell command and return the result."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
check=check,
|
||||
shell=True,
|
||||
text=True,
|
||||
capture_output=capture_output
|
||||
)
|
||||
return result
|
||||
except subprocess.CalledProcessError as e:
|
||||
if capture_output:
|
||||
print(f"Command failed: {command}")
|
||||
print(f"Error: {e.stderr}")
|
||||
return e
|
||||
|
||||
|
||||
def clone_repo(repo, temp_dir):
|
||||
"""Clone the repository to a temporary directory using shallow clone."""
|
||||
print(f"Shallow cloning {repo}...")
|
||||
# Keep the shallow clone for efficiency
|
||||
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
|
||||
if clone_result.returncode != 0:
|
||||
print(f"Failed to clone repository {repo}.")
|
||||
print(f"Error: {clone_result.stderr}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_and_merge(repo, branch, tag, temp_dir):
|
||||
"""Check if tag can be merged into branch and perform the merge if possible."""
|
||||
# Change to the temporary directory
|
||||
os.chdir(temp_dir)
|
||||
|
||||
# First fetch the specific remote branch with its history
|
||||
print(f"Fetching branch '{branch}'...")
|
||||
fetch_branch = run_command(f"git fetch origin {branch}:refs/remotes/origin/{branch} --update-head-ok")
|
||||
if fetch_branch.returncode != 0:
|
||||
print(f"Error: Failed to fetch branch '{branch}'.")
|
||||
return False
|
||||
|
||||
# Then fetch the specific tag
|
||||
print(f"Fetching tag '{tag}'...")
|
||||
fetch_tag = run_command(f"git fetch origin tag {tag}")
|
||||
if fetch_tag.returncode != 0:
|
||||
print(f"Error: Failed to fetch tag '{tag}'.")
|
||||
return False
|
||||
|
||||
# Check if branch exists now that we've fetched it
|
||||
branch_check = run_command(f"git branch -r | grep origin/{branch}")
|
||||
if branch_check.returncode != 0:
|
||||
print(f"Error: Branch '{branch}' does not exist in repository.")
|
||||
return False
|
||||
|
||||
# Check if tag exists
|
||||
tag_check = run_command(f"git tag -l {tag}")
|
||||
if tag_check.returncode != 0 or not tag_check.stdout.strip():
|
||||
print(f"Error: Tag '{tag}' does not exist in repository.")
|
||||
return False
|
||||
|
||||
# Checkout the branch
|
||||
print(f"Checking out branch '{branch}'...")
|
||||
checkout_result = run_command(f"git checkout -b {branch} origin/{branch}")
|
||||
if checkout_result.returncode != 0:
|
||||
return False
|
||||
|
||||
# Try merging the tag in a dry-run to check if it can be merged cleanly
|
||||
print(f"Checking if {tag} can be merged cleanly into {branch}...")
|
||||
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
|
||||
|
||||
if merge_check.returncode != 0:
|
||||
print(f"Cannot merge {tag} cleanly into {branch}.")
|
||||
print("Merge conflicts would occur. Aborting merge.")
|
||||
run_command("git merge --abort")
|
||||
return False
|
||||
|
||||
# Abort the test merge
|
||||
run_command("git reset --hard HEAD")
|
||||
|
||||
# Now perform the actual merge and push to remote
|
||||
print(f"Merging {tag} into {branch}...")
|
||||
merge_result = run_command(f"git merge {tag} --no-edit")
|
||||
if merge_result.returncode != 0:
|
||||
print(f"Failed to merge {tag} into {branch}.")
|
||||
return False
|
||||
|
||||
print(f"Pushing changes to remote...")
|
||||
push_result = run_command(f"git push origin {branch}")
|
||||
if push_result.returncode != 0:
|
||||
print(f"Failed to push changes to remote.")
|
||||
return False
|
||||
|
||||
print(f"Successfully merged {tag} into {branch} and pushed to remote.")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Merge a tag into a branch on a GitHub repository.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s leanprover/mathlib4 stable v4.6.0 Merge tag v4.6.0 into stable branch
|
||||
|
||||
The script will:
|
||||
1. Clone the repository
|
||||
2. Check if the tag and branch exist
|
||||
3. Check if the tag can be merged cleanly into the branch
|
||||
4. Perform the merge and push to remote if possible
|
||||
"""
|
||||
)
|
||||
parser.add_argument("repo", help="GitHub repository in the format 'organization/repository'")
|
||||
parser.add_argument("branch", help="The target branch to merge into")
|
||||
parser.add_argument("tag", help="The tag to merge from")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create a temporary directory for the repository
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
# Clone the repository
|
||||
if not clone_repo(args.repo, temp_dir):
|
||||
sys.exit(1)
|
||||
|
||||
# Check if the tag can be merged and perform the merge
|
||||
if not check_and_merge(args.repo, args.branch, args.tag, temp_dir):
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
# Clean up the temporary directory
|
||||
print(f"Cleaning up temporary files...")
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -25,7 +25,10 @@ cp llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
|
||||
echo '
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
|
||||
#define SEM_FAILCRITICALERRORS 0x0001
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);' > stage1/include/clang/windows.h
|
||||
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
|
||||
#define CP_UTF8 65001
|
||||
__declspec(dllimport) __stdcall int SetConsoleOutputCP(unsigned int wCodePageID);' > stage1/include/clang/windows.h
|
||||
# COFF dependencies
|
||||
cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
|
||||
# runtime
|
||||
|
||||
@@ -7,6 +7,13 @@ import base64
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
# Import run_command from merge_remote.py
|
||||
from merge_remote import run_command
|
||||
|
||||
def debug(verbose, message):
|
||||
"""Print debug message if verbose mode is enabled."""
|
||||
if verbose:
|
||||
print(f" [DEBUG] {message}")
|
||||
|
||||
def parse_repos_config(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
@@ -33,10 +40,17 @@ def branch_exists(repo_url, branch, github_token):
|
||||
return response.status_code == 200
|
||||
|
||||
def tag_exists(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/refs/tags/{tag_name}"
|
||||
# Use /git/matching-refs/tags/ to get all matching tags
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/matching-refs/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
|
||||
if response.status_code != 200:
|
||||
return False
|
||||
|
||||
# Check if any of the returned refs exactly match our tag
|
||||
matching_tags = response.json()
|
||||
return any(tag["ref"] == f"refs/tags/{tag_name}" for tag in matching_tags)
|
||||
|
||||
def release_page_exists(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
|
||||
@@ -78,9 +92,12 @@ def parse_version(version_str):
|
||||
|
||||
def is_version_gte(version1, version2):
|
||||
"""Check if version1 >= version2, including proper handling of release candidates."""
|
||||
# Check if version1 is a nightly toolchain
|
||||
if version1.startswith("leanprover/lean4:nightly-"):
|
||||
return False
|
||||
return parse_version(version1) >= parse_version(version2)
|
||||
|
||||
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
|
||||
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token, verbose=False):
|
||||
# First get the commit SHA for the tag
|
||||
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
@@ -88,20 +105,57 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
|
||||
# Get tag's commit SHA
|
||||
tag_response = requests.get(f"{api_base}/git/refs/tags/{tag_name}", headers=headers)
|
||||
if tag_response.status_code != 200:
|
||||
debug(verbose, f"Could not fetch tag {tag_name}, status code: {tag_response.status_code}")
|
||||
return False
|
||||
tag_sha = tag_response.json()['object']['sha']
|
||||
|
||||
|
||||
# Handle both single object and array responses
|
||||
tag_data = tag_response.json()
|
||||
if isinstance(tag_data, list):
|
||||
# Find the exact matching tag in the list
|
||||
matching_tags = [tag for tag in tag_data if tag['ref'] == f'refs/tags/{tag_name}']
|
||||
if not matching_tags:
|
||||
debug(verbose, f"No matching tag found for {tag_name} in response list")
|
||||
return False
|
||||
tag_sha = matching_tags[0]['object']['sha']
|
||||
else:
|
||||
tag_sha = tag_data['object']['sha']
|
||||
|
||||
# Check if the tag is an annotated tag and get the actual commit SHA
|
||||
if tag_data.get('object', {}).get('type') == 'tag' or (
|
||||
isinstance(tag_data, list) and
|
||||
matching_tags and
|
||||
matching_tags[0].get('object', {}).get('type') == 'tag'):
|
||||
|
||||
# Get the commit that this tag points to
|
||||
tag_obj_response = requests.get(f"{api_base}/git/tags/{tag_sha}", headers=headers)
|
||||
if tag_obj_response.status_code == 200:
|
||||
tag_obj = tag_obj_response.json()
|
||||
if 'object' in tag_obj and tag_obj['object']['type'] == 'commit':
|
||||
commit_sha = tag_obj['object']['sha']
|
||||
debug(verbose, f"Tag is annotated. Resolved commit SHA: {commit_sha}")
|
||||
tag_sha = commit_sha # Use the actual commit SHA
|
||||
|
||||
# Get commits on stable branch containing this SHA
|
||||
commits_response = requests.get(
|
||||
f"{api_base}/commits?sha={stable_branch}&per_page=100",
|
||||
headers=headers
|
||||
)
|
||||
if commits_response.status_code != 200:
|
||||
debug(verbose, f"Could not fetch commits for branch {stable_branch}, status code: {commits_response.status_code}")
|
||||
return False
|
||||
|
||||
# Check if any commit in stable's history matches our tag's SHA
|
||||
stable_commits = [commit['sha'] for commit in commits_response.json()]
|
||||
return tag_sha in stable_commits
|
||||
|
||||
is_merged = tag_sha in stable_commits
|
||||
|
||||
debug(verbose, f"Tag SHA: {tag_sha}")
|
||||
debug(verbose, f"First 5 stable commits: {stable_commits[:5]}")
|
||||
debug(verbose, f"Total stable commits fetched: {len(stable_commits)}")
|
||||
if not is_merged:
|
||||
debug(verbose, f"Tag SHA not found in first {len(stable_commits)} commits of stable branch")
|
||||
|
||||
return is_merged
|
||||
|
||||
def is_release_candidate(version):
|
||||
return "-rc" in version
|
||||
@@ -135,52 +189,101 @@ def extract_org_repo_from_url(repo_url):
|
||||
return repo_url.replace("https://github.com/", "").rstrip("/")
|
||||
return repo_url
|
||||
|
||||
def get_next_version(version):
|
||||
"""Calculate the next version number, ignoring RC suffix."""
|
||||
# Strip v prefix and RC suffix if present
|
||||
base_version = strip_rc_suffix(version.lstrip('v'))
|
||||
major, minor, patch = map(int, base_version.split('.'))
|
||||
# Next version is always .0
|
||||
return f"v{major}.{minor + 1}.0"
|
||||
|
||||
def check_bump_branch_toolchain(url, bump_branch, github_token):
|
||||
"""Check if the lean-toolchain file in bump branch starts with either 'leanprover/lean4:nightly-' or the next version."""
|
||||
content = get_branch_content(url, bump_branch, "lean-toolchain", github_token)
|
||||
if content is None:
|
||||
print(f" ❌ No lean-toolchain file found in {bump_branch} branch")
|
||||
return False
|
||||
|
||||
# Extract the next version from the bump branch name (bump/v4.X.0)
|
||||
next_version = bump_branch.split('/')[1]
|
||||
|
||||
if not (content.startswith("leanprover/lean4:nightly-") or
|
||||
content.startswith(f"leanprover/lean4:{next_version}")):
|
||||
print(f" ❌ Bump branch toolchain should use either nightly or {next_version}, but found: {content}")
|
||||
return False
|
||||
|
||||
print(f" ✅ Bump branch correctly uses toolchain: {content}")
|
||||
return True
|
||||
|
||||
def pr_exists_with_title(repo_url, title, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + "/pulls"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
params = {'state': 'open'}
|
||||
response = requests.get(api_url, headers=headers, params=params)
|
||||
if response.status_code != 200:
|
||||
return None
|
||||
pull_requests = response.json()
|
||||
for pr in pull_requests:
|
||||
if pr['title'] == title:
|
||||
return pr['number'], pr['html_url']
|
||||
return None
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
|
||||
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose debugging output")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Dry run mode (no actions taken)")
|
||||
args = parser.parse_args()
|
||||
|
||||
github_token = get_github_token()
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python3 release_checklist.py <toolchain>")
|
||||
sys.exit(1)
|
||||
|
||||
toolchain = sys.argv[1]
|
||||
toolchain = args.toolchain
|
||||
verbose = args.verbose
|
||||
# dry_run = args.dry_run # Not used yet but available for future implementation
|
||||
|
||||
stripped_toolchain = strip_rc_suffix(toolchain)
|
||||
lean_repo_url = "https://github.com/leanprover/lean4"
|
||||
|
||||
# Preliminary checks
|
||||
# Track repository status
|
||||
repo_status = {} # Will store True for success, False for failure
|
||||
|
||||
# Preliminary checks for lean4 itself
|
||||
print("\nPerforming preliminary checks...")
|
||||
lean4_success = True
|
||||
|
||||
# Check for branch releases/v4.Y.0
|
||||
version_major, version_minor, _ = map(int, stripped_toolchain.lstrip('v').split('.'))
|
||||
branch_name = f"releases/v{version_major}.{version_minor}.0"
|
||||
if branch_exists(lean_repo_url, branch_name, github_token):
|
||||
print(f" ✅ Branch {branch_name} exists")
|
||||
|
||||
# Check CMake version settings
|
||||
check_cmake_version(lean_repo_url, branch_name, version_major, version_minor, github_token)
|
||||
else:
|
||||
if not branch_exists(lean_repo_url, branch_name, github_token):
|
||||
print(f" ❌ Branch {branch_name} does not exist")
|
||||
|
||||
# Check for tag v4.X.Y(-rcZ)
|
||||
if tag_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ✅ Tag {toolchain} exists")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Branch {branch_name} exists")
|
||||
# Check CMake version settings
|
||||
if not check_cmake_version(lean_repo_url, branch_name, version_major, version_minor, github_token):
|
||||
lean4_success = False
|
||||
|
||||
# Check for tag and release page
|
||||
if not tag_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ❌ Tag {toolchain} does not exist.")
|
||||
|
||||
# Check for release page
|
||||
if release_page_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ✅ Release page for {toolchain} exists")
|
||||
|
||||
# Check the first line of the release notes
|
||||
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
|
||||
if release_notes and release_notes.splitlines()[0].strip() == toolchain:
|
||||
print(f" ✅ Release notes look good.")
|
||||
else:
|
||||
previous_minor_version = version_minor - 1
|
||||
previous_stable_branch = f"releases/v{version_major}.{previous_minor_version}.0"
|
||||
previous_release = f"v{version_major}.{previous_minor_version}.0"
|
||||
print(f" ❌ Release notes not published. Please run `script/release_notes.py {previous_release}` on branch `{previous_stable_branch}`.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Tag {toolchain} exists")
|
||||
|
||||
if not release_page_exists(lean_repo_url, toolchain, github_token):
|
||||
print(f" ❌ Release page for {toolchain} does not exist")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release page for {toolchain} exists")
|
||||
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
|
||||
if not (release_notes and toolchain in release_notes.splitlines()[0].strip()):
|
||||
previous_minor_version = version_minor - 1
|
||||
previous_release = f"v{version_major}.{previous_minor_version}.0"
|
||||
print(f" ❌ Release notes not published. Please run `script/release_notes.py --since {previous_release}` on branch `{branch_name}`.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release notes look good.")
|
||||
|
||||
repo_status["lean4"] = lean4_success
|
||||
|
||||
# Load repositories and perform further checks
|
||||
print("\nChecking repositories...")
|
||||
@@ -191,37 +294,127 @@ def main():
|
||||
for repo in repos:
|
||||
name = repo["name"]
|
||||
url = repo["url"]
|
||||
org_repo = extract_org_repo_from_url(url)
|
||||
branch = repo["branch"]
|
||||
check_stable = repo["stable-branch"]
|
||||
check_tag = repo.get("toolchain-tag", True)
|
||||
check_bump = repo.get("bump-branch", False)
|
||||
dependencies = repo.get("dependencies", [])
|
||||
|
||||
print(f"\nRepository: {name}")
|
||||
|
||||
# Check if any dependencies have failed
|
||||
failed_deps = [dep for dep in dependencies if dep in repo_status and not repo_status[dep]]
|
||||
if failed_deps:
|
||||
print(f" 🟡 Dependencies not ready: {', '.join(failed_deps)}")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
|
||||
# Initialize success flag for this repo
|
||||
success = True
|
||||
|
||||
# Check if branch is on at least the target toolchain
|
||||
lean_toolchain_content = get_branch_content(url, branch, "lean-toolchain", github_token)
|
||||
if lean_toolchain_content is None:
|
||||
print(f" ❌ No lean-toolchain file found in {branch} branch")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
|
||||
|
||||
on_target_toolchain = is_version_gte(lean_toolchain_content.strip(), toolchain)
|
||||
if not on_target_toolchain:
|
||||
print(f" ❌ Not on target toolchain (needs ≥ {toolchain}, but {branch} is on {lean_toolchain_content.strip()})")
|
||||
pr_title = f"chore: bump toolchain to {toolchain}"
|
||||
pr_info = pr_exists_with_title(url, pr_title, github_token)
|
||||
if pr_info:
|
||||
pr_number, pr_url = pr_info
|
||||
print(f" ✅ PR with title '{pr_title}' exists: #{pr_number} ({pr_url})")
|
||||
else:
|
||||
print(f" ❌ PR with title '{pr_title}' does not exist")
|
||||
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
print(f" ✅ On compatible toolchain (>= {toolchain})")
|
||||
|
||||
# Only check for tag if toolchain-tag is true
|
||||
if check_tag:
|
||||
if not tag_exists(url, toolchain, github_token):
|
||||
print(f" ❌ Tag {toolchain} does not exist. Run `script/push_repo_release_tag.py {extract_org_repo_from_url(url)} {branch} {toolchain}`.")
|
||||
continue
|
||||
tag_exists_initially = tag_exists(url, toolchain, github_token)
|
||||
if not tag_exists_initially:
|
||||
if args.dry_run:
|
||||
print(f" ❌ Tag {toolchain} does not exist. Run `script/push_repo_release_tag.py {org_repo} {branch} {toolchain}`.")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
else:
|
||||
print(f" … Tag {toolchain} does not exist. Running `script/push_repo_release_tag.py {org_repo} {branch} {toolchain}`...")
|
||||
|
||||
# Run the script to create the tag
|
||||
subprocess.run(["script/push_repo_release_tag.py", org_repo, branch, toolchain])
|
||||
|
||||
# Check again if the tag exists now
|
||||
if not tag_exists(url, toolchain, github_token):
|
||||
print(f" ❌ Manual intervention required.")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
|
||||
# This will print in all successful cases - whether tag existed initially or was created successfully
|
||||
print(f" ✅ Tag {toolchain} exists")
|
||||
|
||||
# Only check merging into stable if stable-branch is true and not a release candidate
|
||||
if check_stable and not is_release_candidate(toolchain):
|
||||
if not is_merged_into_stable(url, toolchain, "stable", github_token):
|
||||
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
|
||||
org_repo = extract_org_repo_from_url(url)
|
||||
print(f" ❌ Tag {toolchain} is not merged into stable")
|
||||
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
print(f" ✅ Tag {toolchain} is merged into stable")
|
||||
|
||||
if check_bump:
|
||||
next_version = get_next_version(toolchain)
|
||||
bump_branch = f"bump/{next_version}"
|
||||
if not branch_exists(url, bump_branch, github_token):
|
||||
if args.dry_run:
|
||||
print(f" ❌ Bump branch {bump_branch} does not exist. Run `gh api -X POST /repos/{org_repo}/git/refs -f ref=refs/heads/{bump_branch} -f sha=$(gh api /repos/{org_repo}/git/refs/heads/{branch} --jq .object.sha)` to create it.")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
print(f" … Bump branch {bump_branch} does not exist. Creating it...")
|
||||
result = run_command(f"gh api -X POST /repos/{org_repo}/git/refs -f ref=refs/heads/{bump_branch} -f sha=$(gh api /repos/{org_repo}/git/refs/heads/{branch} --jq .object.sha)", check=False)
|
||||
if result.returncode != 0:
|
||||
print(f" ❌ Failed to create bump branch {bump_branch}")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
print(f" ✅ Bump branch {bump_branch} exists")
|
||||
if not check_bump_branch_toolchain(url, bump_branch, github_token):
|
||||
repo_status[name] = False
|
||||
continue
|
||||
|
||||
repo_status[name] = success
|
||||
|
||||
# Final check for lean4 master branch
|
||||
print("\nChecking lean4 master branch configuration...")
|
||||
next_version = get_next_version(toolchain)
|
||||
next_minor = int(next_version.split('.')[1])
|
||||
|
||||
cmake_content = get_branch_content(lean_repo_url, "master", "src/CMakeLists.txt", github_token)
|
||||
if cmake_content is None:
|
||||
print(" ❌ Could not retrieve CMakeLists.txt from master")
|
||||
else:
|
||||
cmake_lines = cmake_content.splitlines()
|
||||
# Find the actual minor version in CMakeLists.txt
|
||||
for line in cmake_lines:
|
||||
if line.strip().startswith("set(LEAN_VERSION_MINOR "):
|
||||
actual_minor = int(line.split()[-1].rstrip(")"))
|
||||
version_minor_correct = actual_minor >= next_minor
|
||||
break
|
||||
else:
|
||||
version_minor_correct = False
|
||||
|
||||
is_release_correct = any(
|
||||
l.strip().startswith("set(LEAN_VERSION_IS_RELEASE 0)")
|
||||
for l in cmake_lines
|
||||
)
|
||||
|
||||
if not (version_minor_correct and is_release_correct):
|
||||
print(" ❌ lean4 needs a \"begin dev cycle\" PR")
|
||||
else:
|
||||
print(" ✅ lean4 master branch is configured for next development cycle")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -5,6 +5,7 @@ import re
|
||||
import json
|
||||
import requests
|
||||
import subprocess
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
from git import Repo
|
||||
|
||||
@@ -64,21 +65,39 @@ def format_markdown_description(pr_number, description):
|
||||
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
|
||||
return f"{link} {description}"
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
sys.stderr.write("Usage: script.py <git-tag>\n")
|
||||
sys.exit(1)
|
||||
def commit_types():
|
||||
# see doc/dev/commit_convention.md
|
||||
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
|
||||
|
||||
def count_commit_types(commits):
|
||||
counts = {
|
||||
'total': len(commits),
|
||||
}
|
||||
for commit_type in commit_types():
|
||||
counts[commit_type] = 0
|
||||
|
||||
for _, first_line, _ in commits:
|
||||
for commit_type in commit_types():
|
||||
if first_line.startswith(f'{commit_type}:'):
|
||||
counts[commit_type] += 1
|
||||
break
|
||||
|
||||
return counts
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Generate release notes from Git commits')
|
||||
parser.add_argument('--since', required=True, help='Git tag to generate release notes since')
|
||||
args = parser.parse_args()
|
||||
|
||||
tag = sys.argv[1]
|
||||
try:
|
||||
repo = Repo(".")
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"Error opening Git repository: {e}\n")
|
||||
sys.exit(1)
|
||||
|
||||
commits = get_commits_since_tag(repo, tag)
|
||||
commits = get_commits_since_tag(repo, args.since)
|
||||
|
||||
sys.stderr.write(f"Found {len(commits)} commits since tag {tag}:\n")
|
||||
sys.stderr.write(f"Found {len(commits)} commits since tag {args.since}:\n")
|
||||
for commit_hash, first_line, _ in commits:
|
||||
sys.stderr.write(f"- {commit_hash}: {first_line}\n")
|
||||
|
||||
@@ -92,14 +111,18 @@ def main():
|
||||
pr_number = check_pr_number(first_line)
|
||||
|
||||
if not pr_number:
|
||||
sys.stderr.write(f"No PR number found in {first_line}\n")
|
||||
sys.stderr.write(f"No PR number found in commit:\n{commit_hash}\n{first_line}\n")
|
||||
continue
|
||||
|
||||
# Remove the first line from the full_message for further processing
|
||||
body = full_message[len(first_line):].strip()
|
||||
|
||||
paragraphs = body.split('\n\n')
|
||||
second_paragraph = paragraphs[0] if len(paragraphs) > 0 else ""
|
||||
description = paragraphs[0] if len(paragraphs) > 0 else ""
|
||||
|
||||
# If there's a third paragraph and second ends with colon, include it
|
||||
if len(paragraphs) > 1 and description.endswith(':'):
|
||||
description = description + '\n\n' + paragraphs[1]
|
||||
|
||||
labels = fetch_pr_labels(pr_number)
|
||||
|
||||
@@ -109,7 +132,7 @@ def main():
|
||||
|
||||
report_errors = first_line.startswith("feat:") or first_line.startswith("fix:")
|
||||
|
||||
if not second_paragraph.startswith("This PR "):
|
||||
if not description.startswith("This PR "):
|
||||
if report_errors:
|
||||
sys.stderr.write(f"No PR description found in commit:\n{commit_hash}\n{first_line}\n{body}\n\n")
|
||||
fallback_description = re.sub(r":$", "", first_line.split(" ", 1)[1]).rsplit(" (#", 1)[0]
|
||||
@@ -117,7 +140,7 @@ def main():
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
markdown_description = format_markdown_description(pr_number, second_paragraph.replace("This PR ", ""))
|
||||
markdown_description = format_markdown_description(pr_number, description.replace("This PR ", ""))
|
||||
|
||||
changelog_labels = [label for label in labels if label.startswith("changelog-")]
|
||||
if len(changelog_labels) > 1:
|
||||
@@ -132,6 +155,14 @@ def main():
|
||||
for label in changelog_labels:
|
||||
changelog[label].append((pr_number, markdown_description))
|
||||
|
||||
# Add commit type counting
|
||||
counts = count_commit_types(commits)
|
||||
print(f"For this release, {counts['total']} changes landed. "
|
||||
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
|
||||
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
|
||||
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
|
||||
f"and {counts['style'] + counts['chore']} other changes.\n")
|
||||
|
||||
section_order = sort_sections_order()
|
||||
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
|
||||
|
||||
@@ -139,7 +170,12 @@ def main():
|
||||
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
|
||||
print(f"## {section_title}\n")
|
||||
for _, entry in sorted(entries, key=lambda x: x[0]):
|
||||
print(f"* {entry}\n")
|
||||
# Split entry into lines and indent all lines after the first
|
||||
lines = entry.splitlines()
|
||||
print(f"* {lines[0]}")
|
||||
for line in lines[1:]:
|
||||
print(f" {line}")
|
||||
print() # Empty line after each entry
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
repositories:
|
||||
- name: Batteries
|
||||
- name: batteries
|
||||
url: https://github.com/leanprover-community/batteries
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
branch: main
|
||||
bump-branch: true
|
||||
dependencies: []
|
||||
|
||||
- name: lean4checker
|
||||
@@ -13,6 +14,13 @@ repositories:
|
||||
branch: master
|
||||
dependencies: []
|
||||
|
||||
- name: quote4
|
||||
url: https://github.com/leanprover-community/quote4
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
branch: master
|
||||
dependencies: []
|
||||
|
||||
- name: doc-gen4
|
||||
url: https://github.com/leanprover/doc-gen4
|
||||
toolchain-tag: true
|
||||
@@ -20,14 +28,21 @@ repositories:
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: Verso
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: Cli
|
||||
- name: reference-manual
|
||||
url: https://github.com/leanprover/reference-manual
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: [verso]
|
||||
|
||||
- name: lean4-cli
|
||||
url: https://github.com/leanprover/lean4-cli
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
@@ -42,7 +57,7 @@ repositories:
|
||||
dependencies:
|
||||
- Batteries
|
||||
|
||||
- name: Aesop
|
||||
- name: aesop
|
||||
url: https://github.com/leanprover-community/aesop
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
@@ -55,7 +70,9 @@ repositories:
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
dependencies:
|
||||
- Cli
|
||||
- Batteries
|
||||
|
||||
- name: plausible
|
||||
url: https://github.com/leanprover-community/plausible
|
||||
@@ -64,11 +81,12 @@ repositories:
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: Mathlib
|
||||
- name: mathlib4
|
||||
url: https://github.com/leanprover-community/mathlib4
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
branch: master
|
||||
bump-branch: true
|
||||
dependencies:
|
||||
- Aesop
|
||||
- ProofWidgets4
|
||||
@@ -76,8 +94,9 @@ repositories:
|
||||
- Batteries
|
||||
- doc-gen4
|
||||
- import-graph
|
||||
- plausible
|
||||
|
||||
- name: REPL
|
||||
- name: repl
|
||||
url: https://github.com/leanprover-community/repl
|
||||
toolchain-tag: true
|
||||
stable-branch: true
|
||||
|
||||
142
script/release_steps.py
Executable file
142
script/release_steps.py
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Generate release steps script for Lean4 repositories.
|
||||
|
||||
This script helps automate the release process for Lean4 and its dependent repositories
|
||||
by generating step-by-step instructions for updating toolchains, creating tags,
|
||||
and managing branches.
|
||||
|
||||
Usage:
|
||||
python3 release_steps.py <version> <repo>
|
||||
|
||||
Arguments:
|
||||
version: The version to set in the lean-toolchain file (e.g., v4.6.0)
|
||||
repo: A substring of the repository name as specified in release_repos.yml
|
||||
|
||||
Example:
|
||||
python3 release_steps.py v4.6.0 mathlib
|
||||
python3 release_steps.py v4.6.0 batt
|
||||
|
||||
The script reads repository configurations from release_repos.yml in the same directory.
|
||||
Each repository may have specific requirements for:
|
||||
- Branch management
|
||||
- Toolchain updates
|
||||
- Dependency updates
|
||||
- Tagging conventions
|
||||
- Stable branch handling
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import yaml
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
def load_repos_config(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
return yaml.safe_load(f)["repositories"]
|
||||
|
||||
def find_repo(repo_substring, config):
|
||||
pattern = re.compile(re.escape(repo_substring), re.IGNORECASE)
|
||||
matching_repos = [r for r in config if pattern.search(r["name"])]
|
||||
if not matching_repos:
|
||||
print(f"Error: No repository matching '{repo_substring}' found in configuration.")
|
||||
sys.exit(1)
|
||||
if len(matching_repos) > 1:
|
||||
print(f"Error: Multiple repositories matching '{repo_substring}' found in configuration: {', '.join(r['name'] for r in matching_repos)}")
|
||||
sys.exit(1)
|
||||
return matching_repos[0]
|
||||
|
||||
def generate_script(repo, version, config):
|
||||
repo_config = find_repo(repo, config)
|
||||
repo_name = repo_config['name']
|
||||
repo_url = repo_config['url']
|
||||
# Extract the last component of the URL, removing the .git extension if present
|
||||
repo_dir = repo_url.split('/')[-1].replace('.git', '')
|
||||
default_branch = repo_config.get("branch", "main")
|
||||
dependencies = repo_config.get("dependencies", [])
|
||||
requires_tagging = repo_config.get("toolchain-tag", True)
|
||||
has_stable_branch = repo_config.get("stable-branch", True)
|
||||
|
||||
script_lines = [
|
||||
f"cd {repo_dir}",
|
||||
"git fetch",
|
||||
f"git checkout {default_branch} && git pull",
|
||||
f"git checkout -b bump_to_{version}",
|
||||
f"echo leanprover/lean4:{version} > lean-toolchain",
|
||||
]
|
||||
|
||||
# Special cases for specific repositories
|
||||
if repo_name == "REPL":
|
||||
script_lines.extend([
|
||||
"lake update",
|
||||
"cd test/Mathlib",
|
||||
f"perl -pi -e 's/rev = \"v\\d+\\.\\d+\\.\\d+(-rc\\d+)?\"/rev = \"{version}\"/g' lakefile.toml",
|
||||
f"echo leanprover/lean4:{version} > lean-toolchain",
|
||||
"lake update",
|
||||
"cd ../..",
|
||||
"./test.sh"
|
||||
])
|
||||
elif dependencies:
|
||||
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
|
||||
script_lines.append("lake update")
|
||||
|
||||
script_lines.append("")
|
||||
|
||||
script_lines.extend([
|
||||
f'git commit -am "chore: bump toolchain to {version}"',
|
||||
""
|
||||
])
|
||||
|
||||
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
|
||||
script_lines.extend([
|
||||
"echo 'This repo has nightly-testing infrastructure'",
|
||||
f"git merge origin/bump/{version.split('-rc')[0]}",
|
||||
"echo 'Please resolve any conflicts.'",
|
||||
""
|
||||
])
|
||||
if repo_name != "Mathlib":
|
||||
script_lines.extend([
|
||||
"lake build && if lake check-test; then lake test; fi",
|
||||
""
|
||||
])
|
||||
|
||||
script_lines.extend([
|
||||
'gh pr create --title "chore: bump toolchain to ' + version + '" --body ""',
|
||||
"echo 'Please review the PR and merge it.'",
|
||||
""
|
||||
])
|
||||
|
||||
return "\n".join(script_lines)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate release steps script for Lean4 repositories.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
%(prog)s v4.6.0 mathlib Generate steps for updating Mathlib to v4.6.0
|
||||
%(prog)s v4.6.0 batt Generate steps for updating Batteries to v4.6.0
|
||||
|
||||
The script will generate shell commands to:
|
||||
1. Update the lean-toolchain file
|
||||
2. Create appropriate branches and commits
|
||||
3. Create pull requests
|
||||
|
||||
(Note that the steps of creating toolchain version tags, and merging these into `stable` branches,
|
||||
are handled by `script/release_checklist.py`.)
|
||||
"""
|
||||
)
|
||||
parser.add_argument("version", help="The version to set in the lean-toolchain file (e.g., v4.6.0)")
|
||||
parser.add_argument("repo", help="A substring of the repository name as specified in release_repos.yml")
|
||||
args = parser.parse_args()
|
||||
|
||||
config_path = os.path.join(os.path.dirname(__file__), "release_repos.yml")
|
||||
config = load_repos_config(config_path)
|
||||
|
||||
script = generate_script(args.repo, args.version, config)
|
||||
print(script)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 16)
|
||||
set(LEAN_VERSION_MINOR 20)
|
||||
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'")
|
||||
@@ -20,6 +20,11 @@ if (LEAN_SPECIAL_VERSION_DESC)
|
||||
elseif (NOT LEAN_VERSION_IS_RELEASE)
|
||||
string(APPEND LEAN_VERSION_STRING "-pre")
|
||||
endif()
|
||||
if (LEAN_VERSION_IS_RELEASE)
|
||||
set(LEAN_MANUAL_ROOT "https://lean-lang.org/doc/reference/${LEAN_VERSION_STRING}/")
|
||||
else()
|
||||
set(LEAN_MANUAL_ROOT "")
|
||||
endif()
|
||||
|
||||
set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform")
|
||||
if (NOT LEAN_PLATFORM_TARGET)
|
||||
@@ -69,12 +74,13 @@ option(TRACK_LIVE_EXPRS "TRACK_LIVE_EXPRS" OFF)
|
||||
option(CUSTOM_ALLOCATORS "CUSTOM_ALLOCATORS" ON)
|
||||
option(SAVE_SNAPSHOT "SAVE_SNAPSHOT" ON)
|
||||
option(SAVE_INFO "SAVE_INFO" ON)
|
||||
option(SMALL_ALLOCATOR "SMALL_ALLOCATOR" ON)
|
||||
option(SMALL_ALLOCATOR "SMALL_ALLOCATOR" OFF)
|
||||
option(MMAP "MMAP" ON)
|
||||
option(LAZY_RC "LAZY_RC" OFF)
|
||||
option(RUNTIME_STATS "RUNTIME_STATS" OFF)
|
||||
option(BSYMBOLIC "Link with -Bsymbolic to reduce call overhead in shared libraries (Linux)" ON)
|
||||
option(USE_GMP "USE_GMP" ON)
|
||||
option(USE_MIMALLOC "use mimalloc" ON)
|
||||
|
||||
# development-specific options
|
||||
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
|
||||
@@ -87,6 +93,11 @@ if ("${LAZY_RC}" MATCHES "ON")
|
||||
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
|
||||
endif()
|
||||
|
||||
if (USE_MIMALLOC)
|
||||
set(SMALL_ALLOCATOR OFF)
|
||||
set(LEAN_MIMALLOC "#define LEAN_MIMALLOC")
|
||||
endif()
|
||||
|
||||
if ("${SMALL_ALLOCATOR}" MATCHES "ON")
|
||||
set(LEAN_SMALL_ALLOCATOR "#define LEAN_SMALL_ALLOCATOR")
|
||||
endif()
|
||||
@@ -144,11 +155,12 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
# do not import the world from windows.h using appropriately named flag
|
||||
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D WIN32_LEAN_AND_MEAN")
|
||||
# DLLs must go next to executables on Windows
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
set(CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY "bin")
|
||||
else()
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/lean")
|
||||
set(CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY "lib/lean")
|
||||
endif()
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/lean")
|
||||
|
||||
# OSX default thread stack size is very small. Moreover, in Debug mode, each new stack frame consumes a lot of extra memory.
|
||||
@@ -454,20 +466,20 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=$ORIGIN/../lib:$ORIGIN/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
|
||||
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
|
||||
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
|
||||
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
|
||||
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
|
||||
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
@@ -514,7 +526,16 @@ if(USE_GITHASH)
|
||||
message(STATUS "git commit sha1: ${GIT_SHA1}")
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA1 "")
|
||||
if(USE_LAKE AND ${STAGE} EQUAL 0)
|
||||
# we need to embed *some* hash for Lake to invalidate stage 1 on stage 0 changes
|
||||
execute_process(
|
||||
COMMAND git ls-tree HEAD "${CMAKE_CURRENT_SOURCE_DIR}/../../stage0" --object-only
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
message(STATUS "stage0 sha1: ${GIT_SHA1}")
|
||||
else()
|
||||
set(GIT_SHA1 "")
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
|
||||
|
||||
@@ -533,6 +554,9 @@ else()
|
||||
set(LEAN_IS_STAGE0 "#define LEAN_IS_STAGE0 0")
|
||||
endif()
|
||||
configure_file("${LEAN_SOURCE_DIR}/config.h.in" "${LEAN_BINARY_DIR}/include/lean/config.h")
|
||||
if (USE_MIMALLOC)
|
||||
file(COPY "${LEAN_BINARY_DIR}/../mimalloc/src/mimalloc/include/mimalloc.h" DESTINATION "${LEAN_BINARY_DIR}/include/lean")
|
||||
endif()
|
||||
install(DIRECTORY ${LEAN_BINARY_DIR}/include/ DESTINATION include)
|
||||
configure_file(${LEAN_SOURCE_DIR}/lean.mk.in ${LEAN_BINARY_DIR}/share/lean/lean.mk)
|
||||
install(DIRECTORY ${LEAN_BINARY_DIR}/share/ DESTINATION share)
|
||||
@@ -541,6 +565,9 @@ 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
|
||||
|
||||
# Lean code only needs this one include
|
||||
string(APPEND LEANC_OPTS " -I${CMAKE_BINARY_DIR}/include")
|
||||
|
||||
# Use CMake profile C++ flags for building Lean libraries, but do not embed in `leanc`
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
|
||||
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
|
||||
@@ -699,12 +726,12 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
add_custom_target(lake_lib ALL
|
||||
add_custom_target(lake_lib
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS leanshared
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
|
||||
VERBATIM)
|
||||
add_custom_target(lake_shared ALL
|
||||
add_custom_target(lake_shared
|
||||
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
|
||||
DEPENDS lake_lib
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
|
||||
@@ -753,7 +780,12 @@ add_custom_target(clean-olean
|
||||
DEPENDS clean-stdlib)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
|
||||
PATTERN temp EXCLUDE)
|
||||
PATTERN temp
|
||||
PATTERN "*.export"
|
||||
PATTERN "*.hash"
|
||||
PATTERN "*.trace"
|
||||
PATTERN "*.rsp"
|
||||
EXCLUDE)
|
||||
|
||||
# symlink source into expected installation location for go-to-definition, if file system allows it
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
|
||||
@@ -784,10 +816,32 @@ if(LEAN_INSTALL_PREFIX)
|
||||
endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "\\\$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
|
||||
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
|
||||
|
||||
# hacky
|
||||
function(toml_escape IN OUTVAR)
|
||||
if(IN)
|
||||
string(STRIP "${IN}" OUT)
|
||||
string(REPLACE " " "\", \"" OUT "${OUT}")
|
||||
set(${OUTVAR} "\"${OUT}\"" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
|
||||
set(LEANC_OPTS_TOML "${LEANC_OPTS} ${LEANC_EXTRA_CC_FLAGS} ${LEANC_INTERNAL_FLAGS}")
|
||||
set(LINK_OPTS_TOML "${LEANC_INTERNAL_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/lib/lean ${LEAN_EXTRA_LINKER_FLAGS}")
|
||||
|
||||
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
|
||||
toml_escape("${LEANC_OPTS_TOML}" LEANC_OPTS_TOML)
|
||||
toml_escape("${LINK_OPTS_TOML}" LINK_OPTS_TOML)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(LAKE_LIB_PREFIX "lib")
|
||||
endif()
|
||||
|
||||
if(USE_LAKE AND STAGE EQUAL 1)
|
||||
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
|
||||
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
|
||||
|
||||
@@ -38,3 +38,6 @@ import Init.Grind
|
||||
import Init.While
|
||||
import Init.Syntax
|
||||
import Init.Internal
|
||||
import Init.Try
|
||||
import Init.BinderNameHint
|
||||
import Init.Task
|
||||
|
||||
42
src/Init/BinderNameHint.lean
Normal file
42
src/Init/BinderNameHint.lean
Normal file
@@ -0,0 +1,42 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Prelude
|
||||
import Init.Tactics
|
||||
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
The expression `binderNameHint v binder e` defined to be `e`.
|
||||
|
||||
If it is used on the right-hand side of an equation that is used for rewriting by `rw` or `simp`,
|
||||
and `v` is a local variable, and `binder` is an expression that (after beta-reduction) is a binder
|
||||
(`fun w => …` or `∀ w, …`), then it will rename `v` to the name used in that binder, and remove
|
||||
the `binderNameHint`.
|
||||
|
||||
A typical use of this gadget would be as follows; the gadget ensures that after rewriting, the local
|
||||
variable is still `name`, and not `x`:
|
||||
```
|
||||
theorem all_eq_not_any_not (l : List α) (p : α → Bool) :
|
||||
l.all p = !l.any fun x => binderNameHint x p (!p x) := sorry
|
||||
|
||||
example (names : List String) : names.all (fun name => "Waldo".isPrefixOf name) = true := by
|
||||
rw [all_eq_not_any_not]
|
||||
-- ⊢ (!names.any fun name => !"Waldo".isPrefixOf name) = true
|
||||
```
|
||||
|
||||
If `binder` is not a binder, then the name of `v` attains a macro scope. This only matters when the
|
||||
resulting term is used in a non-hygienic way, e.g. in termination proofs for well-founded recursion.
|
||||
|
||||
This gadget is supported by
|
||||
* `simp`, `dsimp` and `rw` in the right-hand-side of an equation
|
||||
* `simp` in the assumptions of congruence rules
|
||||
|
||||
It is ineffective in other positions (hyptheses of rewrite rules) or when used by other tactics
|
||||
(e.g. `apply`).
|
||||
-/
|
||||
@[simp ↓]
|
||||
def binderNameHint {α : Sort u} {β : Sort v} {γ : Sort w} (v : α) (binder : β) (e : γ) : γ := e
|
||||
@@ -38,7 +38,8 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
|
||||
apply_dite f P (fun _ => x) (fun _ => y)
|
||||
|
||||
/-- 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
|
||||
@[simp] theorem dite_eq_ite [Decidable P] :
|
||||
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
|
||||
|
||||
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")]
|
||||
theorem ite_some_none_eq_none [Decidable P] :
|
||||
|
||||
@@ -175,7 +175,10 @@ theorem or_iff_not_imp_right : a ∨ b ↔ (¬b → a) := Decidable.or_iff_not_i
|
||||
|
||||
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_and_iff_not_or_not : ¬(a ∧ b) ↔ ¬a ∨ ¬b := Decidable.not_and_iff_not_or_not
|
||||
|
||||
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
|
||||
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
|
||||
|
||||
theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
|
||||
|
||||
@@ -195,7 +198,7 @@ end Classical
|
||||
/- Export for Mathlib compat. -/
|
||||
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
|
||||
|
||||
/-- Extract an element from a existential statement, using `Classical.choose`. -/
|
||||
/-- Extract an element from an existential statement, using `Classical.choose`. -/
|
||||
-- This enables projection notation.
|
||||
@[reducible] noncomputable def Exists.choose {p : α → Prop} (P : ∃ a, p a) : α := Classical.choose P
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Author: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
import Init.BinderNameHint
|
||||
|
||||
universe u v w
|
||||
|
||||
@@ -35,7 +36,17 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
|
||||
simp [h]
|
||||
rfl
|
||||
|
||||
/-- Extract the value from a `ForInStep`, ignoring whether it is `done` or `yield`. -/
|
||||
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m]
|
||||
(x : ρ) (b : β) (f : (a : α) → β → m (ForInStep β)) :
|
||||
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
|
||||
rfl
|
||||
|
||||
@[deprecated forIn_eq_forIn' (since := "2025-04-04")]
|
||||
abbrev forIn_eq_forin' := @forIn_eq_forIn'
|
||||
|
||||
/--
|
||||
Extracts the value from a `ForInStep`, ignoring whether it is `ForInStep.done` or `ForInStep.yield`.
|
||||
-/
|
||||
def ForInStep.value (x : ForInStep α) : α :=
|
||||
match x with
|
||||
| ForInStep.done b => b
|
||||
@@ -44,12 +55,28 @@ def ForInStep.value (x : ForInStep α) : α :=
|
||||
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
|
||||
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
|
||||
|
||||
/--
|
||||
Maps a function over a functor, with parameters swapped so that the function comes last.
|
||||
|
||||
This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.
|
||||
-/
|
||||
@[reducible]
|
||||
def Functor.mapRev {f : Type u → Type v} [Functor f] {α β : Type u} : f α → (α → β) → f β :=
|
||||
fun a f => f <$> a
|
||||
|
||||
@[inherit_doc Functor.mapRev]
|
||||
infixr:100 " <&> " => Functor.mapRev
|
||||
|
||||
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»]
|
||||
|
||||
/--
|
||||
Discards the value in a functor, retaining the functor's structure.
|
||||
|
||||
Discarding values is especially useful when using `Applicative` functors or `Monad`s to implement
|
||||
effects, and some operation should be carried out only for its effects. In `do`-notation, statements
|
||||
whose values are discarded must return `Unit`, and `discard` can be used to explicitly discard their
|
||||
values.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
|
||||
Functor.mapConst PUnit.unit x
|
||||
@@ -69,7 +96,7 @@ Error recovery and state can interact subtly. For example, the implementation of
|
||||
-/
|
||||
-- NB: List instance is in mathlib. Once upstreamed, add
|
||||
-- * `List`, where `failure` is the empty list and `<|>` concatenates.
|
||||
class Alternative (f : Type u → Type v) extends Applicative f : Type (max (u+1) v) where
|
||||
class Alternative (f : Type u → Type v) : Type (max (u+1) v) extends Applicative f where
|
||||
/--
|
||||
Produces an empty collection or recoverable failure. The `<|>` operator collects values or recovers
|
||||
from failures. See `Alternative` for more details.
|
||||
@@ -112,6 +139,13 @@ instance : ToBool Bool where
|
||||
| true => t
|
||||
| false => f
|
||||
|
||||
/--
|
||||
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns it and ignores
|
||||
`y`; otherwise, runs `y` and returns its result.
|
||||
|
||||
This a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
|
||||
operator.
|
||||
-/
|
||||
@[macro_inline] def orM {m : Type u → Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
|
||||
let b ← x
|
||||
match toBool b with
|
||||
@@ -120,6 +154,15 @@ instance : ToBool Bool where
|
||||
|
||||
infixr:30 " <||> " => orM
|
||||
|
||||
recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»]
|
||||
|
||||
/--
|
||||
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns `y`; otherwise,
|
||||
returns the original result of `x`.
|
||||
|
||||
This a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
|
||||
operator.
|
||||
-/
|
||||
@[macro_inline] def andM {m : Type u → Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
|
||||
let b ← x
|
||||
match toBool b with
|
||||
@@ -128,7 +171,12 @@ infixr:30 " <||> " => orM
|
||||
|
||||
infixr:35 " <&&> " => andM
|
||||
|
||||
@[macro_inline] def notM {m : Type → Type v} [Applicative m] (x : m Bool) : m Bool :=
|
||||
recommended_spelling "andM" for "<&&>" in [andM, «term_<&&>_»]
|
||||
|
||||
/--
|
||||
Runs a monadic action and returns the negation of its result.
|
||||
-/
|
||||
@[macro_inline] def notM {m : Type → Type v} [Functor m] (x : m Bool) : m Bool :=
|
||||
not <$> x
|
||||
|
||||
/-!
|
||||
@@ -243,21 +291,61 @@ Using `control` means that `runInBase` can be used multiple times.
|
||||
-/
|
||||
|
||||
|
||||
/-- MonadControl is a way of stating that the monad `m` can be 'run inside' the monad `n`.
|
||||
|
||||
This is the same as [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl) in Haskell.
|
||||
To learn about `MonadControl`, see the comment above this docstring.
|
||||
/--
|
||||
A way to lift a computation from one monad to another while providing the lifted computation with a
|
||||
means of interpreting computations from the outer monad. This provides a means of lifting
|
||||
higher-order operations automatically.
|
||||
|
||||
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
|
||||
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
|
||||
`MonadControl` itself.
|
||||
-/
|
||||
-- This is the same as
|
||||
-- [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl)
|
||||
-- in Haskell.
|
||||
class MonadControl (m : semiOutParam (Type u → Type v)) (n : Type u → Type w) where
|
||||
/--
|
||||
A type that can be used to reconstruct both a returned value and any state used by the outer
|
||||
monad.
|
||||
-/
|
||||
stM : Type u → Type u
|
||||
/--
|
||||
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
|
||||
reverse lifting operator that can run an `n` action, returning a value and state together.
|
||||
-/
|
||||
liftWith : {α : Type u} → (({β : Type u} → n β → m (stM β)) → m α) → n α
|
||||
/--
|
||||
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
|
||||
outer monad. The extra state information is used to restore the results of effects from the
|
||||
reverse lift passed to `liftWith`'s parameter.
|
||||
-/
|
||||
restoreM : {α : Type u} → m (stM α) → n α
|
||||
|
||||
/-- Transitive closure of MonadControl. -/
|
||||
/--
|
||||
A way to lift a computation from one monad to another while providing the lifted computation with a
|
||||
means of interpreting computations from the outer monad. This provides a means of lifting
|
||||
higher-order operations automatically.
|
||||
|
||||
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
|
||||
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
|
||||
`MonadControl` itself.
|
||||
-/
|
||||
class MonadControlT (m : Type u → Type v) (n : Type u → Type w) where
|
||||
/--
|
||||
A type that can be used to reconstruct both a returned value and any state used by the outer
|
||||
monad.
|
||||
-/
|
||||
stM : Type u → Type u
|
||||
/--
|
||||
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
|
||||
reverse lifting operator that can run an `n` action, returning a value and state together.
|
||||
-/
|
||||
liftWith : {α : Type u} → (({β : Type u} → n β → m (stM β)) → m α) → n α
|
||||
/--
|
||||
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
|
||||
outer monad. The extra state information is used to restore the results of effects from the
|
||||
reverse lift passed to `liftWith`'s parameter.
|
||||
-/
|
||||
restoreM {α : Type u} : stM α → n α
|
||||
|
||||
export MonadControlT (stM liftWith restoreM)
|
||||
@@ -273,25 +361,48 @@ instance (m : Type u → Type v) [Pure m] : MonadControlT m m where
|
||||
liftWith f := f fun x => x
|
||||
restoreM x := pure x
|
||||
|
||||
/--
|
||||
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
|
||||
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
|
||||
required to return extra information that is required in order to reconstruct the reverse lift's
|
||||
effects in the outer monad; this extra information is determined by `stM`.
|
||||
|
||||
This function takes the inner monad as an explicit parameter. Use `control` to infer the monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def controlAt (m : Type u → Type v) {n : Type u → Type w} [MonadControlT m n] [Bind n] {α : Type u}
|
||||
(f : ({β : Type u} → n β → m (stM m n β)) → m (stM m n α)) : n α :=
|
||||
liftWith f >>= restoreM
|
||||
|
||||
/--
|
||||
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
|
||||
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
|
||||
required to return extra information that is required in order to reconstruct the reverse lift's
|
||||
effects in the outer monad; this extra information is determined by `stM`.
|
||||
|
||||
This function takes the inner monad as an implicit parameter. Use `controlAt` to specify it
|
||||
explicitly.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def control {m : Type u → Type v} {n : Type u → Type w} [MonadControlT m n] [Bind n] {α : Type u}
|
||||
(f : ({β : Type u} → n β → m (stM m n β)) → m (stM m n α)) : n α :=
|
||||
controlAt m f
|
||||
|
||||
/--
|
||||
Typeclass for the polymorphic `forM` operation described in the "do unchained" paper.
|
||||
Remark:
|
||||
- `γ` is a "container" type of elements of type `α`.
|
||||
- `α` is treated as an output parameter by the typeclass resolution procedure.
|
||||
That is, it tries to find an instance using only `m` and `γ`.
|
||||
Overloaded monadic iteration over some container type.
|
||||
|
||||
An instance of `ForM m γ α` describes how to iterate a monadic operator over a container of type `γ`
|
||||
with elements of type `α` in the monad `m`. The element type should be uniquely determined by the
|
||||
monad and the container.
|
||||
|
||||
Use `ForM.forIn` to construct a `ForIn` instance from a `ForM` instance, thus enabling the use of
|
||||
the `for` operator in `do`-notation.
|
||||
-/
|
||||
class ForM (m : Type u → Type v) (γ : Type w₁) (α : outParam (Type w₂)) where
|
||||
forM [Monad m] : γ → (α → m PUnit) → m PUnit
|
||||
/--
|
||||
Runs the monadic action `f` on each element of the collection `coll`.
|
||||
-/
|
||||
forM [Monad m] (coll : γ) (f : α → m PUnit) : m PUnit
|
||||
|
||||
export ForM (forM)
|
||||
|
||||
@@ -315,3 +426,7 @@ def Bind.bindLeft [Bind m] (f : α → m β) (ma : m α) : m β :=
|
||||
@[inherit_doc] infixr:55 " >=> " => Bind.kleisliRight
|
||||
@[inherit_doc] infixr:55 " <=< " => Bind.kleisliLeft
|
||||
@[inherit_doc] infixr:55 " =<< " => Bind.bindLeft
|
||||
|
||||
recommended_spelling "kleisliRight" for ">=>" in [Bind.kleisliRight, «term_>=>_»]
|
||||
recommended_spelling "kleisliLeft" for "<=<" in [Bind.kleisliLeft, «term_<=<_»]
|
||||
recommended_spelling "bindLeft" for "=<<" in [Bind.bindLeft, «term_=<<_»]
|
||||
|
||||
@@ -29,8 +29,11 @@ namespace EStateM
|
||||
|
||||
variable {ε σ α β : Type u}
|
||||
|
||||
/-- Alternative orElse operator that allows to select which exception should be used.
|
||||
The default is to use the first exception since the standard `orElse` uses the second. -/
|
||||
/--
|
||||
Alternative orElse operator that allows callers to select which exception should be used when both
|
||||
operations fail. The default is to use the first exception since the standard `orElse` uses the
|
||||
second.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def orElse' {δ} [Backtrackable δ σ] (x₁ x₂ : EStateM ε σ α) (useFirstEx := true) : EStateM ε σ α := fun s =>
|
||||
let d := Backtrackable.save s;
|
||||
@@ -54,6 +57,11 @@ instance : MonadFinally (EStateM ε σ) := {
|
||||
| Result.error e₂ s => Result.error e₂ s
|
||||
}
|
||||
|
||||
/--
|
||||
Converts a state monad action into a state monad action with exceptions.
|
||||
|
||||
The resulting action does not throw an exception.
|
||||
-/
|
||||
@[always_inline, inline] def fromStateM {ε σ α : Type} (x : StateM σ α) : EStateM ε σ α := fun s =>
|
||||
match x.run s with
|
||||
| (a, s') => EStateM.Result.ok a s'
|
||||
|
||||
@@ -13,10 +13,20 @@ import Init.Coe
|
||||
namespace Except
|
||||
variable {ε : Type u}
|
||||
|
||||
/--
|
||||
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : Except ε α :=
|
||||
Except.ok a
|
||||
|
||||
/--
|
||||
Transforms a successful result with a function, doing nothing when an exception is thrown.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).map toString = pure 2`
|
||||
* `(throw "Error" : Except String Nat).map toString = throw "Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map (f : α → β) : Except ε α → Except ε β
|
||||
| Except.error err => Except.error err
|
||||
@@ -27,36 +37,78 @@ protected def map (f : α → β) : Except ε α → Except ε β
|
||||
intro e
|
||||
simp [Except.map]; cases e <;> rfl
|
||||
|
||||
/--
|
||||
Transforms exceptions with a function, doing nothing on successful results.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).mapError (·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).mapError (·.length) = throw 5`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def mapError (f : ε → ε') : Except ε α → Except ε' α
|
||||
| Except.error err => Except.error <| f err
|
||||
| Except.ok v => Except.ok v
|
||||
|
||||
/--
|
||||
Sequences two operations that may throw exceptions, allowing the second to depend on the value
|
||||
returned by the first.
|
||||
|
||||
If the first operation throws an exception, then it is the result of the computation. If the first
|
||||
succeeds but the second throws an exception, then that exception is the result. If both succeed,
|
||||
then the result is the result of the second computation.
|
||||
|
||||
This is the implementation of the `>>=` operator for `Except ε`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (ma : Except ε α) (f : α → Except ε β) : Except ε β :=
|
||||
match ma with
|
||||
| Except.error err => Except.error err
|
||||
| Except.ok v => f v
|
||||
|
||||
/-- Returns true if the value is `Except.ok`, false otherwise. -/
|
||||
/-- Returns `true` if the value is `Except.ok`, `false` otherwise. -/
|
||||
@[always_inline, inline]
|
||||
protected def toBool : Except ε α → Bool
|
||||
| Except.ok _ => true
|
||||
| Except.error _ => false
|
||||
|
||||
@[inherit_doc Except.toBool]
|
||||
abbrev isOk : Except ε α → Bool := Except.toBool
|
||||
|
||||
/--
|
||||
Returns `none` if an exception was thrown, or `some` around the value on success.
|
||||
|
||||
Examples:
|
||||
* `(pure 10 : Except String Nat).toOption = some 10`
|
||||
* `(throw "Failure" : Except String Nat).toOption = none`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def toOption : Except ε α → Option α
|
||||
| Except.ok a => some a
|
||||
| Except.error _ => none
|
||||
|
||||
/--
|
||||
Handles exceptions thrown in the `Except ε` monad.
|
||||
|
||||
If `ma` is successful, its result is returned. If it throws an exception, then `handle` is invoked
|
||||
on the exception's value.
|
||||
|
||||
Examples:
|
||||
* `(pure 2 : Except String Nat).tryCatch (pure ·.length) = pure 2`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (pure ·.length) = pure 5`
|
||||
* `(throw "Error" : Except String Nat).tryCatch (fun x => throw ("E: " ++ x)) = throw "E: Error"`
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch (ma : Except ε α) (handle : ε → Except ε α) : Except ε α :=
|
||||
match ma with
|
||||
| Except.ok a => Except.ok a
|
||||
| Except.error e => handle e
|
||||
|
||||
/--
|
||||
Recovers from exceptions thrown in the `Except ε` monad. Typically used via the `<|>` operator.
|
||||
|
||||
`Except.tryCatch` is a related operator that allows the recovery procedure to depend on _which_
|
||||
exception was thrown.
|
||||
-/
|
||||
def orElseLazy (x : Except ε α) (y : Unit → Except ε α) : Except ε α :=
|
||||
match x with
|
||||
| Except.ok a => Except.ok a
|
||||
@@ -70,12 +122,26 @@ instance : Monad (Except ε) where
|
||||
|
||||
end Except
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
-/
|
||||
def ExceptT (ε : Type u) (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Except ε α)
|
||||
|
||||
/--
|
||||
Use a monadic action that may return an exception's value as an action in the transformed monad that
|
||||
may throw the corresponding exception.
|
||||
|
||||
This is the inverse of `ExceptT.run`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.mk {ε : Type u} {m : Type u → Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
|
||||
This is the inverse of `ExceptT.mk`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ExceptT.run {ε : Type u} {m : Type u → Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
|
||||
|
||||
@@ -83,25 +149,41 @@ namespace ExceptT
|
||||
|
||||
variable {ε : Type u} {m : Type u → Type v} [Monad m]
|
||||
|
||||
/--
|
||||
Returns the value `a` without throwing exceptions or having any other effect.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| pure (Except.ok a)
|
||||
|
||||
/--
|
||||
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bindCont {α β : Type u} (f : α → ExceptT ε m β) : Except ε α → m (Except ε β)
|
||||
| Except.ok a => f a
|
||||
| Except.error e => pure (Except.error e)
|
||||
|
||||
/--
|
||||
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
|
||||
operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α → ExceptT ε m β) : ExceptT ε m β :=
|
||||
ExceptT.mk <| ma >>= ExceptT.bindCont f
|
||||
|
||||
/--
|
||||
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map {α β : Type u} (f : α → β) (x : ExceptT ε m α) : ExceptT ε m β :=
|
||||
ExceptT.mk <| x >>= fun a => match a with
|
||||
| (Except.ok a) => pure <| Except.ok (f a)
|
||||
| (Except.error e) => pure <| Except.error e
|
||||
|
||||
/--
|
||||
Runs a computation from an underlying monad in the transformed monad with exceptions.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| Except.ok <$> t
|
||||
@@ -110,6 +192,9 @@ protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
|
||||
instance : MonadLift (Except ε) (ExceptT ε m) := ⟨fun e => ExceptT.mk <| pure e⟩
|
||||
instance : MonadLift m (ExceptT ε m) := ⟨ExceptT.lift⟩
|
||||
|
||||
/--
|
||||
Handles exceptions produced in the `ExceptT ε` transformer.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε → ExceptT ε m α) : ExceptT ε m α :=
|
||||
ExceptT.mk <| ma >>= fun res => match res with
|
||||
@@ -124,6 +209,11 @@ instance : Monad (ExceptT ε m) where
|
||||
bind := ExceptT.bind
|
||||
map := ExceptT.map
|
||||
|
||||
/--
|
||||
Transforms exceptions using the function `f`.
|
||||
|
||||
This is the `ExceptT` version of `Except.mapError`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → ExceptT ε' m α := fun x =>
|
||||
ExceptT.mk <| Except.mapError f <$> x
|
||||
@@ -150,8 +240,12 @@ instance (ε) : MonadExceptOf ε (Except ε) where
|
||||
namespace MonadExcept
|
||||
variable {ε : Type u} {m : Type v → Type w}
|
||||
|
||||
/-- Alternative orelse operator that allows to select which exception should be used.
|
||||
The default is to use the first exception since the standard `orelse` uses the second. -/
|
||||
/--
|
||||
An alternative unconditional error recovery operator that allows callers to specify which exception
|
||||
to throw in cases where both operations throw exceptions.
|
||||
|
||||
By default, the first is thrown, because the `<|>` operator throws the second.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
|
||||
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)
|
||||
@@ -171,13 +265,24 @@ instance (ε : Type u) (m : Type u → Type v) [Monad m] : MonadControl m (Excep
|
||||
liftWith f := liftM <| f fun x => x.run
|
||||
restoreM x := x
|
||||
|
||||
/--
|
||||
Monads that provide the ability to ensure an action happens, regardless of exceptions or other
|
||||
failures.
|
||||
|
||||
`MonadFinally.tryFinally'` is used to desugar `try ... finally ...` syntax.
|
||||
-/
|
||||
class MonadFinally (m : Type u → Type v) where
|
||||
/-- `tryFinally' x f` runs `x` and then the "finally" computation `f`.
|
||||
When `x` succeeds with `a : α`, `f (some a)` is returned. If `x` fails
|
||||
for `m`'s definition of failure, `f none` is returned. Hence `tryFinally'`
|
||||
can be thought of as performing the same role as a `finally` block in
|
||||
an imperative programming language. -/
|
||||
tryFinally' {α β} : m α → (Option α → m β) → m (α × β)
|
||||
/--
|
||||
Runs an action, ensuring that some other action always happens afterward.
|
||||
|
||||
More specifically, `tryFinally' x f` runs `x` and then the “finally” computation `f`. If `x`
|
||||
succeeds with some value `a : α`, `f (some a)` is returned. If `x` fails for `m`'s definition of
|
||||
failure, `f none` is returned.
|
||||
|
||||
`tryFinally'` can be thought of as performing the same role as a `finally` block in an imperative
|
||||
programming language.
|
||||
-/
|
||||
tryFinally' {α β} : (x : m α) → (f : Option α → m β) → m (α × β)
|
||||
|
||||
export MonadFinally (tryFinally')
|
||||
|
||||
|
||||
@@ -10,19 +10,37 @@ import Init.Control.Lawful.Basic
|
||||
The Exception monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
|
||||
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
|
||||
style. This has different performance characteristics from `ExceptT ε`.
|
||||
-/
|
||||
def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type u) → (α → m β) → (ε → m β) → m β
|
||||
|
||||
namespace ExceptCpsT
|
||||
|
||||
/--
|
||||
Use a monadic action that may throw an exception as an action that may return an exception's value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
|
||||
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
|
||||
|
||||
set_option linter.unusedVariables false in -- `s` unused
|
||||
/--
|
||||
Use a monadic action that may throw an exception by providing explicit success and failure
|
||||
continuations.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α → m β) (error : ε → m β) : m β :=
|
||||
x _ ok error
|
||||
|
||||
/--
|
||||
Returns the value of a computation, forgetting whether it was an exception or a success.
|
||||
|
||||
This corresponds to early return.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
|
||||
x α pure pure
|
||||
@@ -40,6 +58,9 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
|
||||
throw e := fun _ _ k => k e
|
||||
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
|
||||
|
||||
/--
|
||||
Run an action from the transformed monad in the exception monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
|
||||
fun _ k _ => x >>= k
|
||||
|
||||
@@ -10,6 +10,28 @@ import Init.Core
|
||||
|
||||
universe u
|
||||
|
||||
/--
|
||||
The identity function on types, used primarily for its `Monad` instance.
|
||||
|
||||
The identity monad is useful together with monad transformers to construct monads for particular
|
||||
purposes. Additionally, it can be used with `do`-notation in order to use control structures such as
|
||||
local mutability, `for`-loops, and early returns in code that does not otherwise use monads.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
def containsFive (xs : List Nat) : Bool := Id.run do
|
||||
for x in xs do
|
||||
if x == 5 then return true
|
||||
return false
|
||||
```
|
||||
|
||||
```lean example
|
||||
#eval containsFive [1, 3, 5, 7]
|
||||
```
|
||||
```output
|
||||
true
|
||||
```
|
||||
-/
|
||||
def Id (type : Type u) : Type u := type
|
||||
|
||||
namespace Id
|
||||
@@ -20,9 +42,18 @@ instance : Monad Id where
|
||||
bind x f := f x
|
||||
map f x := f x
|
||||
|
||||
/--
|
||||
The identity monad has a `bind` operator.
|
||||
-/
|
||||
def hasBind : Bind Id :=
|
||||
inferInstance
|
||||
|
||||
/--
|
||||
Runs a computation in the identity monad.
|
||||
|
||||
This function is the identity function. Because its parameter has type `Id α`, it causes
|
||||
`do`-notation in its arguments to use the `Monad Id` instance.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def run (x : Id α) : α := x
|
||||
|
||||
|
||||
@@ -6,3 +6,4 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
|
||||
prelude
|
||||
import Init.Control.Lawful.Basic
|
||||
import Init.Control.Lawful.Instances
|
||||
import Init.Control.Lawful.Lemmas
|
||||
|
||||
@@ -13,46 +13,85 @@ open Function
|
||||
rfl
|
||||
|
||||
/--
|
||||
The `Functor` typeclass only contains the operations of a functor.
|
||||
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
|
||||
including the preservation of the identity and composition laws:
|
||||
```
|
||||
id <$> x = x
|
||||
(h ∘ g) <$> x = h <$> g <$> x
|
||||
```
|
||||
A functor satisfies the functor laws.
|
||||
|
||||
The `Functor` class contains the operations of a functor, but does not require that instances
|
||||
prove they satisfy the laws of a functor. A `LawfulFunctor` instance includes proofs that the laws
|
||||
are satisfied. Because `Functor` instances may provide optimized implementations of `mapConst`,
|
||||
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulFunctor (f : Type u → Type v) [Functor f] : Prop where
|
||||
/--
|
||||
The `mapConst` implementation is equivalent to the default implementation.
|
||||
-/
|
||||
map_const : (Functor.mapConst : α → f β → f α) = Functor.map ∘ const β
|
||||
/--
|
||||
The `map` implementation preserves identity.
|
||||
-/
|
||||
id_map (x : f α) : id <$> x = x
|
||||
/--
|
||||
The `map` implementation preserves function composition.
|
||||
-/
|
||||
comp_map (g : α → β) (h : β → γ) (x : f α) : (h ∘ g) <$> x = h <$> g <$> x
|
||||
|
||||
export LawfulFunctor (map_const id_map comp_map)
|
||||
|
||||
attribute [simp] id_map
|
||||
|
||||
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
|
||||
@[simp] theorem id_map' [Functor f] [LawfulFunctor f] (x : f α) : (fun a => a) <$> x = x :=
|
||||
id_map x
|
||||
|
||||
@[simp] theorem Functor.map_map [Functor f] [LawfulFunctor f] (m : α → β) (g : β → γ) (x : f α) :
|
||||
g <$> m <$> x = (fun a => g (m a)) <$> x :=
|
||||
(comp_map _ _ _).symm
|
||||
|
||||
theorem Functor.map_unit [Functor f] [LawfulFunctor f] {a : f PUnit} : (fun _ => PUnit.unit) <$> a = a := by
|
||||
simp [map]
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
```
|
||||
pure id <*> v = v
|
||||
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
|
||||
pure f <*> pure x = pure (f x)
|
||||
u <*> pure y = pure (· y) <*> u
|
||||
```
|
||||
An applicative functor satisfies the laws of an applicative functor.
|
||||
|
||||
The `Applicative` class contains the operations of an applicative functor, but does not require that
|
||||
instances prove they satisfy the laws of an applicative functor. A `LawfulApplicative` instance
|
||||
includes proofs that the laws are satisfied.
|
||||
|
||||
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`,
|
||||
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the
|
||||
standard implementation.
|
||||
-/
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] extends LawfulFunctor f : Prop where
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] : Prop extends LawfulFunctor f where
|
||||
/-- `seqLeft` is equivalent to the default implementation. -/
|
||||
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
|
||||
/-- `seqRight` is equivalent to the default implementation. -/
|
||||
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
|
||||
/--
|
||||
`pure` before `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring immediately prior to `seq`.
|
||||
-/
|
||||
pure_seq (g : α → β) (x : f α) : pure g <*> x = g <$> x
|
||||
|
||||
/--
|
||||
Mapping a function over the result of `pure` is equivalent to applying the function under `pure`.
|
||||
|
||||
This means that `pure` really is pure with respect to `Functor.map`.
|
||||
-/
|
||||
map_pure (g : α → β) (x : α) : g <$> (pure x : f α) = pure (g x)
|
||||
|
||||
/--
|
||||
`pure` after `seq` is equivalent to `Functor.map`.
|
||||
|
||||
This means that `pure` really is pure when occurring just after `seq`.
|
||||
-/
|
||||
seq_pure {α β : Type u} (g : f (α → β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
|
||||
|
||||
/--
|
||||
`seq` is associative.
|
||||
|
||||
Changing the nesting of `seq` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `seq` is not doing any more than sequencing.
|
||||
-/
|
||||
seq_assoc {α β γ : Type u} (x : f α) (g : f (α → β)) (h : f (β → γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
|
||||
comp_map g h x := (by
|
||||
repeat rw [← pure_seq]
|
||||
@@ -66,21 +105,36 @@ attribute [simp] map_pure seq_pure
|
||||
simp [pure_seq]
|
||||
|
||||
/--
|
||||
The `Monad` typeclass only contains the operations of a monad.
|
||||
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
|
||||
including associativity and identity laws for `bind`:
|
||||
```
|
||||
pure x >>= f = f x
|
||||
x >>= pure = x
|
||||
x >>= f >>= g = x >>= (fun x => f x >>= g)
|
||||
```
|
||||
Lawful monads are those that satisfy a certain behavioral specification. While all instances of
|
||||
`Monad` should satisfy these laws, not all implementations are required to prove this.
|
||||
|
||||
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
|
||||
`LawfulMonad.mk'` is an alternative constructor that contains useful defaults for many fields.
|
||||
-/
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m : Prop where
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplicative m where
|
||||
/--
|
||||
A `bind` followed by `pure` composed with a function is equivalent to a functorial map.
|
||||
|
||||
This means that `pure` really is pure after a `bind` and has no effects.
|
||||
-/
|
||||
bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
|
||||
/--
|
||||
A `bind` followed by a functorial map is equivalent to `Applicative` sequencing.
|
||||
|
||||
This means that the effect sequencing from `Monad` and `Applicative` are the same.
|
||||
-/
|
||||
bind_map {α β : Type u} (f : m (α → β)) (x : m α) : f >>= (. <$> x) = f <*> x
|
||||
/--
|
||||
`pure` followed by `bind` is equivalent to function application.
|
||||
|
||||
This means that `pure` really is pure before a `bind` and has no effects.
|
||||
-/
|
||||
pure_bind (x : α) (f : α → m β) : pure x >>= f = f x
|
||||
/--
|
||||
`bind` is associative.
|
||||
|
||||
Changing the nesting of `bind` calls while maintaining the order of computations results in an
|
||||
equivalent computation. This means that `bind` is not doing more than data-dependent sequencing.
|
||||
-/
|
||||
bind_assoc (x : m α) (f : α → m β) (g : β → m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
|
||||
map_pure g x := (by rw [← bind_pure_comp, pure_bind])
|
||||
seq_pure g x := (by rw [← bind_map]; simp [map_pure, bind_pure_comp])
|
||||
@@ -133,8 +187,22 @@ theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y
|
||||
rw [← bind_pure_comp]
|
||||
simp only [bind_assoc, pure_bind]
|
||||
|
||||
theorem Functor.map_unit [Monad m] [LawfulMonad m] {a : m PUnit} : (fun _ => PUnit.unit) <$> a = a := by
|
||||
simp [map]
|
||||
/--
|
||||
This is just a duplicate of `LawfulApplicative.map_pure`,
|
||||
but sometimes applies when that doesn't.
|
||||
|
||||
It is named with a prime to avoid conflict with the inherited field `LawfulMonad.map_pure`.
|
||||
-/
|
||||
@[simp] theorem LawfulMonad.map_pure' [Monad m] [LawfulMonad m] {a : α} :
|
||||
(f <$> pure a : m β) = pure (f a) := by
|
||||
simp only [map_pure]
|
||||
|
||||
/--
|
||||
This is just a duplicate of `Functor.map_map`, but sometimes applies when that doesn't.
|
||||
-/
|
||||
@[simp] theorem LawfulMonad.map_map {m} [Monad m] [LawfulMonad m] {x : m α} :
|
||||
g <$> f <$> x = (fun a => g (f a)) <$> x := by
|
||||
simp only [Functor.map_map]
|
||||
|
||||
/--
|
||||
An alternative constructor for `LawfulMonad` which has more
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace ReaderT
|
||||
@[simp] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
|
||||
: (monadLift x : ReaderT ρ m α).run ctx = (monadLift x : m α) := rfl
|
||||
|
||||
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} → n β → n β) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
@[simp] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} → n β → n β) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
: (monadMap @f x : ReaderT ρ m α).run ctx = monadMap @f (x.run ctx) := rfl
|
||||
|
||||
@[simp] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
|
||||
@@ -199,7 +199,7 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
|
||||
|
||||
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
|
||||
|
||||
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} → n β → n β) (x : StateT σ m α) (s : σ) :
|
||||
@[simp] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} → n β → n β) (x : StateT σ m α) (s : σ) :
|
||||
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
|
||||
|
||||
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α → β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
|
||||
|
||||
57
src/Init/Control/Lawful/Lemmas.lean
Normal file
57
src/Init/Control/Lawful/Lemmas.lean
Normal file
@@ -0,0 +1,57 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Control.Lawful.Basic
|
||||
import Init.RCases
|
||||
import Init.ByCases
|
||||
|
||||
-- Mapping by a function with a left inverse is injective.
|
||||
theorem map_inj_of_left_inverse [Functor m] [LawfulFunctor m] {f : α → β}
|
||||
(w : ∃ g : β → α, ∀ x, g (f x) = x) {x y : m α} :
|
||||
f <$> x = f <$> y ↔ x = y := by
|
||||
constructor
|
||||
· intro h
|
||||
rcases w with ⟨g, w⟩
|
||||
replace h := congrArg (g <$> ·) h
|
||||
simpa [w] using h
|
||||
· rintro rfl
|
||||
rfl
|
||||
|
||||
-- Mapping by an injective function is injective, as long as the domain is nonempty.
|
||||
@[simp] theorem map_inj_right_of_nonempty [Functor m] [LawfulFunctor m] [Nonempty α] {f : α → β}
|
||||
(w : ∀ {x y}, f x = f y → x = y) {x y : m α} :
|
||||
f <$> x = f <$> y ↔ x = y := by
|
||||
constructor
|
||||
· intro h
|
||||
apply (map_inj_of_left_inverse ?_).mp h
|
||||
let ⟨a⟩ := ‹Nonempty α›
|
||||
refine ⟨?_, ?_⟩
|
||||
· intro b
|
||||
by_cases p : ∃ a, f a = b
|
||||
· exact Exists.choose p
|
||||
· exact a
|
||||
· intro b
|
||||
simp only [exists_apply_eq_apply, ↓reduceDIte]
|
||||
apply w
|
||||
apply Exists.choose_spec (p := fun a => f a = f b)
|
||||
· rintro rfl
|
||||
rfl
|
||||
|
||||
@[simp] theorem map_inj_right [Monad m] [LawfulMonad m]
|
||||
{f : α → β} (h : ∀ {x y : α}, f x = f y → x = y) {x y : m α} :
|
||||
f <$> x = f <$> y ↔ x = y := by
|
||||
by_cases hempty : Nonempty α
|
||||
· exact map_inj_right_of_nonempty h
|
||||
· constructor
|
||||
· intro h'
|
||||
have (z : m α) : z = (do let a ← z; let b ← pure (f a); x) := by
|
||||
conv => lhs; rw [← bind_pure z]
|
||||
congr; funext a
|
||||
exact (hempty ⟨a⟩).elim
|
||||
rw [this x, this y]
|
||||
rw [← bind_assoc, ← map_eq_pure_bind, h', map_eq_pure_bind, bind_assoc]
|
||||
· intro h'
|
||||
rw [h']
|
||||
@@ -8,13 +8,22 @@ import Init.Data.Option.Basic
|
||||
import Init.Control.Basic
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v
|
||||
|
||||
instance : ToBool (Option α) := ⟨Option.isSome⟩
|
||||
|
||||
/--
|
||||
Adds the ability to fail to a monad. Unlike ordinary exceptions, there is no way to signal why a
|
||||
failure occurred.
|
||||
-/
|
||||
def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Option α)
|
||||
|
||||
/--
|
||||
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
|
||||
x
|
||||
@@ -22,15 +31,25 @@ def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Op
|
||||
namespace OptionT
|
||||
variable {m : Type u → Type v} [Monad m] {α β : Type u}
|
||||
|
||||
/--
|
||||
Converts an action that returns an `Option` into one that might fail, with `none` indicating
|
||||
failure.
|
||||
-/
|
||||
protected def mk (x : m (Option α)) : OptionT m α :=
|
||||
x
|
||||
|
||||
/--
|
||||
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (x : OptionT m α) (f : α → OptionT m β) : OptionT m β := OptionT.mk do
|
||||
match (← x) with
|
||||
| some a => f a
|
||||
| none => pure none
|
||||
|
||||
/--
|
||||
Succeeds with the provided value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : OptionT m α := OptionT.mk do
|
||||
pure (some a)
|
||||
@@ -40,11 +59,17 @@ instance : Monad (OptionT m) where
|
||||
pure := OptionT.pure
|
||||
bind := OptionT.bind
|
||||
|
||||
/--
|
||||
Recovers from failures. Typically used via the `<|>` operator.
|
||||
-/
|
||||
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit → OptionT m α) : OptionT m α := OptionT.mk do
|
||||
match (← x) with
|
||||
| some a => pure (some a)
|
||||
| _ => y ()
|
||||
|
||||
/--
|
||||
A recoverable failure.
|
||||
-/
|
||||
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
|
||||
pure none
|
||||
|
||||
@@ -52,6 +77,12 @@ instance : Alternative (OptionT m) where
|
||||
failure := OptionT.fail
|
||||
orElse := OptionT.orElse
|
||||
|
||||
/--
|
||||
Converts a computation from the underlying monad into one that could fail, even though it does not.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
|
||||
return some (← x)
|
||||
|
||||
@@ -59,6 +90,9 @@ instance : MonadLift m (OptionT m) := ⟨OptionT.lift⟩
|
||||
|
||||
instance : MonadFunctor m (OptionT m) := ⟨fun f x => f x⟩
|
||||
|
||||
/--
|
||||
Handles failures by treating them as exceptions of type `Unit`.
|
||||
-/
|
||||
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit → OptionT m α) : OptionT m α := OptionT.mk do
|
||||
let some a ← x | handle ()
|
||||
pure a
|
||||
|
||||
@@ -10,12 +10,21 @@ import Init.Control.Basic
|
||||
import Init.Control.Id
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace ReaderT
|
||||
|
||||
/--
|
||||
Recovers from errors. The same local value is provided to both branches. Typically used via the
|
||||
`<|>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit → ReaderT ρ m α) : ReaderT ρ m α :=
|
||||
fun s => x₁ s <|> x₂ () s
|
||||
|
||||
/--
|
||||
Fails with a recoverable error.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def failure [Alternative m] : ReaderT ρ m α :=
|
||||
fun _ => failure
|
||||
@@ -35,4 +44,8 @@ instance : MonadControl m (ReaderT ρ m) where
|
||||
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
|
||||
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
|
||||
|
||||
/--
|
||||
A monad with access to a read-only value of type `ρ`. The value can be locally overridden by
|
||||
`withReader`, but it cannot be mutated.
|
||||
-/
|
||||
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id
|
||||
|
||||
@@ -9,19 +9,42 @@ prelude
|
||||
import Init.Control.Basic
|
||||
import Init.Control.Id
|
||||
import Init.Control.Except
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
/--
|
||||
Adds a mutable state of type `σ` to a monad.
|
||||
|
||||
Actions in the resulting monad are functions that take an initial state and return, in `m`, a tuple
|
||||
of a value and a state.
|
||||
-/
|
||||
def StateT (σ : Type u) (m : Type u → Type v) (α : Type u) : Type (max u v) :=
|
||||
σ → m (α × σ)
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateT.run {σ : Type u} {m : Type u → Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
|
||||
x s
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateT.run' {σ : Type u} {m : Type u → Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
|
||||
(·.1) <$> x s
|
||||
|
||||
/--
|
||||
A tuple-based state monad.
|
||||
|
||||
Actions in `StateM σ` are functions that take an initial state and return a value paired with a
|
||||
final state.
|
||||
-/
|
||||
@[reducible]
|
||||
def StateM (σ α : Type u) : Type u := StateT σ Id α
|
||||
|
||||
@@ -38,14 +61,23 @@ section
|
||||
variable {σ : Type u} {m : Type u → Type v}
|
||||
variable [Monad m] {α β : Type u}
|
||||
|
||||
/--
|
||||
Returns the given value without modifying the state. Typically used via `Pure.pure`.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def pure (a : α) : StateT σ m α :=
|
||||
fun s => pure (a, s)
|
||||
|
||||
/--
|
||||
Sequences two actions. Typically used via the `>>=` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def bind (x : StateT σ m α) (f : α → StateT σ m β) : StateT σ m β :=
|
||||
fun s => do let (a, s) ← x s; f a s
|
||||
|
||||
/--
|
||||
Modifies the value returned by a computation. Typically used via the `<$>` operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def map (f : α → β) (x : StateT σ m α) : StateT σ m β :=
|
||||
fun s => do let (a, s) ← x s; pure (f a, s)
|
||||
@@ -56,10 +88,17 @@ instance : Monad (StateT σ m) where
|
||||
bind := StateT.bind
|
||||
map := StateT.map
|
||||
|
||||
/--
|
||||
Recovers from errors. The state is rolled back on error recovery. Typically used via the `<|>`
|
||||
operator.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit → StateT σ m α) : StateT σ m α :=
|
||||
fun s => x₁ s <|> x₂ () s
|
||||
|
||||
/--
|
||||
Fails with a recoverable error. The state is rolled back on error recovery.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
|
||||
fun _ => failure
|
||||
@@ -68,18 +107,40 @@ instance [Alternative m] : Alternative (StateT σ m) where
|
||||
failure := StateT.failure
|
||||
orElse := StateT.orElse
|
||||
|
||||
/--
|
||||
Retrieves the current value of the monad's mutable state.
|
||||
|
||||
This increments the reference count of the state, which may inhibit in-place updates.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def get : StateT σ m σ :=
|
||||
fun s => pure (s, s)
|
||||
|
||||
/--
|
||||
Replaces the mutable state with a new value.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def set : σ → StateT σ m PUnit :=
|
||||
fun s' _ => pure (⟨⟩, s')
|
||||
|
||||
/--
|
||||
Applies a function to the current state that both computes a new state and a value. The new state
|
||||
replaces the current state, and the value is returned.
|
||||
|
||||
It is equivalent to `do let (a, s) := f (← StateT.get); StateT.set s; pure a`. However, using
|
||||
`StateT.modifyGet` may lead to better performance because it doesn't add a new reference to the
|
||||
state value, and additional references can inhibit in-place updates of data.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def modifyGet (f : σ → α × σ) : StateT σ m α :=
|
||||
fun s => pure (f s)
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
|
||||
fun s => do let a ← t; pure (a, s)
|
||||
@@ -98,7 +159,9 @@ instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {
|
||||
end
|
||||
end StateT
|
||||
|
||||
/-- Adapter to create a ForIn instance from a ForM instance -/
|
||||
/--
|
||||
Creates a suitable implementation of `ForIn.forIn` from a `ForM` instance.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def ForM.forIn [Monad m] [ForM (StateT β (ExceptT β m)) ρ α]
|
||||
(x : ρ) (b : β) (f : α → β → m (ForInStep β)) : m β := do
|
||||
|
||||
@@ -6,24 +6,45 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Control.Lawful.Basic
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/-!
|
||||
The State monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
/--
|
||||
An alternative implementation of a state monad transformer that internally uses continuation passing
|
||||
style instead of tuples.
|
||||
-/
|
||||
def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type u) → σ → (α → σ → m δ) → m δ
|
||||
|
||||
namespace StateCpsT
|
||||
|
||||
variable {α σ : Type u} {m : Type u → Type v}
|
||||
|
||||
/--
|
||||
Runs a stateful computation that's represented using continuation passing style by providing it with
|
||||
an initial state and a continuation.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def runK (x : StateCpsT σ m α) (s : σ) (k : α → σ → m β) : m β :=
|
||||
x _ s k
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
|
||||
While the state is internally represented in continuation passing style, the resulting value is the
|
||||
same as for a non-CPS state monad.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
|
||||
runK x s (fun a s => pure (a, s))
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
|
||||
runK x s (fun a _ => pure a)
|
||||
@@ -43,6 +64,12 @@ instance : MonadStateOf σ (StateCpsT σ m) where
|
||||
set s := fun _ _ k => k ⟨⟩ s
|
||||
modifyGet f := fun _ s k => let (a, s) := f s; k a s
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
|
||||
fun _ s k => x >>= (k . s)
|
||||
|
||||
@@ -8,10 +8,23 @@ The State monad transformer using IO references.
|
||||
prelude
|
||||
import Init.System.ST
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
/--
|
||||
A state monad that uses an actual mutable reference cell (i.e. an `ST.Ref ω σ`).
|
||||
|
||||
The macro `StateRefT σ m α` infers `ω` from `m`. It should normally be used instead.
|
||||
-/
|
||||
def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
|
||||
|
||||
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value paired with the final state.
|
||||
|
||||
The monad `m` must support `ST` effects in order to create and mutate reference cells.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
|
||||
let ref ← ST.mkRef s
|
||||
@@ -19,6 +32,12 @@ def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
|
||||
let s ← ref.get
|
||||
pure (a, s)
|
||||
|
||||
/--
|
||||
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
|
||||
state, it returns a value, discarding the final state.
|
||||
|
||||
The monad `m` must support `ST` effects in order to create and mutate reference cells.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
|
||||
let (a, _) ← x.run s
|
||||
@@ -27,6 +46,12 @@ def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
|
||||
namespace StateRefT'
|
||||
variable {ω σ : Type} {m : Type → Type} {α : Type}
|
||||
|
||||
/--
|
||||
Runs an action from the underlying monad in the monad with state. The state is not modified.
|
||||
|
||||
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
|
||||
lifting](lean-manual://section/monad-lifting).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
protected def lift (x : m α) : StateRefT' ω σ m α :=
|
||||
fun _ => x
|
||||
@@ -36,14 +61,30 @@ instance : MonadLift m (StateRefT' ω σ m) := ⟨StateRefT'.lift⟩
|
||||
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
|
||||
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
|
||||
|
||||
/--
|
||||
Retrieves the current value of the monad's mutable state.
|
||||
|
||||
This increments the reference count of the state, which may inhibit in-place updates.
|
||||
-/
|
||||
@[inline]
|
||||
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
|
||||
fun ref => ref.get
|
||||
|
||||
/--
|
||||
Replaces the mutable state with a new value.
|
||||
-/
|
||||
@[inline]
|
||||
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
|
||||
fun ref => ref.set s
|
||||
|
||||
/--
|
||||
Applies a function to the current state that both computes a new state and a value. The new state
|
||||
replaces the current state, and the value is returned.
|
||||
|
||||
It is equivalent to a `get` followed by a `set`. However, using `modifyGet` may lead to higher
|
||||
performance because it doesn't add a new reference to the state value. Additional references can
|
||||
inhibit in-place updates of data.
|
||||
-/
|
||||
@[inline]
|
||||
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ → α × σ) : StateRefT' ω σ m α :=
|
||||
fun ref => ref.modifyGet f
|
||||
|
||||
@@ -97,7 +97,7 @@ syntax (name := arg) "arg " argArg : conv
|
||||
|
||||
/-- `ext x` traverses into a binder (a `fun x => e` or `∀ x, e` expression)
|
||||
to target `e`, introducing name `x` in the process. -/
|
||||
syntax (name := ext) "ext" (ppSpace colGt ident)* : conv
|
||||
syntax (name := ext) "ext" (ppSpace colGt binderIdent)* : conv
|
||||
|
||||
/-- `change t'` replaces the target `t` with `t'`,
|
||||
assuming `t` and `t'` are definitionally equal. -/
|
||||
@@ -281,9 +281,9 @@ macro "left" : conv => `(conv| lhs)
|
||||
/-- `right` traverses into the right argument. Synonym for `rhs`. -/
|
||||
macro "right" : conv => `(conv| rhs)
|
||||
/-- `intro` traverses into binders. Synonym for `ext`. -/
|
||||
macro "intro" xs:(ppSpace colGt ident)* : conv => `(conv| ext $xs*)
|
||||
macro "intro" xs:(ppSpace colGt binderIdent)* : conv => `(conv| ext $xs*)
|
||||
|
||||
syntax enterArg := ident <|> argArg
|
||||
syntax enterArg := binderIdent <|> argArg
|
||||
|
||||
/-- `enter [arg, ...]` is a compact way to describe a path to a subterm.
|
||||
It is a shorthand for other conv tactics as follows:
|
||||
@@ -306,6 +306,10 @@ syntax (name := first) "first " withPosition((ppDedent(ppLine) colGe "| " convSe
|
||||
/-- `try tac` runs `tac` and succeeds even if `tac` failed. -/
|
||||
macro "try " t:convSeq : conv => `(conv| first | $t | skip)
|
||||
|
||||
/--
|
||||
`tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal, concatenating all goals
|
||||
produced by `tac'`.
|
||||
-/
|
||||
macro:1 x:conv tk:" <;> " y:conv:0 : conv =>
|
||||
`(conv| tactic' => (conv' => $x:conv) <;>%$tk (conv' => $y:conv))
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ class EvalInformation (α : Sort u) (β : Sort v) where
|
||||
evalVar : α → Nat → β
|
||||
|
||||
def Context.var (ctx : Context α) (idx : Nat) : Variable ctx.op :=
|
||||
ctx.vars.getD idx ⟨ctx.arbitrary, none⟩
|
||||
ctx.vars[idx]?.getD ⟨ctx.arbitrary, none⟩
|
||||
|
||||
instance : ContextInformation (Context α) where
|
||||
isNeutral ctx x := ctx.var x |>.neutral.isSome
|
||||
|
||||
@@ -23,3 +23,8 @@ import Init.Data.Array.FinRange
|
||||
import Init.Data.Array.Perm
|
||||
import Init.Data.Array.Find
|
||||
import Init.Data.Array.Lex
|
||||
import Init.Data.Array.Range
|
||||
import Init.Data.Array.Erase
|
||||
import Init.Data.Array.Zip
|
||||
import Init.Data.Array.InsertIdx
|
||||
import Init.Data.Array.Extract
|
||||
|
||||
@@ -6,20 +6,23 @@ Authors: Joachim Breitner, Mario Carneiro
|
||||
prelude
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Count
|
||||
import Init.Data.List.Attach
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
`O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
|
||||
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
|
||||
but is defined only when all members of `l` satisfy `P`, using the proof
|
||||
to apply `f`.
|
||||
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
|
||||
an array `xs : Array α`, given a proof that every element of `xs` in fact satisfies `P`.
|
||||
|
||||
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
|
||||
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
|
||||
-/
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (l : Array α) (H : ∀ a ∈ l, P a) : Array β :=
|
||||
(l.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
|
||||
|
||||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) : Array β :=
|
||||
(xs.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
|
||||
|
||||
/--
|
||||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||||
@@ -28,14 +31,27 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
@[inline] private unsafe def attachWithImpl
|
||||
(xs : Array α) (P : α → Prop) (_ : ∀ x ∈ xs, P x) : Array {x // P x} := unsafeCast xs
|
||||
|
||||
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
|
||||
with the same elements but in the type `{x // P x}`. -/
|
||||
/--
|
||||
“Attaches” individual proofs to an array of values that satisfy a predicate `P`, returning an array
|
||||
of elements in the corresponding subtype `{ x // P x }`.
|
||||
|
||||
`O(1)`.
|
||||
-/
|
||||
@[implemented_by attachWithImpl] def attachWith
|
||||
(xs : Array α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Array {x // P x} :=
|
||||
⟨xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)⟩
|
||||
|
||||
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
|
||||
with the same elements but in the type `{x // x ∈ xs}`. -/
|
||||
/--
|
||||
“Attaches” the proof that the elements of `xs` are in fact elements of `xs`, producing a new array with
|
||||
the same elements but in the subtype `{ x // x ∈ xs }`.
|
||||
|
||||
`O(1)`.
|
||||
|
||||
This function is primarily used to allow definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
|
||||
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
|
||||
well-founded recursion mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[inline] def attach (xs : Array α) : Array {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||||
@@ -50,35 +66,35 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem toList_attachWith {l : Array α} {P : α → Prop} {H : ∀ x ∈ l, P x} :
|
||||
(l.attachWith P H).toList = l.toList.attachWith P (by simpa [mem_toList] using H) := by
|
||||
@[simp] theorem toList_attachWith {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs, P x} :
|
||||
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList] using H) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem toList_attach {α : Type _} {l : Array α} :
|
||||
l.attach.toList = l.toList.attachWith (· ∈ l) (by simp [mem_toList]) := by
|
||||
@[simp] theorem toList_attach {xs : Array α} :
|
||||
xs.attach.toList = xs.toList.attachWith (· ∈ xs) (by simp [mem_toList]) := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toList_pmap {l : Array α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l, P a} :
|
||||
(l.pmap f H).toList = l.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
|
||||
@[simp] theorem toList_pmap {xs : Array α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ xs, P a} :
|
||||
(xs.pmap f H).toList = xs.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
|
||||
simp [pmap]
|
||||
|
||||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (l : Array α) (H : ∀ a ∈ l, P a) :
|
||||
Array β := (l.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) :
|
||||
Array β := (xs.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||||
|
||||
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||||
funext α β p f L h'
|
||||
cases L
|
||||
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith]
|
||||
funext α β p f xs H
|
||||
cases xs
|
||||
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith_eq_pmap]
|
||||
apply List.pmap_congr_left
|
||||
intro a m h₁ h₂
|
||||
congr
|
||||
|
||||
@[simp] theorem pmap_empty {P : α → Prop} (f : ∀ a, P a → β) : pmap f #[] (by simp) = #[] := rfl
|
||||
|
||||
@[simp] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (l : Array α) (h : ∀ b ∈ l.push a, P b) :
|
||||
pmap f (l.push a) h =
|
||||
(pmap f l (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
|
||||
@[simp] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (xs : Array α) (h : ∀ b ∈ xs.push a, P b) :
|
||||
pmap f (xs.push a) h =
|
||||
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
|
||||
@@ -93,147 +109,160 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_map (p : α → Prop) (f : α → β) (l : Array α) (H) :
|
||||
@pmap _ _ p (fun a _ => f a) l H = map f l := by
|
||||
cases l; simp
|
||||
theorem pmap_eq_map {p : α → Prop} {f : α → β} {xs : Array α} (H) :
|
||||
@pmap _ _ p (fun a _ => f a) xs H = map f xs := by
|
||||
cases xs; simp
|
||||
|
||||
theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (l : Array α) {H₁ H₂}
|
||||
(h : ∀ a ∈ l, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
|
||||
cases l
|
||||
theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (xs : Array α) {H₁ H₂}
|
||||
(h : ∀ a ∈ xs, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f xs H₁ = pmap g xs H₂ := by
|
||||
cases xs
|
||||
simp only [mem_toArray] at h
|
||||
simp only [List.pmap_toArray, mk.injEq]
|
||||
rw [List.pmap_congr_left _ h]
|
||||
|
||||
theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (l H) :
|
||||
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
|
||||
cases l
|
||||
theorem map_pmap {p : α → Prop} {g : β → γ} {f : ∀ a, p a → β} {xs : Array α} (H) :
|
||||
map g (pmap f xs H) = pmap (fun a h => g (f a h)) xs H := by
|
||||
cases xs
|
||||
simp [List.map_pmap]
|
||||
|
||||
theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H) :
|
||||
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun _ h => H _ (mem_map_of_mem _ h) := by
|
||||
cases l
|
||||
theorem pmap_map {p : β → Prop} {g : ∀ b, p b → γ} {f : α → β} {xs : Array α} (H) :
|
||||
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem h) := by
|
||||
cases xs
|
||||
simp [List.pmap_map]
|
||||
|
||||
theorem attach_congr {l₁ l₂ : Array α} (h : l₁ = l₂) :
|
||||
l₁.attach = l₂.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||||
theorem attach_congr {xs ys : Array α} (h : xs = ys) :
|
||||
xs.attach = ys.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem attachWith_congr {l₁ l₂ : Array α} (w : l₁ = l₂) {P : α → Prop} {H : ∀ x ∈ l₁, P x} :
|
||||
l₁.attachWith P H = l₂.attachWith P fun _ h => H _ (w ▸ h) := by
|
||||
theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H : ∀ x ∈ xs, P x} :
|
||||
xs.attachWith P H = ys.attachWith P fun _ h => H _ (w ▸ h) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
@[simp] theorem attach_push {a : α} {l : Array α} :
|
||||
(l.push a).attach =
|
||||
(l.attach.map (fun ⟨x, h⟩ => ⟨x, mem_push_of_mem a h⟩)).push ⟨a, by simp⟩ := by
|
||||
cases l
|
||||
@[simp] theorem attach_push {a : α} {xs : Array α} :
|
||||
(xs.push a).attach =
|
||||
(xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_push_of_mem a h⟩)).push ⟨a, by simp⟩ := by
|
||||
cases xs
|
||||
rw [attach_congr (List.push_toArray _ _)]
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp] theorem attachWith_push {a : α} {l : Array α} {P : α → Prop} {H : ∀ x ∈ l.push a, P x} :
|
||||
(l.push a).attachWith P H =
|
||||
(l.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push ⟨a, H a (by simp)⟩ := by
|
||||
cases l
|
||||
@[simp] theorem attachWith_push {a : α} {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs.push a, P x} :
|
||||
(xs.push a).attachWith P H =
|
||||
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push ⟨a, H a (by simp)⟩ := by
|
||||
cases xs
|
||||
simp [attachWith_congr (List.push_toArray _ _)]
|
||||
|
||||
theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
cases l
|
||||
theorem pmap_eq_map_attach {p : α → Prop} {f : ∀ a, p a → β} {xs : Array α} (H) :
|
||||
pmap f xs H = xs.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
cases xs
|
||||
simp [List.pmap_eq_map_attach]
|
||||
|
||||
theorem attach_map_coe (l : Array α) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
cases l
|
||||
simp [List.attach_map_coe]
|
||||
@[simp]
|
||||
theorem pmap_eq_attachWith {p q : α → Prop} {f : ∀ a, p a → q a} {xs : Array α} (H) :
|
||||
pmap (fun a h => ⟨a, f a h⟩) xs H = xs.attachWith q (fun x h => f x (H x h)) := by
|
||||
cases xs
|
||||
simp [List.pmap_eq_attachWith]
|
||||
|
||||
theorem attach_map_val (l : Array α) (f : α → β) : (l.attach.map fun i => f i.val) = l.map f :=
|
||||
attach_map_coe _ _
|
||||
theorem attach_map_val (xs : Array α) (f : α → β) :
|
||||
(xs.attach.map fun (i : {i // i ∈ xs}) => f i) = xs.map f := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
theorem attach_map_subtype_val (l : Array α) : l.attach.map Subtype.val = l := by
|
||||
cases l; simp
|
||||
@[deprecated attach_map_val (since := "2025-02-17")]
|
||||
abbrev attach_map_coe := @attach_map_val
|
||||
|
||||
theorem attachWith_map_coe {p : α → Prop} (f : α → β) (l : Array α) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
|
||||
cases l; simp
|
||||
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
|
||||
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
|
||||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (l : Array α) (H : ∀ a ∈ l, p a) :
|
||||
((l.attachWith p H).map fun i => f i.val) = l.map f :=
|
||||
attachWith_map_coe _ _ _
|
||||
theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||||
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
|
||||
cases xs; simp
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} (l : Array α) (H : ∀ a ∈ l, p a) :
|
||||
(l.attachWith p H).map Subtype.val = l := by
|
||||
cases l; simp
|
||||
@[deprecated attachWith_map_val (since := "2025-02-17")]
|
||||
abbrev attachWith_map_coe := @attachWith_map_val
|
||||
|
||||
theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||||
(xs.attachWith p H).map Subtype.val = xs := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_attach (l : Array α) : ∀ x, x ∈ l.attach
|
||||
theorem mem_attach (xs : Array α) : ∀ x, x ∈ xs.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||||
exact m
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
theorem mem_attachWith {xs : Array α} {q : α → Prop} (H) (x : {x // q x}) :
|
||||
x ∈ xs.attachWith q H ↔ x.1 ∈ xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs H b} :
|
||||
b ∈ pmap f xs H ↔ ∃ (a : _) (h : a ∈ xs), f a (H a h) = b := by
|
||||
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
|
||||
|
||||
theorem mem_pmap_of_mem {p : α → Prop} {f : ∀ a, p a → β} {l H} {a} (h : a ∈ l) :
|
||||
f a (H a h) ∈ pmap f l H := by
|
||||
theorem mem_pmap_of_mem {p : α → Prop} {f : ∀ a, p a → β} {xs H} {a} (h : a ∈ xs) :
|
||||
f a (H a h) ∈ pmap f xs H := by
|
||||
rw [mem_pmap]
|
||||
exact ⟨a, h, rfl⟩
|
||||
|
||||
@[simp]
|
||||
theorem size_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H} : (pmap f l H).size = l.size := by
|
||||
cases l; simp
|
||||
theorem size_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs H} : (pmap f xs H).size = xs.size := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem size_attach {L : Array α} : L.attach.size = L.size := by
|
||||
cases L; simp
|
||||
theorem size_attach {xs : Array α} : xs.attach.size = xs.size := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem size_attachWith {p : α → Prop} {l : Array α} {H} : (l.attachWith p H).size = l.size := by
|
||||
cases l; simp
|
||||
theorem size_attachWith {p : α → Prop} {xs : Array α} {H} : (xs.attachWith p H).size = xs.size := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_empty_iff {p : α → Prop} {f : ∀ a, p a → β} {l H} : pmap f l H = #[] ↔ l = #[] := by
|
||||
cases l; simp
|
||||
theorem pmap_eq_empty_iff {p : α → Prop} {f : ∀ a, p a → β} {xs H} : pmap f xs H = #[] ↔ xs = #[] := by
|
||||
cases xs; simp
|
||||
|
||||
theorem pmap_ne_empty_iff {P : α → Prop} (f : (a : α) → P a → β) {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) : xs.pmap f H ≠ #[] ↔ xs ≠ #[] := by
|
||||
cases xs; simp
|
||||
|
||||
theorem pmap_eq_self {l : Array α} {p : α → Prop} {hp : ∀ (a : α), a ∈ l → p a}
|
||||
{f : (a : α) → p a → α} : l.pmap f hp = l ↔ ∀ a (h : a ∈ l), f a (hp a h) = a := by
|
||||
cases l; simp [List.pmap_eq_self]
|
||||
theorem pmap_eq_self {xs : Array α} {p : α → Prop} {hp : ∀ (a : α), a ∈ xs → p a}
|
||||
{f : (a : α) → p a → α} : xs.pmap f hp = xs ↔ ∀ a (h : a ∈ xs), f a (hp a h) = a := by
|
||||
cases xs; simp [List.pmap_eq_self]
|
||||
|
||||
@[simp]
|
||||
theorem attach_eq_empty_iff {l : Array α} : l.attach = #[] ↔ l = #[] := by
|
||||
cases l; simp
|
||||
theorem attach_eq_empty_iff {xs : Array α} : xs.attach = #[] ↔ xs = #[] := by
|
||||
cases xs; simp
|
||||
|
||||
theorem attach_ne_empty_iff {l : Array α} : l.attach ≠ #[] ↔ l ≠ #[] := by
|
||||
cases l; simp
|
||||
theorem attach_ne_empty_iff {xs : Array α} : xs.attach ≠ #[] ↔ xs ≠ #[] := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem attachWith_eq_empty_iff {l : Array α} {P : α → Prop} {H : ∀ a ∈ l, P a} :
|
||||
l.attachWith P H = #[] ↔ l = #[] := by
|
||||
cases l; simp
|
||||
theorem attachWith_eq_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
xs.attachWith P H = #[] ↔ xs = #[] := by
|
||||
cases xs; simp
|
||||
|
||||
theorem attachWith_ne_empty_iff {l : Array α} {P : α → Prop} {H : ∀ a ∈ l, P a} :
|
||||
l.attachWith P H ≠ #[] ↔ l ≠ #[] := by
|
||||
cases l; simp
|
||||
theorem attachWith_ne_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
xs.attachWith P H ≠ #[] ↔ xs ≠ #[] := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (mem_of_getElem? H) := by
|
||||
cases l; simp
|
||||
theorem getElem?_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs : Array α} (h : ∀ a ∈ xs, p a) (i : Nat) :
|
||||
(pmap f xs h)[i]? = Option.pmap f xs[i]? fun x H => h x (mem_of_getElem? H) := by
|
||||
cases xs; simp
|
||||
|
||||
-- The argument `f` is explicit to allow rewriting from right to left.
|
||||
@[simp]
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : Array α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).size) :
|
||||
(pmap f l h)[n] =
|
||||
f (l[n]'(@size_pmap _ _ p f l h ▸ hn))
|
||||
(h _ (getElem_mem (@size_pmap _ _ p f l h ▸ hn))) := by
|
||||
cases l; simp
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {xs : Array α} (h : ∀ a ∈ xs, p a) {i : Nat}
|
||||
(hi : i < (pmap f xs h).size) :
|
||||
(pmap f xs h)[i] =
|
||||
f (xs[i]'(@size_pmap _ _ p f xs h ▸ hi))
|
||||
(h _ (getElem_mem (@size_pmap _ _ p f xs h ▸ hi))) := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_attachWith {xs : Array α} {i : Nat} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||||
@@ -256,16 +285,42 @@ theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
|
||||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||||
getElem_attachWith h
|
||||
|
||||
theorem foldl_pmap (l : Array α) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : γ → β → γ) (x : γ) :
|
||||
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x ∈ xs} → Prop} {f : ∀ a, p a → β} (H) :
|
||||
pmap f xs.attach H =
|
||||
xs.pmap (P := fun a => ∃ h : a ∈ xs, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem pmap_attachWith {xs : Array α} {p : {x // q x} → Prop} {f : ∀ a, p a → β} (H₁ H₂) :
|
||||
pmap f (xs.attachWith q H₁) H₂ =
|
||||
xs.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||||
ext <;> simp
|
||||
|
||||
theorem foldl_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (g : γ → β → γ) (x : γ) :
|
||||
(xs.pmap f H).foldl g x = xs.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||||
rw [pmap_eq_map_attach, foldl_map]
|
||||
|
||||
theorem foldr_pmap (l : Array α) {P : α → Prop} (f : (a : α) → P a → β)
|
||||
(H : ∀ (a : α), a ∈ l → P a) (g : β → γ → γ) (x : γ) :
|
||||
(l.pmap f H).foldr g x = l.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||||
theorem foldr_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (g : β → γ → γ) (x : γ) :
|
||||
(xs.pmap f H).foldr g x = xs.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||||
rw [pmap_eq_map_attach, foldr_map]
|
||||
|
||||
@[simp] theorem foldl_attachWith
|
||||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : β → { x // q x} → β} {b} (w : stop = xs.size) :
|
||||
(xs.attachWith q H).foldl f b 0 stop = xs.attach.foldl (fun b ⟨a, h⟩ => f b ⟨a, H _ h⟩) b := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldl_attachWith, List.foldl_map]
|
||||
|
||||
@[simp] theorem foldr_attachWith
|
||||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : { x // q x} → β → β} {b} (w : start = xs.size) :
|
||||
(xs.attachWith q H).foldr f b start 0 = xs.attach.foldr (fun a acc => f ⟨a.1, H _ a.2⟩ acc) b := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldr_attachWith, List.foldr_map]
|
||||
|
||||
/--
|
||||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||||
we get the same results as folding over `l` directly.
|
||||
@@ -276,10 +331,10 @@ Unfortunately this can't be applied by `simp` because of the higher order unific
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
See however `foldl_subtype` below.
|
||||
-/
|
||||
theorem foldl_attach (l : Array α) (f : β → α → β) (b : β) :
|
||||
l.attach.foldl (fun acc t => f acc t.1) b = l.foldl f b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
|
||||
theorem foldl_attach {xs : Array α} {f : β → α → β} {b : β} :
|
||||
xs.attach.foldl (fun acc t => f acc t.1) b = xs.foldl f b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
|
||||
List.length_pmap, List.foldl_toArray', mem_toArray, List.foldl_subtype]
|
||||
congr
|
||||
ext
|
||||
@@ -295,83 +350,103 @@ Unfortunately this can't be applied by `simp` because of the higher order unific
|
||||
and even when rewriting we need to specify the function explicitly.
|
||||
See however `foldr_subtype` below.
|
||||
-/
|
||||
theorem foldr_attach (l : Array α) (f : α → β → β) (b : β) :
|
||||
l.attach.foldr (fun t acc => f t.1 acc) b = l.foldr f b := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
|
||||
theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
|
||||
xs.attach.foldr (fun t acc => f t.1 acc) b = xs.foldr f b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
|
||||
List.length_pmap, List.foldr_toArray', mem_toArray, List.foldr_subtype]
|
||||
congr
|
||||
ext
|
||||
simpa using fun a => List.mem_of_getElem? a
|
||||
|
||||
theorem attach_map {l : Array α} (f : α → β) :
|
||||
(l.map f).attach = l.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||||
cases l
|
||||
theorem attach_map {xs : Array α} {f : α → β} :
|
||||
(xs.map f).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem h⟩) := by
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
theorem attachWith_map {l : Array α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ l.map f → P b} :
|
||||
(l.map f).attachWith P H = (l.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem f h))).map
|
||||
theorem attachWith_map {xs : Array α} {f : α → β} {P : β → Prop} (H : ∀ (b : β), b ∈ xs.map f → P b) :
|
||||
(xs.map f).attachWith P H = (xs.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem h))).map
|
||||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||||
cases l
|
||||
ext
|
||||
· simp
|
||||
· simp only [List.map_toArray, List.attachWith_toArray, List.getElem_toArray,
|
||||
List.getElem_attachWith, List.getElem_map, Function.comp_apply]
|
||||
erw [List.getElem_attachWith] -- Why is `erw` needed here?
|
||||
cases xs
|
||||
simp [List.attachWith_map]
|
||||
|
||||
theorem map_attachWith {l : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ l → P a}
|
||||
(f : { x // P x } → β) :
|
||||
(l.attachWith P H).map f =
|
||||
l.pmap (fun a (h : a ∈ l ∧ P a) => f ⟨a, H _ h.1⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
cases l
|
||||
@[simp] theorem map_attachWith {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||||
{f : { x // P x } → β} :
|
||||
(xs.attachWith P H).map f = xs.attach.map fun ⟨x, h⟩ => f ⟨x, H _ h⟩ := by
|
||||
cases xs <;> simp_all
|
||||
|
||||
theorem map_attachWith_eq_pmap {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||||
{f : { x // P x } → β} :
|
||||
(xs.attachWith P H).map f =
|
||||
xs.pmap (fun a (h : a ∈ xs ∧ P a) => f ⟨a, H _ h.1⟩) (fun a h => ⟨h, H a h⟩) := by
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
|
||||
theorem map_attach {l : Array α} (f : { x // x ∈ l } → β) :
|
||||
l.attach.map f = l.pmap (fun a h => f ⟨a, h⟩) (fun _ => id) := by
|
||||
cases l
|
||||
theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
|
||||
xs.attach.map f = xs.pmap (fun a h => f ⟨a, h⟩) (fun _ => id) := by
|
||||
cases xs
|
||||
ext <;> simp
|
||||
|
||||
theorem attach_filterMap {l : Array α} {f : α → Option β} :
|
||||
(l.filterMap f).attach = l.attach.filterMap
|
||||
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
|
||||
abbrev map_attach := @map_attach_eq_pmap
|
||||
|
||||
theorem attach_filterMap {xs : Array α} {f : α → Option β} :
|
||||
(xs.filterMap f).attach = xs.attach.filterMap
|
||||
fun ⟨x, h⟩ => (f x).pbind (fun b m => some ⟨b, mem_filterMap.mpr ⟨x, h, m⟩⟩) := by
|
||||
cases l
|
||||
rw [attach_congr (List.filterMap_toArray f _)]
|
||||
cases xs
|
||||
rw [attach_congr List.filterMap_toArray]
|
||||
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
|
||||
|
||||
theorem attach_filter {l : Array α} (p : α → Bool) :
|
||||
(l.filter p).attach = l.attach.filterMap
|
||||
theorem attach_filter {xs : Array α} (p : α → Bool) :
|
||||
(xs.filter p).attach = xs.attach.filterMap
|
||||
fun x => if w : p x.1 then some ⟨x.1, mem_filter.mpr ⟨x.2, w⟩⟩ else none := by
|
||||
cases l
|
||||
rw [attach_congr (List.filter_toArray p _)]
|
||||
cases xs
|
||||
rw [attach_congr List.filter_toArray]
|
||||
simp [List.attach_filter, List.map_filterMap, Function.comp_def]
|
||||
|
||||
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
|
||||
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} (g : ∀ a, p a → β) (f : ∀ b, q b → γ) (l H₁ H₂) :
|
||||
pmap f (pmap g l H₁) H₂ =
|
||||
pmap (α := { x // x ∈ l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
|
||||
@[simp]
|
||||
theorem filterMap_attachWith {q : α → Prop} {xs : Array α} {f : {x // q x} → Option β} (H)
|
||||
(w : stop = (xs.attachWith q H).size) :
|
||||
(xs.attachWith q H).filterMap f 0 stop = xs.attach.filterMap (fun ⟨x, h⟩ => f ⟨x, H _ h⟩) := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} → Bool} (H)
|
||||
(w : stop = (xs.attachWith q H).size) :
|
||||
(xs.attachWith q H).filter p 0 stop =
|
||||
(xs.attach.filter (fun ⟨x, h⟩ => p ⟨x, H _ h⟩)).map (fun ⟨x, h⟩ => ⟨x, H _ h⟩) := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [Function.comp_def, List.filter_map]
|
||||
|
||||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f : ∀ b, q b → γ} {xs} (H₁ H₂) :
|
||||
pmap f (pmap g xs H₁) H₂ =
|
||||
pmap (α := { x // x ∈ xs }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) xs.attach
|
||||
(fun a _ => H₁ a a.2) := by
|
||||
cases l
|
||||
cases xs
|
||||
simp [List.pmap_pmap, List.pmap_map]
|
||||
|
||||
@[simp] theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : Array ι)
|
||||
(h : ∀ a ∈ l₁ ++ l₂, p a) :
|
||||
(l₁ ++ l₂).pmap f h =
|
||||
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
|
||||
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
@[simp] theorem pmap_append {p : ι → Prop} {f : ∀ a : ι, p a → α} {xs ys : Array ι}
|
||||
(h : ∀ a ∈ xs ++ ys, p a) :
|
||||
(xs ++ ys).pmap f h =
|
||||
(xs.pmap f fun a ha => h a (mem_append_left ys ha)) ++
|
||||
ys.pmap f fun a ha => h a (mem_append_right xs ha) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp
|
||||
|
||||
theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ : Array α)
|
||||
(h₁ : ∀ a ∈ l₁, p a) (h₂ : ∀ a ∈ l₂, p a) :
|
||||
((l₁ ++ l₂).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
|
||||
pmap_append f l₁ l₂ _
|
||||
theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Array α}
|
||||
(h₁ : ∀ a ∈ xs, p a) (h₂ : ∀ a ∈ ys, p a) :
|
||||
((xs ++ ys).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
xs.pmap f h₁ ++ ys.pmap f h₂ :=
|
||||
pmap_append _
|
||||
|
||||
@[simp] theorem attach_append (xs ys : Array α) :
|
||||
@[simp] theorem attach_append {xs ys : Array α} :
|
||||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_append_left ys h⟩) ++
|
||||
ys.attach.map fun ⟨x, h⟩ => ⟨x, mem_append_right xs h⟩ := by
|
||||
cases xs
|
||||
@@ -385,12 +460,12 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ :
|
||||
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
|
||||
simp [attachWith, attach_append, map_pmap, pmap_append]
|
||||
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} (f : (a : α) → P a → β) (xs : Array α)
|
||||
@[simp] theorem pmap_reverse {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs.reverse → P a) :
|
||||
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||||
induction xs <;> simp_all
|
||||
|
||||
theorem reverse_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Array α)
|
||||
theorem reverse_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||||
rw [pmap_reverse]
|
||||
@@ -408,18 +483,18 @@ theorem reverse_attachWith {P : α → Prop} {xs : Array α}
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem attach_reverse (xs : Array α) :
|
||||
@[simp] theorem attach_reverse {xs : Array α} :
|
||||
xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
rw [attach_congr (List.reverse_toArray _)]
|
||||
rw [attach_congr List.reverse_toArray]
|
||||
simp
|
||||
|
||||
theorem reverse_attach (xs : Array α) :
|
||||
theorem reverse_attach {xs : Array α} :
|
||||
xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem back?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Array α)
|
||||
@[simp] theorem back?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||||
(xs.pmap f H).back? = xs.attach.back?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
cases xs
|
||||
@@ -427,16 +502,48 @@ theorem reverse_attach (xs : Array α) :
|
||||
|
||||
@[simp] theorem back?_attachWith {P : α → Prop} {xs : Array α}
|
||||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back?_eq_some h)⟩) := by
|
||||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back? h)⟩) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem back?_attach {xs : Array α} :
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back?_eq_some h⟩ := by
|
||||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back? h⟩ := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem countP_attach {xs : Array α} {p : α → Bool} :
|
||||
xs.attach.countP (fun a : {x // x ∈ xs} => p a) = xs.countP p := by
|
||||
cases xs
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp]
|
||||
theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H : ∀ a ∈ xs, p a} :
|
||||
(xs.attachWith p H).countP (fun a : {x // p x} => q a) = xs.countP q := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem count_attach [DecidableEq α] {xs : Array α} {a : {x // x ∈ xs}} :
|
||||
xs.attach.count a = xs.count ↑a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
|
||||
rw [List.map_attach_eq_pmap, List.count_eq_countP]
|
||||
simp only [Subtype.beq_iff]
|
||||
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
|
||||
|
||||
@[simp]
|
||||
theorem count_attachWith [DecidableEq α] {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) {a : {x // p x}} :
|
||||
(xs.attachWith p H).count a = xs.count ↑a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pmap {p : α → Prop} {g : ∀ a, p a → β} {f : β → Bool} {xs : Array α} (H₁) :
|
||||
(xs.pmap g H₁).countP f =
|
||||
xs.attach.countP (fun ⟨a, m⟩ => f (g a (H₁ a m))) := by
|
||||
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
|
||||
|
||||
/-! ## unattach
|
||||
|
||||
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
|
||||
@@ -449,49 +556,63 @@ Further, we provide simp lemmas that push `unattach` inwards.
|
||||
-/
|
||||
|
||||
/--
|
||||
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
|
||||
It is introduced as in intermediate step by lemmas such as `map_subtype`,
|
||||
and is ideally subsequently simplified away by `unattach_attach`.
|
||||
Maps an array of terms in a subtype to the corresponding terms in the type by forgetting that they
|
||||
satisfy the predicate.
|
||||
|
||||
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
|
||||
This is the inverse of `Array.attachWith` and a synonym for `xs.map (·.val)`.
|
||||
|
||||
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
|
||||
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
|
||||
|
||||
This function is usually inserted automatically by Lean as an intermediate step while proving
|
||||
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
|
||||
the elaboration of definitions by [well-founded
|
||||
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
|
||||
state, the right approach is usually the tactic `simp [Array.unattach, -Array.map_subtype]`.
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) := l.map (·.val)
|
||||
def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {l : Array { x // p x }} :
|
||||
(l.push a).unattach = l.unattach.push a.1 := by
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {xs : Array { x // p x }} :
|
||||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||||
simp only [unattach, Array.map_push]
|
||||
|
||||
@[simp] theorem size_unattach {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.unattach.size = l.size := by
|
||||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Array { x // p x }} {a} :
|
||||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||||
|
||||
@[simp] theorem size_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.unattach.size = xs.size := by
|
||||
unfold unattach
|
||||
simp
|
||||
|
||||
@[simp] theorem _root_.List.unattach_toArray {p : α → Prop} {l : List { x // p x }} :
|
||||
l.toArray.unattach = l.unattach.toArray := by
|
||||
@[simp] theorem _root_.List.unattach_toArray {p : α → Prop} {xs : List { x // p x }} :
|
||||
xs.toArray.unattach = xs.unattach.toArray := by
|
||||
simp only [unattach, List.map_toArray, List.unattach]
|
||||
|
||||
@[simp] theorem toList_unattach {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.unattach.toList = l.toList.unattach := by
|
||||
@[simp] theorem toList_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.unattach.toList = xs.toList.unattach := by
|
||||
simp only [unattach, toList_map, List.unattach]
|
||||
|
||||
@[simp] theorem unattach_attach {l : Array α} : l.attach.unattach = l := by
|
||||
cases l
|
||||
@[simp] theorem unattach_attach {xs : Array α} : xs.attach.unattach = xs := by
|
||||
cases xs
|
||||
simp only [List.attach_toArray, List.unattach_toArray, List.unattach_attachWith]
|
||||
|
||||
@[simp] theorem unattach_attachWith {p : α → Prop} {l : Array α}
|
||||
{H : ∀ a ∈ l, p a} :
|
||||
(l.attachWith p H).unattach = l := by
|
||||
cases l
|
||||
@[simp] theorem unattach_attachWith {p : α → Prop} {xs : Array α}
|
||||
{H : ∀ a ∈ xs, p a} :
|
||||
(xs.attachWith p H).unattach = xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_unattach {p : α → Prop} {l : Array { x // p x }} (i : Nat) :
|
||||
l.unattach[i]? = l[i]?.map Subtype.val := by
|
||||
@[simp] theorem getElem?_unattach {p : α → Prop} {xs : Array { x // p x }} (i : Nat) :
|
||||
xs.unattach[i]? = xs[i]?.map Subtype.val := by
|
||||
simp [unattach]
|
||||
|
||||
@[simp] theorem getElem_unattach
|
||||
{p : α → Prop} {l : Array { x // p x }} (i : Nat) (h : i < l.unattach.size) :
|
||||
l.unattach[i] = (l[i]'(by simpa using h)).1 := by
|
||||
{p : α → Prop} {xs : Array { x // p x }} (i : Nat) (h : i < xs.unattach.size) :
|
||||
xs.unattach[i] = (xs[i]'(by simpa using h)).1 := by
|
||||
simp [unattach]
|
||||
|
||||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||||
@@ -500,20 +621,20 @@ def unattach {α : Type _} {p : α → Prop} (l : Array { x // p x }) := l.map (
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
theorem foldl_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
theorem foldl_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} :
|
||||
l.foldl f x = l.unattach.foldl g x := by
|
||||
cases l
|
||||
(hf : ∀ b x h, f b ⟨x, h⟩ = g b x) :
|
||||
xs.foldl f x = xs.unattach.foldl g x := by
|
||||
cases xs
|
||||
simp only [List.foldl_toArray', List.unattach_toArray]
|
||||
rw [List.foldl_subtype] -- Why can't simp do this?
|
||||
simp [hf]
|
||||
|
||||
/-- Variant of `foldl_subtype` with side condition to check `stop = l.size`. -/
|
||||
@[simp] theorem foldl_subtype' {p : α → Prop} {l : Array { x // p x }}
|
||||
@[simp] theorem foldl_subtype' {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||||
{hf : ∀ b x h, f b ⟨x, h⟩ = g b x} (h : stop = l.size) :
|
||||
l.foldl f x 0 stop = l.unattach.foldl g x := by
|
||||
(hf : ∀ b x h, f b ⟨x, h⟩ = g b x) (h : stop = xs.size) :
|
||||
xs.foldl f x 0 stop = xs.unattach.foldl g x := by
|
||||
subst h
|
||||
rwa [foldl_subtype]
|
||||
|
||||
@@ -521,20 +642,20 @@ theorem foldl_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
theorem foldr_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
theorem foldr_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} :
|
||||
l.foldr f x = l.unattach.foldr g x := by
|
||||
cases l
|
||||
(hf : ∀ x h b, f ⟨x, h⟩ b = g x b) :
|
||||
xs.foldr f x = xs.unattach.foldr g x := by
|
||||
cases xs
|
||||
simp only [List.foldr_toArray', List.unattach_toArray]
|
||||
rw [List.foldr_subtype]
|
||||
simp [hf]
|
||||
|
||||
/-- Variant of `foldr_subtype` with side condition to check `stop = l.size`. -/
|
||||
@[simp] theorem foldr_subtype' {p : α → Prop} {l : Array { x // p x }}
|
||||
@[simp] theorem foldr_subtype' {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||||
{hf : ∀ x h b, f ⟨x, h⟩ b = g x b} (h : start = l.size) :
|
||||
l.foldr f x start 0 = l.unattach.foldr g x := by
|
||||
(hf : ∀ x h b, f ⟨x, h⟩ b = g x b) (h : start = xs.size) :
|
||||
xs.foldr f x start 0 = xs.unattach.foldr g x := by
|
||||
subst h
|
||||
rwa [foldr_subtype]
|
||||
|
||||
@@ -542,40 +663,155 @@ theorem foldr_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
This lemma identifies maps over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem map_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → β} {g : α → β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.map f = l.unattach.map g := by
|
||||
cases l
|
||||
@[simp] theorem map_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → β} {g : α → β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.map f = xs.unattach.map g := by
|
||||
cases xs
|
||||
simp only [List.map_toArray, List.unattach_toArray]
|
||||
rw [List.map_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem filterMap_subtype {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → Option β} {g : α → Option β} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
l.filterMap f = l.unattach.filterMap g := by
|
||||
cases l
|
||||
simp only [size_toArray, List.filterMap_toArray', List.unattach_toArray, List.length_unattach,
|
||||
@[simp] theorem filterMap_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Option β} {g : α → Option β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.filterMap f = xs.unattach.filterMap g := by
|
||||
cases xs
|
||||
simp only [List.size_toArray, List.filterMap_toArray', List.unattach_toArray, List.length_unattach,
|
||||
mk.injEq]
|
||||
rw [List.filterMap_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {l : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} {hf : ∀ x h, f ⟨x, h⟩ = g x} :
|
||||
(l.filter f).unattach = l.unattach.filter g := by
|
||||
cases l
|
||||
|
||||
@[simp] theorem flatMap_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Array β} {g : α → Array β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
(xs.flatMap f) = xs.unattach.flatMap g := by
|
||||
cases xs
|
||||
simp only [List.size_toArray, List.flatMap_toArray, List.unattach_toArray, List.length_unattach,
|
||||
mk.injEq]
|
||||
rw [List.flatMap_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem findSome?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Option β} {g : α → Option β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findSome? f = xs.unattach.findSome? g := by
|
||||
cases xs
|
||||
simp
|
||||
rw [List.findSome?_subtype hf]
|
||||
|
||||
@[simp] theorem find?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
(xs.find? f).map Subtype.val = xs.unattach.find? g := by
|
||||
cases xs
|
||||
simp
|
||||
rw [List.find?_subtype hf]
|
||||
|
||||
@[simp] theorem all_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.all f 0 stop = xs.unattach.all g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem any_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.any f 0 stop = xs.unattach.any g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [hf]
|
||||
|
||||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {l : Array { x // p x }} :
|
||||
l.reverse.unattach = l.unattach.reverse := by
|
||||
cases l
|
||||
@[simp] theorem unattach_filter {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
(xs.filter f).unattach = xs.unattach.filter g := by
|
||||
cases xs
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem unattach_reverse {p : α → Prop} {xs : Array { x // p x }} :
|
||||
xs.reverse.unattach = xs.unattach.reverse := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_append {p : α → Prop} {l₁ l₂ : Array { x // p x }} :
|
||||
(l₁ ++ l₂).unattach = l₁.unattach ++ l₂.unattach := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
@[simp] theorem unattach_append {p : α → Prop} {xs₁ xs₂ : Array { x // p x }} :
|
||||
(xs₁ ++ xs₂).unattach = xs₁.unattach ++ xs₂.unattach := by
|
||||
cases xs₁
|
||||
cases xs₂
|
||||
simp
|
||||
|
||||
@[simp] theorem unattach_flatten {p : α → Prop} {xs : Array (Array { x // p x })} :
|
||||
xs.flatten.unattach = (xs.map unattach).flatten := by
|
||||
unfold unattach
|
||||
cases xs using array₂_induction
|
||||
simp only [flatten_toArray, List.map_map, Function.comp_def, List.map_id_fun', id_eq,
|
||||
List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
|
||||
simp only [List.unattach]
|
||||
|
||||
@[simp] theorem unattach_replicate {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||||
(Array.replicate n x).unattach = Array.replicate n x.1 := by
|
||||
simp [unattach]
|
||||
|
||||
@[deprecated unattach_replicate (since := "2025-03-18")]
|
||||
abbrev unattach_mkArray := @unattach_replicate
|
||||
|
||||
/-! ### Well-founded recursion preprocessing setup -/
|
||||
|
||||
@[wf_preprocess] theorem map_wfParam {xs : Array α} {f : α → β} :
|
||||
(wfParam xs).map f = xs.attach.unattach.map f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem map_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → β} :
|
||||
xs.unattach.map f = xs.map fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldl_wfParam {xs : Array α} {f : β → α → β} {x : β} :
|
||||
(wfParam xs).foldl f x = xs.attach.unattach.foldl f x := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldl_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : β → α → β} {x : β} :
|
||||
xs.unattach.foldl f x = xs.foldl (fun s ⟨x, h⟩ =>
|
||||
binderNameHint s f <| binderNameHint x (f s) <| binderNameHint h () <| f s (wfParam x)) x := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldr_wfParam {xs : Array α} {f : α → β → β} {x : β} :
|
||||
(wfParam xs).foldr f x = xs.attach.unattach.foldr f x := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldr_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → β → β} {x : β} :
|
||||
xs.unattach.foldr f x = xs.foldr (fun ⟨x, h⟩ s =>
|
||||
binderNameHint x f <| binderNameHint s (f x) <| binderNameHint h () <| f (wfParam x) s) x := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem filter_wfParam {xs : Array α} {f : α → Bool} :
|
||||
(wfParam xs).filter f = xs.attach.unattach.filter f:= by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem filter_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Bool} :
|
||||
xs.unattach.filter f = (xs.filter (fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x))).unattach := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem reverse_wfParam {xs : Array α} :
|
||||
(wfParam xs).reverse = xs.attach.unattach.reverse := by simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem reverse_unattach {P : α → Prop} {xs : Array (Subtype P)} :
|
||||
xs.unattach.reverse = xs.reverse.unattach := by simp
|
||||
|
||||
@[wf_preprocess] theorem filterMap_wfParam {xs : Array α} {f : α → Option β} :
|
||||
(wfParam xs).filterMap f = xs.attach.unattach.filterMap f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem filterMap_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Option β} :
|
||||
xs.unattach.filterMap f = xs.filterMap fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem flatMap_wfParam {xs : Array α} {f : α → Array β} :
|
||||
(wfParam xs).flatMap f = xs.attach.unattach.flatMap f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem flatMap_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Array β} :
|
||||
xs.unattach.flatMap f = xs.flatMap fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
end Array
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,22 +8,25 @@ import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.NotationExtra
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : as = bs ∧ a = b := by
|
||||
simp only [push, mk.injEq] at h
|
||||
have ⟨h₁, h₂⟩ := List.of_concat_eq_concat h
|
||||
cases as; cases bs
|
||||
simp_all
|
||||
|
||||
private theorem List.size_toArrayAux (as : List α) (bs : Array α) : (as.toArrayAux bs).size = as.length + bs.size := by
|
||||
private theorem List.size_toArrayAux {as : List α} {bs : Array α} : (as.toArrayAux bs).size = as.length + bs.size := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [toArrayAux]
|
||||
| cons a as ih => simp_arith [toArrayAux, *]
|
||||
| cons a as ih => simp +arith [toArrayAux, *]
|
||||
|
||||
private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Array α} (h : as.toArrayAux cs = bs.toArrayAux ds) (hlen : cs.size = ds.size) : as = bs ∧ cs = ds := by
|
||||
match as, bs with
|
||||
| [], [] => simp [toArrayAux] at h; simp [h]
|
||||
| a::as, [] => simp [toArrayAux] at h; rw [← h] at hlen; simp_arith [size_toArrayAux] at hlen
|
||||
| [], b::bs => simp [toArrayAux] at h; rw [h] at hlen; simp_arith [size_toArrayAux] at hlen
|
||||
| a::as, [] => simp [toArrayAux] at h; rw [← h] at hlen; simp +arith [size_toArrayAux] at hlen
|
||||
| [], b::bs => simp [toArrayAux] at h; rw [h] at hlen; simp +arith [size_toArrayAux] at hlen
|
||||
| a::as, b::bs =>
|
||||
simp [toArrayAux] at h
|
||||
have : (cs.push a).size = (ds.push b).size := by simp [*]
|
||||
@@ -32,9 +35,14 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
|
||||
have := Array.of_push_eq_push ih₂
|
||||
simp [this]
|
||||
|
||||
theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
|
||||
theorem List.toArray_eq_toArray_eq {as bs : List α} : (as.toArray = bs.toArray) = (as = bs) := by
|
||||
simp
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` to every element in the array, left-to-right, and returns the array
|
||||
of results. Furthermore, the resulting array's type guarantees that it contains the same number of
|
||||
elements as the input array.
|
||||
-/
|
||||
def Array.mapM' [Monad m] (f : α → m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
|
||||
go 0 ⟨mkEmpty as.size, rfl⟩ (by simp)
|
||||
where
|
||||
@@ -63,11 +71,19 @@ where
|
||||
return as
|
||||
|
||||
/--
|
||||
Monomorphic `Array.mapM`. The internal implementation uses pointer equality, and does not allocate a new array
|
||||
if the result of each `f a` is a pointer equal value `a`.
|
||||
Applies a monadic function to each element of an array, returning the array of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new array if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
as.mapM f
|
||||
|
||||
/--
|
||||
Applies a function to each element of an array, returning the array of results. The function is
|
||||
monomorphic: it is required to return a value of the same type. The internal implementation uses
|
||||
pointer equality, and does not allocate a new array if the result of each function call is
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[inline] def Array.mapMono (as : Array α) (f : α → α) : Array α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
@@ -5,9 +5,13 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Int.DivMod.Lemmas
|
||||
import Init.Omega
|
||||
universe u v
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- We do not use `linter.indexVariables` here as it is helpful to name the index variables as `lo`, `mid`, and `hi`.
|
||||
|
||||
namespace Array
|
||||
|
||||
@[specialize] def binSearchAux {α : Type u} {β : Type v} (lt : α → α → Bool) (found : Option α → β) (as : Array α) (k : α) :
|
||||
@@ -25,6 +29,16 @@ namespace Array
|
||||
else found (some a)
|
||||
termination_by lo hi => hi.1 - lo.1
|
||||
|
||||
/--
|
||||
Binary search for an element equivalent to `k` in the sorted array `as`. Returns the element from
|
||||
the array, if it is found, or `none` otherwise.
|
||||
|
||||
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
|
||||
order.
|
||||
|
||||
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
|
||||
are inclusive, and default to searching the entire array.
|
||||
-/
|
||||
@[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α → α → Bool) (lo := 0) (hi := as.size - 1) : Option α :=
|
||||
if h : lo < as.size then
|
||||
let hi := if hi < as.size then hi else as.size - 1
|
||||
@@ -35,6 +49,16 @@ termination_by lo hi => hi.1 - lo.1
|
||||
else
|
||||
none
|
||||
|
||||
/--
|
||||
Binary search for an element equivalent to `k` in the sorted array `as`. Returns `true` if the
|
||||
element is found, or `false` otherwise.
|
||||
|
||||
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
|
||||
order.
|
||||
|
||||
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
|
||||
are inclusive, and default to searching the entire array.
|
||||
-/
|
||||
@[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α → α → Bool) (lo := 0) (hi := as.size - 1) : Bool :=
|
||||
if h : lo < as.size then
|
||||
let hi := if hi < as.size then hi else as.size - 1
|
||||
@@ -64,6 +88,16 @@ termination_by lo hi => hi.1 - lo.1
|
||||
as.modifyM mid <| fun v => merge v
|
||||
termination_by lo hi => hi.1 - lo.1
|
||||
|
||||
/--
|
||||
Inserts an element `k` into a sorted array `as` such that the resulting array is sorted.
|
||||
|
||||
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
|
||||
with respect to `lt`.
|
||||
|
||||
If an element that `lt` equates to `k` is already present in `as`, then `merge` is applied to the
|
||||
existing element to determine the value of that position in the resulting array. If no element equal
|
||||
to `k` is present, then `add` is used to determine the value to be inserted.
|
||||
-/
|
||||
@[specialize] def binInsertM {α : Type u} {m : Type u → Type v} [Monad m]
|
||||
(lt : α → α → Bool)
|
||||
(merge : α → m α)
|
||||
@@ -77,6 +111,21 @@ termination_by lo hi => hi.1 - lo.1
|
||||
else if !lt k as[as.size - 1] then as.modifyM (as.size - 1) <| merge
|
||||
else binInsertAux lt merge add as k ⟨0, by omega⟩ ⟨as.size - 1, by omega⟩ (by simp) (by simpa using h')
|
||||
|
||||
/--
|
||||
Inserts an element into a sorted array such that the resulting array is sorted. If the element is
|
||||
already present in the array, it is not inserted.
|
||||
|
||||
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
|
||||
with respect to `lt`.
|
||||
|
||||
`Array.binInsertM` is a more general operator that provides greater control over the handling of
|
||||
duplicate elements in addition to running in a monad.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 3, 5].binInsert (· < ·) 2 = #[0, 1, 2, 3, 5]`
|
||||
* `#[0, 1, 3, 5].binInsert (· < ·) 1 = #[0, 1, 3, 5]`
|
||||
* `#[].binInsert (· < ·) 1 = #[1]`
|
||||
-/
|
||||
@[inline] def binInsert {α : Type u} (lt : α → α → Bool) (as : Array α) (k : α) : Array α :=
|
||||
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
|
||||
|
||||
|
||||
@@ -13,122 +13,148 @@ import Init.Data.List.TakeDrop
|
||||
This file contains some theorems about `Array` and `List` needed for `Init.Data.List.Impl`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]` instead.
|
||||
|
||||
Access an element from an array without needing a runtime bounds checks,
|
||||
using a `Nat` index and a proof that it is in bounds.
|
||||
|
||||
This function does not use `get_elem_tactic` to automatically find the proof that
|
||||
the index is in bounds. This is because the tactic itself needs to look up values in
|
||||
arrays.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]` instead" (since := "2025-02-17")]
|
||||
def get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size) : α :=
|
||||
a.toList.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Use the indexing notation `a[i]!` instead.
|
||||
|
||||
Access an element from an array, or panic if the index is out of bounds.
|
||||
-/
|
||||
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17")]
|
||||
def get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
|
||||
Array.getD a i default
|
||||
|
||||
theorem foldlM_toList.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.toList.drop j).foldlM f b := by
|
||||
{f : β → α → m β} {xs : Array α} {i j} (H : xs.size ≤ i + j) {b} :
|
||||
foldlM.loop f xs xs.size (Nat.le_refl _) i j b = (xs.toList.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_toList.aux f arr i (j+1) H]
|
||||
simp [foldlM_toList.aux (j := j+1) H]
|
||||
rw (occs := [2]) [← List.getElem_cons_drop_succ_eq_drop ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
@[simp] theorem foldlM_toList [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldlM f init = arr.foldlM f init := by
|
||||
{f : β → α → m β} {init : β} {xs : Array α} :
|
||||
xs.toList.foldlM f init = xs.foldlM f init := by
|
||||
simp [foldlM, foldlM_toList.aux]
|
||||
|
||||
@[simp] theorem foldl_toList (f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldl f init = arr.foldl f init :=
|
||||
@[simp] theorem foldl_toList (f : β → α → β) {init : β} {xs : Array α} :
|
||||
xs.toList.foldl f init = xs.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_toList ..
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
|
||||
(f : α → β → m β) (arr : Array α) (init : β) (i h) :
|
||||
(arr.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
|
||||
{f : α → β → m β} {xs : Array α} {init : β} {i} (h) :
|
||||
(xs.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f xs 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)]
|
||||
| i+1 => rw [← List.take_concat_get h]; simp [← aux]
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.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 => ?_
|
||||
theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init : β} {xs : Array α} :
|
||||
xs.foldrM f init = xs.toList.reverse.foldlM (fun x y => f y x) init := by
|
||||
have : xs = #[] ∨ 0 < xs.size :=
|
||||
match xs with | ⟨[]⟩ => .inl rfl | ⟨a::l⟩ => .inr (Nat.zero_lt_succ _)
|
||||
match xs, this with | _, .inl rfl => rfl | xs, .inr h => ?_
|
||||
simp [foldrM, h, ← foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
|
||||
|
||||
@[simp] theorem foldrM_toList [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldrM f init = arr.foldrM f init := by
|
||||
{f : α → β → m β} {init : β} {xs : Array α} :
|
||||
xs.toList.foldrM f init = xs.foldrM f init := by
|
||||
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
|
||||
|
||||
@[simp] theorem foldr_toList (f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.toList.foldr f init = arr.foldr f init :=
|
||||
@[simp] theorem foldr_toList (f : α → β → β) {init : β} {xs : Array α} :
|
||||
xs.toList.foldr f init = xs.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_toList ..
|
||||
|
||||
@[simp] theorem push_toList (arr : Array α) (a : α) : (arr.push a).toList = arr.toList ++ [a] := by
|
||||
@[simp] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
|
||||
simp [push, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.toList ++ l := by
|
||||
@[simp] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
|
||||
simp [toListAppend, ← foldr_toList]
|
||||
|
||||
@[simp] theorem toListImpl_eq (arr : Array α) : arr.toListImpl = arr.toList := by
|
||||
@[simp] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
|
||||
simp [toListImpl, ← foldr_toList]
|
||||
|
||||
@[simp] theorem pop_toList (arr : Array α) : arr.pop.toList = arr.toList.dropLast := rfl
|
||||
@[simp] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
|
||||
|
||||
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
|
||||
@[deprecated toList_pop (since := "2025-02-17")]
|
||||
abbrev pop_toList := @Array.toList_pop
|
||||
|
||||
@[simp] theorem toList_append (arr arr' : Array α) :
|
||||
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
|
||||
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
|
||||
|
||||
@[simp] theorem toList_append {xs ys : Array α} :
|
||||
(xs ++ ys).toList = xs.toList ++ ys.toList := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [← foldl_toList]
|
||||
induction arr'.toList generalizing arr <;> simp [*]
|
||||
induction ys.toList generalizing xs <;> simp [*]
|
||||
|
||||
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
|
||||
|
||||
@[simp] theorem append_empty (as : Array α) : as ++ #[] = as := by
|
||||
@[simp] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
|
||||
|
||||
@[deprecated append_empty (since := "2025-01-13")]
|
||||
abbrev append_nil := @append_empty
|
||||
|
||||
@[simp] theorem empty_append (as : Array α) : #[] ++ as = as := by
|
||||
@[simp] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
|
||||
|
||||
@[deprecated empty_append (since := "2025-01-13")]
|
||||
abbrev nil_append := @empty_append
|
||||
|
||||
@[simp] theorem append_assoc (as bs cs : Array α) : as ++ bs ++ cs = as ++ (bs ++ cs) := by
|
||||
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
|
||||
apply ext'; simp only [toList_append, List.append_assoc]
|
||||
|
||||
@[simp] theorem appendList_eq_append
|
||||
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
|
||||
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
|
||||
|
||||
@[simp] theorem toList_appendList (arr : Array α) (l : List α) :
|
||||
(arr ++ l).toList = arr.toList ++ l := by
|
||||
@[simp] theorem toList_appendList {xs : Array α} {l : List α} :
|
||||
(xs ++ l).toList = xs.toList ++ l := by
|
||||
rw [← appendList_eq_append]; unfold Array.appendList
|
||||
induction l generalizing arr <;> simp [*]
|
||||
induction l generalizing xs <;> simp [*]
|
||||
|
||||
@[deprecated toList_appendList (since := "2024-12-11")]
|
||||
abbrev appendList_toList := @toList_appendList
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldrM_toList`." (since := "2024-11-13")]
|
||||
theorem foldrM_eq_foldrM_toList [Monad m]
|
||||
(f : α → β → m β) (init : β) (arr : Array α) :
|
||||
arr.foldrM f init = arr.toList.foldrM f init := by
|
||||
{f : α → β → m β} {init : β} {xs : Array α} :
|
||||
xs.foldrM f init = xs.toList.foldrM f init := by
|
||||
simp
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldlM_toList`." (since := "2024-11-13")]
|
||||
theorem foldlM_eq_foldlM_toList [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
arr.foldlM f init = arr.toList.foldlM f init:= by
|
||||
{f : β → α → m β} {init : β} {xs : Array α} :
|
||||
xs.foldlM f init = xs.toList.foldlM f init:= by
|
||||
simp
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldr_toList`." (since := "2024-11-13")]
|
||||
theorem foldr_eq_foldr_toList
|
||||
(f : α → β → β) (init : β) (arr : Array α) :
|
||||
arr.foldr f init = arr.toList.foldr f init := by
|
||||
theorem foldr_eq_foldr_toList {f : α → β → β} {init : β} {xs : Array α} :
|
||||
xs.foldr f init = xs.toList.foldr f init := by
|
||||
simp
|
||||
|
||||
@[deprecated "Use the reverse direction of `foldl_toList`." (since := "2024-11-13")]
|
||||
theorem foldl_eq_foldl_toList
|
||||
(f : β → α → β) (init : β) (arr : Array α) :
|
||||
arr.foldl f init = arr.toList.foldl f init:= by
|
||||
theorem foldl_eq_foldl_toList {f : β → α → β} {init : β} {xs : Array α} :
|
||||
xs.foldl f init = xs.toList.foldl f init:= by
|
||||
simp
|
||||
|
||||
@[deprecated foldlM_toList (since := "2024-09-09")]
|
||||
@@ -153,7 +179,7 @@ abbrev push_data := @push_toList
|
||||
abbrev toList_eq := @toListImpl_eq
|
||||
|
||||
@[deprecated pop_toList (since := "2024-09-09")]
|
||||
abbrev pop_data := @pop_toList
|
||||
abbrev pop_data := @toList_pop
|
||||
|
||||
@[deprecated toList_append (since := "2024-09-09")]
|
||||
abbrev append_data := @toList_append
|
||||
|
||||
302
src/Init/Data/Array/Count.lean
Normal file
302
src/Init/Data/Array/Count.lean
Normal file
@@ -0,0 +1,302 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.Count
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.countP` and `Array.count`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### countP -/
|
||||
section countP
|
||||
|
||||
variable {p q : α → Bool}
|
||||
|
||||
@[simp] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
|
||||
simp [countP]
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons hd tl ih =>
|
||||
simp only [List.foldr_cons, ih, List.countP_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
|
||||
|
||||
@[simp] theorem countP_push_of_pos {xs : Array α} (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_push_of_neg {xs : Array α} (pa : ¬p a) : countP p (xs.push a) = countP p xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p xs + if p a then 1 else 0 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
|
||||
simp [countP_push]
|
||||
|
||||
theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + countP (fun a => ¬p a) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.length_eq_countP_add_countP (p := p)]
|
||||
|
||||
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_eq_length_filter]
|
||||
|
||||
theorem countP_eq_size_filter' : countP p = size ∘ filter p := by
|
||||
funext xs
|
||||
apply countP_eq_size_filter
|
||||
|
||||
theorem countP_le_size : countP p xs ≤ xs.size := by
|
||||
simp only [countP_eq_size_filter]
|
||||
apply size_filter_le
|
||||
|
||||
@[simp] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_pos_iff {p} : 0 < countP p xs ↔ ∃ a ∈ xs, p a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem one_le_countP_iff {p} : 1 ≤ countP p xs ↔ ∃ a ∈ xs, p a :=
|
||||
countP_pos_iff
|
||||
|
||||
@[simp] theorem countP_eq_zero {p} : countP p xs = 0 ↔ ∀ a ∈ xs, ¬p a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_eq_size {p} : countP p xs = xs.size ↔ ∀ a ∈ xs, p a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem countP_replicate {a : α} {n : Nat} : countP p (replicate n a) = if p a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.countP_replicate]
|
||||
|
||||
@[deprecated countP_replicate (since := "2025-03-18")]
|
||||
abbrev countP_mkArray := @countP_replicate
|
||||
|
||||
theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
(if p xs[i] then 1 else 0) ≤ xs.countP p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.boole_getElem_le_countP]
|
||||
|
||||
theorem countP_set {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
|
||||
(xs.set i a).countP p = xs.countP p - (if p xs[i] then 1 else 0) + (if p a then 1 else 0) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_set, h]
|
||||
|
||||
theorem countP_filter {xs : Array α} :
|
||||
countP p (filter q xs) = countP (fun a => p a && q a) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_filter]
|
||||
|
||||
@[simp] theorem countP_true : (countP fun (_ : α) => true) = size := by
|
||||
funext xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_false : (countP fun (_ : α) => false) = Function.const _ 0 := by
|
||||
funext xs
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_map {p : β → Bool} {f : α → β} {xs : Array α} :
|
||||
countP p (map f xs) = countP (p ∘ f) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem size_filterMap_eq_countP {f : α → Option β} {xs : Array α} :
|
||||
(filterMap f xs).size = countP (fun a => (f a).isSome) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.length_filterMap_eq_countP]
|
||||
|
||||
theorem countP_filterMap {p : β → Bool} {f : α → Option β} {xs : Array α} :
|
||||
countP p (filterMap f xs) = countP (fun a => ((f a).map p).getD false) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_filterMap]
|
||||
|
||||
@[simp] theorem countP_flatten {xss : Array (Array α)} :
|
||||
countP p xss.flatten = (xss.map (countP p)).sum := by
|
||||
cases xss using array₂_induction
|
||||
simp [List.countP_flatten, Function.comp_def]
|
||||
|
||||
theorem countP_flatMap {p : β → Bool} {xs : Array α} {f : α → Array β} :
|
||||
countP p (xs.flatMap f) = sum (map (countP p ∘ f) xs) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_flatMap, Function.comp_def]
|
||||
|
||||
@[simp] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.countP_reverse]
|
||||
|
||||
theorem countP_mono_left (h : ∀ x ∈ xs, p x → q x) : countP p xs ≤ countP q xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.countP_mono_left (by simpa using h)
|
||||
|
||||
theorem countP_congr (h : ∀ x ∈ xs, p x ↔ q x) : countP p xs = countP q xs :=
|
||||
Nat.le_antisymm
|
||||
(countP_mono_left fun x hx => (h x hx).1)
|
||||
(countP_mono_left fun x hx => (h x hx).2)
|
||||
|
||||
end countP
|
||||
|
||||
/-! ### count -/
|
||||
section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
|
||||
simp [count, List.count_eq_countP]
|
||||
|
||||
@[simp] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem count_empty {a : α} : count a #[] = 0 := rfl
|
||||
|
||||
theorem count_push {a b : α} {xs : Array α} :
|
||||
count a (xs.push b) = count a xs + if b == a then 1 else 0 := by
|
||||
simp [count, countP_push]
|
||||
|
||||
theorem count_eq_countP {a : α} {xs : Array α} : count a xs = countP (· == a) xs := rfl
|
||||
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
|
||||
funext xs
|
||||
apply count_eq_countP
|
||||
|
||||
theorem count_le_size {a : α} {xs : Array α} : count a xs ≤ xs.size := countP_le_size
|
||||
|
||||
theorem count_le_count_push {a b : α} {xs : Array α} : count a xs ≤ count a (xs.push b) := by
|
||||
simp [count_push]
|
||||
|
||||
theorem count_singleton {a b : α} : count a #[b] = if b == a then 1 else 0 := by
|
||||
simp [count_eq_countP]
|
||||
|
||||
@[simp] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
|
||||
countP_append
|
||||
|
||||
@[simp] theorem count_flatten {a : α} {xss : Array (Array α)} :
|
||||
count a xss.flatten = (xss.map (count a)).sum := by
|
||||
cases xss using array₂_induction
|
||||
simp [List.count_flatten, Function.comp_def]
|
||||
|
||||
@[simp] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem boole_getElem_le_count {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
|
||||
(if xs[i] == a then 1 else 0) ≤ xs.count a := by
|
||||
rw [count_eq_countP]
|
||||
apply boole_getElem_le_countP (p := (· == a))
|
||||
|
||||
theorem count_set {xs : Array α} {i : Nat} {a b : α} (h : i < xs.size) :
|
||||
(xs.set i a).count b = xs.count b - (if xs[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
|
||||
simp [count_eq_countP, countP_set, h]
|
||||
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem count_push_self {a : α} {xs : Array α} : count a (xs.push a) = count a xs + 1 := by
|
||||
simp [count_push]
|
||||
|
||||
@[simp] theorem count_push_of_ne {xs : Array α} (h : b ≠ a) : count a (xs.push b) = count a xs := by
|
||||
simp_all [count_push, h]
|
||||
|
||||
theorem count_singleton_self {a : α} : count a #[a] = 1 := by simp
|
||||
|
||||
@[simp]
|
||||
theorem count_pos_iff {a : α} {xs : Array α} : 0 < count a xs ↔ a ∈ xs := by
|
||||
simp only [count, countP_pos_iff, beq_iff_eq, exists_eq_right]
|
||||
|
||||
@[simp] theorem one_le_count_iff {a : α} {xs : Array α} : 1 ≤ count a xs ↔ a ∈ xs :=
|
||||
count_pos_iff
|
||||
|
||||
theorem count_eq_zero_of_not_mem {a : α} {xs : Array α} (h : a ∉ xs) : count a xs = 0 :=
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff.1 (Nat.pos_of_ne_zero h')
|
||||
|
||||
theorem not_mem_of_count_eq_zero {a : α} {xs : Array α} (h : count a xs = 0) : a ∉ xs :=
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff.2 h') h.symm
|
||||
|
||||
theorem count_eq_zero {xs : Array α} : count a xs = 0 ↔ a ∉ xs :=
|
||||
⟨not_mem_of_count_eq_zero, count_eq_zero_of_not_mem⟩
|
||||
|
||||
theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a = b := by
|
||||
rw [count, countP_eq_size]
|
||||
refine ⟨fun h b hb => Eq.symm ?_, fun h b hb => ?_⟩
|
||||
· simpa using h b hb
|
||||
· rw [h b hb, beq_self_eq_true]
|
||||
|
||||
@[simp] theorem count_replicate_self {a : α} {n : Nat} : count a (replicate n a) = n := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated count_replicate_self (since := "2025-03-18")]
|
||||
abbrev count_mkArray_self := @count_replicate_self
|
||||
|
||||
theorem count_replicate {a b : α} {n : Nat} : count a (replicate n b) = if b == a then n else 0 := by
|
||||
simp [← List.toArray_replicate, List.count_replicate]
|
||||
|
||||
@[deprecated count_replicate (since := "2025-03-18")]
|
||||
abbrev count_mkArray := @count_replicate
|
||||
|
||||
theorem filter_beq {xs : Array α} (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.filter_beq]
|
||||
|
||||
theorem filter_eq {α} [DecidableEq α] {xs : Array α} (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
|
||||
filter_beq a
|
||||
|
||||
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
|
||||
replicate (count a xs) a = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
|
||||
|
||||
@[deprecated replicate_count_eq_of_count_eq_size (since := "2025-03-18")]
|
||||
abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
|
||||
|
||||
@[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.count_filter, h]
|
||||
|
||||
theorem count_le_count_map [DecidableEq β] {xs : Array α} {f : α → β} {x : α} :
|
||||
count x xs ≤ count (f x) (map f xs) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.count_le_count_map, countP_map]
|
||||
|
||||
theorem count_filterMap {α} [BEq β] {b : β} {f : α → Option β} {xs : Array α} :
|
||||
count b (filterMap f xs) = countP (fun a => f a == some b) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.count_filterMap, countP_filterMap]
|
||||
|
||||
theorem count_flatMap {α} [BEq β] {xs : Array α} {f : α → Array β} {x : β} :
|
||||
count x (xs.flatMap f) = sum (map (count x ∘ f) xs) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.count_flatMap, countP_flatMap, Function.comp_def]
|
||||
|
||||
-- FIXME these theorems can be restored once `List.erase` and `Array.erase` have been related.
|
||||
|
||||
-- theorem count_erase (a b : α) (l : Array α) : count a (l.erase b) = count a l - if b == a then 1 else 0 := by
|
||||
-- sorry
|
||||
|
||||
-- @[simp] theorem count_erase_self (a : α) (l : Array α) :
|
||||
-- count a (l.erase a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
|
||||
|
||||
-- @[simp] theorem count_erase_of_ne (ab : a ≠ b) (l : Array α) : count a (l.erase b) = count a l := by
|
||||
-- rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
|
||||
|
||||
end count
|
||||
@@ -9,12 +9,15 @@ import Init.Data.BEq
|
||||
import Init.Data.List.Nat.BEq
|
||||
import Init.ByCases
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem rel_of_isEqvAux
|
||||
{r : α → α → Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i ≤ a.size)
|
||||
(heqv : Array.isEqvAux a b hsz r i hi)
|
||||
{j : Nat} (hj : j < i) : r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi))) := by
|
||||
private theorem rel_of_isEqvAux
|
||||
{r : α → α → Bool} {xs ys : Array α} (hsz : xs.size = ys.size) {i : Nat} (hi : i ≤ xs.size)
|
||||
(heqv : Array.isEqvAux xs ys hsz r i hi)
|
||||
{j : Nat} (hj : j < i) : r (xs[j]'(Nat.lt_of_lt_of_le hj hi)) (ys[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi))) := by
|
||||
induction i with
|
||||
| zero => contradiction
|
||||
| succ i ih =>
|
||||
@@ -27,31 +30,32 @@ theorem rel_of_isEqvAux
|
||||
subst hj'
|
||||
exact heqv.left
|
||||
|
||||
theorem isEqvAux_of_rel {r : α → α → Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i ≤ a.size)
|
||||
(w : ∀ j, (hj : j < i) → r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi)))) : Array.isEqvAux a b hsz r i hi := by
|
||||
private theorem isEqvAux_of_rel {r : α → α → Bool} {xs ys : Array α} (hsz : xs.size = ys.size) {i : Nat} (hi : i ≤ xs.size)
|
||||
(w : ∀ j, (hj : j < i) → r (xs[j]'(Nat.lt_of_lt_of_le hj hi)) (ys[j]'(Nat.lt_of_lt_of_le hj (hsz ▸ hi)))) : Array.isEqvAux xs ys hsz r i hi := by
|
||||
induction i with
|
||||
| zero => simp [Array.isEqvAux]
|
||||
| succ i ih =>
|
||||
simp only [isEqvAux, Bool.and_eq_true]
|
||||
exact ⟨w i (Nat.lt_add_one i), ih _ fun j hj => w j (Nat.lt_add_right 1 hj)⟩
|
||||
|
||||
theorem rel_of_isEqv {r : α → α → Bool} {a b : Array α} :
|
||||
Array.isEqv a b r → ∃ h : a.size = b.size, ∀ (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h ▸ h')) := by
|
||||
-- This is private as the forward direction of `isEqv_iff_rel` may be used.
|
||||
private theorem rel_of_isEqv {r : α → α → Bool} {xs ys : Array α} :
|
||||
Array.isEqv xs ys r → ∃ h : xs.size = ys.size, ∀ (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h ▸ h')) := by
|
||||
simp only [isEqv]
|
||||
split <;> rename_i h
|
||||
· exact fun h' => ⟨h, fun i => rel_of_isEqvAux h (Nat.le_refl ..) h'⟩
|
||||
· intro; contradiction
|
||||
|
||||
theorem isEqv_iff_rel {a b : Array α} {r} :
|
||||
Array.isEqv a b r ↔ ∃ h : a.size = b.size, ∀ (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h ▸ h')) :=
|
||||
theorem isEqv_iff_rel {xs ys : Array α} {r} :
|
||||
Array.isEqv xs ys r ↔ ∃ h : xs.size = ys.size, ∀ (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h ▸ h')) :=
|
||||
⟨rel_of_isEqv, fun ⟨h, w⟩ => by
|
||||
simp only [isEqv, ← h, ↓reduceDIte]
|
||||
exact isEqvAux_of_rel h (by simp [h]) w⟩
|
||||
|
||||
theorem isEqv_eq_decide (a b : Array α) (r) :
|
||||
Array.isEqv a b r =
|
||||
if h : a.size = b.size then decide (∀ (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h ▸ h'))) else false := by
|
||||
by_cases h : Array.isEqv a b r
|
||||
theorem isEqv_eq_decide (xs ys : Array α) (r) :
|
||||
Array.isEqv xs ys r =
|
||||
if h : xs.size = ys.size then decide (∀ (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h ▸ h'))) else false := by
|
||||
by_cases h : Array.isEqv xs ys r
|
||||
· simp only [h, Bool.true_eq]
|
||||
simp only [isEqv_iff_rel] at h
|
||||
obtain ⟨h, w⟩ := h
|
||||
@@ -62,48 +66,58 @@ theorem isEqv_eq_decide (a b : Array α) (r) :
|
||||
Bool.not_eq_true]
|
||||
simpa [isEqv_iff_rel] using h'
|
||||
|
||||
@[simp] theorem isEqv_toList [BEq α] (a b : Array α) : (a.toList.isEqv b.toList r) = (a.isEqv b r) := by
|
||||
@[simp] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
|
||||
simp [isEqv_eq_decide, List.isEqv_eq_decide]
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) (h : Array.isEqv a b (fun x y => x = y)) : a = b := by
|
||||
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
|
||||
have ⟨h, h'⟩ := rel_of_isEqv h
|
||||
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
|
||||
exact ext h (fun i lt _ => by simpa using h' i lt)
|
||||
|
||||
theorem isEqvAux_self (r : α → α → Bool) (hr : ∀ a, r a a) (a : Array α) (i : Nat) (h : i ≤ a.size) :
|
||||
Array.isEqvAux a a rfl r i h = true := by
|
||||
private theorem isEqvAux_self (r : α → α → Bool) (hr : ∀ a, r a a) (xs : Array α) (i : Nat) (h : i ≤ xs.size) :
|
||||
Array.isEqvAux xs xs rfl r i h = true := by
|
||||
induction i with
|
||||
| zero => simp [Array.isEqvAux]
|
||||
| succ i ih =>
|
||||
simp_all only [isEqvAux, Bool.and_self]
|
||||
|
||||
theorem isEqv_self_beq [BEq α] [ReflBEq α] (a : Array α) : Array.isEqv a a (· == ·) = true := by
|
||||
theorem isEqv_self_beq [BEq α] [ReflBEq α] (xs : Array α) : Array.isEqv xs xs (· == ·) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (· = ·) = true := by
|
||||
theorem isEqv_self [DecidableEq α] (xs : Array α) : Array.isEqv xs xs (· = ·) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
instance [DecidableEq α] : DecidableEq (Array α) :=
|
||||
fun a b =>
|
||||
match h:isEqv a b (fun a b => a = b) with
|
||||
| true => isTrue (eq_of_isEqv a b h)
|
||||
fun xs ys =>
|
||||
match h:isEqv xs ys (fun a b => a = b) with
|
||||
| true => isTrue (eq_of_isEqv xs ys h)
|
||||
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
|
||||
|
||||
theorem beq_eq_decide [BEq α] (a b : Array α) :
|
||||
(a == b) = if h : a.size = b.size then
|
||||
decide (∀ (i : Nat) (h' : i < a.size), a[i] == b[i]'(h ▸ h')) else false := by
|
||||
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
|
||||
(xs == ys) = if h : xs.size = ys.size then
|
||||
decide (∀ (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h ▸ h')) else false := by
|
||||
simp [BEq.beq, isEqv_eq_decide]
|
||||
|
||||
@[simp] theorem beq_toList [BEq α] (a b : Array α) : (a.toList == b.toList) = (a == b) := by
|
||||
@[simp] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
|
||||
simp [beq_eq_decide, List.beq_eq_decide]
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem isEqv_toArray [BEq α] (a b : List α) : (a.toArray.isEqv b.toArray r) = (a.isEqv b r) := by
|
||||
@[simp] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
|
||||
simp [isEqv_eq_decide, Array.isEqv_eq_decide]
|
||||
|
||||
@[simp] theorem beq_toArray [BEq α] (a b : List α) : (a.toArray == b.toArray) = (a == b) := by
|
||||
@[simp] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
|
||||
simp [beq_eq_decide, Array.beq_eq_decide]
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
|
||||
rfl := by simp [BEq.beq, isEqv_self_beq]
|
||||
eq_of_beq := by
|
||||
rintro ⟨_⟩ ⟨_⟩ h
|
||||
simpa using h
|
||||
|
||||
end Array
|
||||
|
||||
434
src/Init/Data/Array/Erase.lean
Normal file
434
src/Init/Data/Array/Erase.lean
Normal file
@@ -0,0 +1,434 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.Erase
|
||||
import Init.Data.List.Nat.Basic
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.eraseP`, `Array.erase`, and `Array.eraseIdx`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### eraseP -/
|
||||
|
||||
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
|
||||
|
||||
theorem eraseP_of_forall_mem_not {xs : Array α} (h : ∀ a, a ∈ xs → ¬p a) : xs.eraseP p = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all [List.eraseP_of_forall_not]
|
||||
|
||||
theorem eraseP_of_forall_getElem_not {xs : Array α} (h : ∀ i, (h : i < xs.size) → ¬p xs[i]) : xs.eraseP p = xs :=
|
||||
eraseP_of_forall_mem_not fun a m => by
|
||||
rw [mem_iff_getElem] at m
|
||||
obtain ⟨i, w, rfl⟩ := m
|
||||
exact h i w
|
||||
|
||||
@[simp] theorem eraseP_eq_empty_iff {xs : Array α} {p : α → Bool} : xs.eraseP p = #[] ↔ xs = #[] ∨ ∃ x, p x ∧ xs = #[x] := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
theorem eraseP_ne_empty_iff {xs : Array α} {p : α → Bool} : xs.eraseP p ≠ #[] ↔ xs ≠ #[] ∧ ∀ x, p x → xs ≠ #[x] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_eraseP {xs : Array α} {a} (hm : a ∈ xs) (hp : p a) :
|
||||
∃ a ys zs, (∀ b ∈ ys, ¬p b) ∧ p a ∧ xs = ys.push a ++ zs ∧ xs.eraseP p = ys ++ zs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
obtain ⟨a, l₁, l₂, h₁, h₂, rfl, h₃⟩ := List.exists_of_eraseP (by simpa using hm) (hp)
|
||||
refine ⟨a, ⟨l₁⟩, ⟨l₂⟩, by simpa using h₁, h₂, by simp, by simpa using h₃⟩
|
||||
|
||||
-- The arguments are explicit here, so this lemma can be used as a case split.
|
||||
theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
|
||||
xs.eraseP p = xs ∨
|
||||
∃ a ys zs, (∀ b ∈ ys, ¬p b) ∧ p a ∧ xs = ys.push a ++ zs ∧ xs.eraseP p = ys ++ zs :=
|
||||
if h : ∃ a ∈ xs, p a then
|
||||
let ⟨_, ha, pa⟩ := h
|
||||
.inr (exists_of_eraseP ha pa)
|
||||
else
|
||||
.inl (eraseP_of_forall_mem_not (h ⟨·, ·, ·⟩))
|
||||
|
||||
@[simp] theorem size_eraseP_of_mem {xs : Array α} (al : a ∈ xs) (pa : p a) :
|
||||
(xs.eraseP p).size = xs.size - 1 := by
|
||||
let ⟨_, ys, zs, _, _, e₁, e₂⟩ := exists_of_eraseP al pa
|
||||
rw [e₂]; simp [size_append, e₁]; omega
|
||||
|
||||
theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.size - 1 else xs.size := by
|
||||
split <;> rename_i h
|
||||
· simp only [any_eq_true] at h
|
||||
obtain ⟨i, h, w⟩ := h
|
||||
simp [size_eraseP_of_mem (xs := xs) (by simp) w]
|
||||
· simp only [any_eq_true] at h
|
||||
rw [eraseP_of_forall_getElem_not]
|
||||
simp_all
|
||||
|
||||
theorem size_eraseP_le {xs : Array α} : (xs.eraseP p).size ≤ xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.length_eraseP_le
|
||||
|
||||
theorem le_size_eraseP {xs : Array α} : xs.size - 1 ≤ (xs.eraseP p).size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.le_length_eraseP
|
||||
|
||||
theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.mem_of_mem_eraseP
|
||||
|
||||
@[simp] theorem mem_eraseP_of_neg {xs : Array α} (pa : ¬p a) : a ∈ xs.eraseP p ↔ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.mem_eraseP_of_neg pa
|
||||
|
||||
@[simp] theorem eraseP_eq_self_iff {xs : Array α} : xs.eraseP p = xs ↔ ∀ a ∈ xs, ¬ p a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem eraseP_map {f : β → α} {xs : Array β} : (xs.map f).eraseP p = (xs.eraseP (p ∘ f)).map f := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.eraseP_map
|
||||
|
||||
theorem eraseP_filterMap {f : α → Option β} {xs : Array α} :
|
||||
(filterMap f xs).eraseP p = filterMap f (xs.eraseP (fun x => match f x with | some y => p y | none => false)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.eraseP_filterMap
|
||||
|
||||
theorem eraseP_filter {f : α → Bool} {xs : Array α} :
|
||||
(filter f xs).eraseP p = filter f (xs.eraseP (fun x => p x && f x)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.eraseP_filter
|
||||
|
||||
theorem eraseP_append_left {a : α} (pa : p a) {xs : Array α} {ys : Array α} (h : a ∈ xs) :
|
||||
(xs ++ ys).eraseP p = xs.eraseP p ++ ys := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simpa using List.eraseP_append_left pa ys (by simpa using h)
|
||||
|
||||
theorem eraseP_append_right {xs : Array α} ys (h : ∀ b ∈ xs, ¬p b) :
|
||||
(xs ++ ys).eraseP p = xs ++ ys.eraseP p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simpa using List.eraseP_append_right ys (by simpa using h)
|
||||
|
||||
theorem eraseP_append {xs : Array α} {ys : Array α} :
|
||||
(xs ++ ys).eraseP p = if xs.any p then xs.eraseP p ++ ys else xs ++ ys.eraseP p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
|
||||
split <;> simp
|
||||
|
||||
theorem eraseP_replicate {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
|
||||
split <;> simp
|
||||
|
||||
@[deprecated eraseP_replicate (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray := @eraseP_replicate
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
|
||||
(replicate n a).eraseP p = replicate (n - 1) a := by
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray]
|
||||
simp [h]
|
||||
|
||||
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
|
||||
(replicate n a).eraseP p = replicate n a := by
|
||||
simp only [← List.toArray_replicate, List.eraseP_toArray]
|
||||
simp [h]
|
||||
|
||||
@[deprecated eraseP_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev eraseP_mkArray_of_neg := @eraseP_replicate_of_neg
|
||||
|
||||
theorem eraseP_eq_iff {p} {xs : Array α} :
|
||||
xs.eraseP p = ys ↔
|
||||
((∀ a ∈ xs, ¬ p a) ∧ xs = ys) ∨
|
||||
∃ a as bs, (∀ b ∈ as, ¬ p b) ∧ p a ∧ xs = as.push a ++ bs ∧ ys = as ++ bs := by
|
||||
rcases xs with ⟨l⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp [List.eraseP_eq_iff]
|
||||
constructor
|
||||
· rintro (h | ⟨a, l₁, h₁, h₂, ⟨l, rfl, rfl⟩⟩)
|
||||
· exact Or.inl h
|
||||
· exact Or.inr ⟨a, ⟨l₁⟩, by simpa using h₁, h₂, ⟨⟨l⟩, by simp⟩⟩
|
||||
· rintro (h | ⟨a, ⟨l₁⟩, h₁, h₂, ⟨⟨l⟩, rfl, rfl⟩⟩)
|
||||
· exact Or.inl h
|
||||
· exact Or.inr ⟨a, l₁, by simpa using h₁, h₂, ⟨l, by simp⟩⟩
|
||||
|
||||
theorem eraseP_comm {xs : Array α} (h : ∀ a ∈ xs, ¬ p a ∨ ¬ q a) :
|
||||
(xs.eraseP p).eraseP q = (xs.eraseP q).eraseP p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.eraseP_comm (by simpa using h)
|
||||
|
||||
/-! ### erase -/
|
||||
|
||||
section erase
|
||||
variable [BEq α]
|
||||
|
||||
theorem erase_of_not_mem [LawfulBEq α] {a : α} {xs : Array α} (h : a ∉ xs) : xs.erase a = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.erase_of_not_mem (by simpa using h)]
|
||||
|
||||
-- The arguments are intentionally explicit.
|
||||
theorem erase_eq_eraseP' (a : α) (xs : Array α) : xs.erase a = xs.eraseP (· == a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.erase_eq_eraseP']
|
||||
|
||||
-- The arguments are intentionally explicit.
|
||||
theorem erase_eq_eraseP [LawfulBEq α] (a : α) (xs : Array α) : xs.erase a = xs.eraseP (a == ·) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.erase_eq_eraseP]
|
||||
|
||||
@[simp] theorem erase_eq_empty_iff [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.erase a = #[] ↔ xs = #[] ∨ xs = #[a] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.erase_eq_nil_iff]
|
||||
|
||||
theorem erase_ne_empty_iff [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.erase a ≠ #[] ↔ xs ≠ #[] ∧ xs ≠ #[a] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.erase_ne_nil_iff]
|
||||
|
||||
theorem exists_erase_eq [LawfulBEq α] {a : α} {xs : Array α} (h : a ∈ xs) :
|
||||
∃ ys zs, a ∉ ys ∧ xs = ys.push a ++ zs ∧ xs.erase a = ys ++ zs := by
|
||||
let ⟨_, ys, zs, h₁, e, h₂, h₃⟩ := exists_of_eraseP h (beq_self_eq_true _)
|
||||
rw [erase_eq_eraseP]; exact ⟨ys, zs, fun h => h₁ _ h (beq_self_eq_true _), eq_of_beq e ▸ h₂, h₃⟩
|
||||
|
||||
@[simp] theorem size_erase_of_mem [LawfulBEq α] {a : α} {xs : Array α} (h : a ∈ xs) :
|
||||
(xs.erase a).size = xs.size - 1 := by
|
||||
rw [erase_eq_eraseP]; exact size_eraseP_of_mem h (beq_self_eq_true a)
|
||||
|
||||
theorem size_erase [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
(xs.erase a).size = if a ∈ xs then xs.size - 1 else xs.size := by
|
||||
rw [erase_eq_eraseP, size_eraseP]
|
||||
congr
|
||||
simp [mem_iff_getElem, eq_comm (a := a)]
|
||||
|
||||
theorem size_erase_le {a : α} {xs : Array α} : (xs.erase a).size ≤ xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.length_erase_le
|
||||
|
||||
theorem le_size_erase [LawfulBEq α] {a : α} {xs : Array α} : xs.size - 1 ≤ (xs.erase a).size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.le_length_erase
|
||||
|
||||
theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.mem_of_mem_erase (by simpa using h)
|
||||
|
||||
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {xs : Array α} (ab : a ≠ b) :
|
||||
a ∈ xs.erase b ↔ a ∈ xs :=
|
||||
erase_eq_eraseP b xs ▸ mem_eraseP_of_neg (mt eq_of_beq ab.symm)
|
||||
|
||||
@[simp] theorem erase_eq_self_iff [LawfulBEq α] {xs : Array α} : xs.erase a = xs ↔ a ∉ xs := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_self_iff]
|
||||
simp [forall_mem_ne']
|
||||
|
||||
theorem erase_filter [LawfulBEq α] {f : α → Bool} {xs : Array α} :
|
||||
(filter f xs).erase a = filter f (xs.erase a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.erase_filter
|
||||
|
||||
theorem erase_append_left [LawfulBEq α] {xs : Array α} (ys) (h : a ∈ xs) :
|
||||
(xs ++ ys).erase a = xs.erase a ++ ys := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simpa using List.erase_append_left ys (by simpa using h)
|
||||
|
||||
theorem erase_append_right [LawfulBEq α] {a : α} {xs : Array α} (ys : Array α) (h : a ∉ xs) :
|
||||
(xs ++ ys).erase a = (xs ++ ys.erase a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simpa using List.erase_append_right ys (by simpa using h)
|
||||
|
||||
theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
|
||||
(xs ++ ys).erase a = if a ∈ xs then xs.erase a ++ ys else xs ++ ys.erase a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
|
||||
split <;> simp
|
||||
|
||||
theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
|
||||
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
|
||||
simp only [← List.toArray_replicate, List.erase_toArray]
|
||||
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
|
||||
split <;> simp
|
||||
|
||||
@[deprecated erase_replicate (since := "2025-03-18")]
|
||||
abbrev erase_mkArray := @erase_replicate
|
||||
|
||||
-- The arguments `a b` are explicit,
|
||||
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
|
||||
theorem erase_comm [LawfulBEq α] (a b : α) {xs : Array α} :
|
||||
(xs.erase a).erase b = (xs.erase b).erase a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.erase_comm a b
|
||||
|
||||
theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
xs.erase a = ys ↔
|
||||
(a ∉ xs ∧ xs = ys) ∨
|
||||
∃ as bs, a ∉ as ∧ xs = as.push a ++ bs ∧ ys = as ++ bs := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_iff]
|
||||
simp only [beq_iff_eq, forall_mem_ne', exists_and_left]
|
||||
constructor
|
||||
· rintro (⟨h, rfl⟩ | ⟨a', as, h, rfl, bs, rfl, rfl⟩)
|
||||
· left; simp_all
|
||||
· right; refine ⟨as, h, bs, by simp⟩
|
||||
· rintro (⟨h, rfl⟩ | ⟨as, h, bs, rfl, rfl⟩)
|
||||
· left; simp_all
|
||||
· right; refine ⟨a, as, h, rfl, bs, by simp⟩
|
||||
|
||||
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
|
||||
(replicate n a).erase a = replicate (n - 1) a := by
|
||||
simp only [← List.toArray_replicate, List.erase_toArray]
|
||||
simp [List.erase_replicate]
|
||||
|
||||
@[deprecated erase_replicate_self (since := "2025-03-18")]
|
||||
abbrev erase_mkArray_self := @erase_replicate_self
|
||||
|
||||
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
|
||||
(replicate n a).erase b = replicate n a := by
|
||||
rw [erase_of_not_mem]
|
||||
simp_all
|
||||
|
||||
@[deprecated erase_replicate_ne (since := "2025-03-18")]
|
||||
abbrev erase_mkArray_ne := @erase_replicate_ne
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
|
||||
simp [eraseIdxIfInBounds, h]
|
||||
|
||||
theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
|
||||
xs.eraseIdx i h = xs.take i ++ xs.drop (i + 1) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.size_toArray] at h
|
||||
simp only [List.eraseIdx_toArray, List.eraseIdx_eq_take_drop_succ, take_eq_extract,
|
||||
List.extract_toArray, List.extract_eq_drop_take, Nat.sub_zero, List.drop_zero, drop_eq_extract,
|
||||
List.size_toArray, List.append_toArray, mk.injEq, List.append_cancel_left_eq]
|
||||
rw [List.take_of_length_le]
|
||||
simp
|
||||
|
||||
theorem getElem?_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} :
|
||||
(xs.eraseIdx i)[j]? = if j < i then xs[j]? else xs[j + 1]? := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.getElem?_eraseIdx]
|
||||
|
||||
theorem getElem?_eraseIdx_of_lt {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < i) :
|
||||
(xs.eraseIdx i)[j]? = xs[j]? := by
|
||||
rw [getElem?_eraseIdx]
|
||||
simp [h']
|
||||
|
||||
theorem getElem?_eraseIdx_of_ge {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : i ≤ j) :
|
||||
(xs.eraseIdx i)[j]? = xs[j + 1]? := by
|
||||
rw [getElem?_eraseIdx]
|
||||
simp only [dite_eq_ite, ite_eq_right_iff]
|
||||
intro h'
|
||||
omega
|
||||
|
||||
theorem getElem_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < (xs.eraseIdx i).size) :
|
||||
(xs.eraseIdx i)[j] = if h'' : j < i then
|
||||
xs[j]
|
||||
else
|
||||
xs[j + 1]'(by rw [size_eraseIdx] at h'; omega) := by
|
||||
apply Option.some.inj
|
||||
rw [← getElem?_eq_getElem, getElem?_eraseIdx]
|
||||
split <;> simp
|
||||
|
||||
@[simp] theorem eraseIdx_eq_empty_iff {xs : Array α} {i : Nat} {h} : xs.eraseIdx i = #[] ↔ xs.size = 1 ∧ i = 0 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.eraseIdx_toArray, mk.injEq, List.eraseIdx_eq_nil_iff, List.size_toArray,
|
||||
or_iff_right_iff_imp]
|
||||
rintro rfl
|
||||
simp_all
|
||||
|
||||
theorem eraseIdx_ne_empty_iff {xs : Array α} {i : Nat} {h} : xs.eraseIdx i ≠ #[] ↔ 2 ≤ xs.size := by
|
||||
rcases xs with ⟨_ | ⟨a, (_ | ⟨b, l⟩)⟩⟩
|
||||
· simp
|
||||
· simp at h
|
||||
simp [h]
|
||||
· simp
|
||||
|
||||
theorem mem_of_mem_eraseIdx {xs : Array α} {i : Nat} {h} {a : α} (h : a ∈ xs.eraseIdx i) : a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.mem_of_mem_eraseIdx (by simpa using h)
|
||||
|
||||
theorem eraseIdx_append_of_lt_size {xs : Array α} {k : Nat} (hk : k < xs.size) (ys : Array α) (h) :
|
||||
eraseIdx (xs ++ ys) k = eraseIdx xs k ++ ys := by
|
||||
rcases xs with ⟨l⟩
|
||||
rcases ys with ⟨l'⟩
|
||||
simp at hk
|
||||
simp [List.eraseIdx_append_of_lt_length, *]
|
||||
|
||||
theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size ≤ k) (ys : Array α) (h) :
|
||||
eraseIdx (xs ++ ys) k = xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
|
||||
rcases xs with ⟨l⟩
|
||||
rcases ys with ⟨l'⟩
|
||||
simp at hk
|
||||
simp [List.eraseIdx_append_of_length_le, *]
|
||||
|
||||
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
|
||||
(replicate n a).eraseIdx k = replicate (n - 1) a := by
|
||||
simp at h
|
||||
simp only [← List.toArray_replicate, List.eraseIdx_toArray]
|
||||
simp [List.eraseIdx_replicate, h]
|
||||
|
||||
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
|
||||
abbrev eraseIdx_mkArray := @eraseIdx_replicate
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Array α} {k} {h} : x ∈ xs.eraseIdx k h ↔ ∃ i w, i ≠ k ∧ xs[i]'w = x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mem_eraseIdx_iff_getElem, *]
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem? {x : α} {xs : Array α} {k} {h} : x ∈ xs.eraseIdx k h ↔ ∃ i ≠ k, xs[i]? = some x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mem_eraseIdx_iff_getElem?, *]
|
||||
|
||||
theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Nat} (w : xs.idxOf a = i) (h : i < xs.size) :
|
||||
xs.erase a = xs.eraseIdx i := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp at w
|
||||
simp [List.erase_eq_eraseIdx_of_idxOf, *]
|
||||
|
||||
theorem getElem_eraseIdx_of_lt {xs : Array α} {i : Nat} (w : i < xs.size) {j : Nat} (h : j < (xs.eraseIdx i).size) (h' : j < i) :
|
||||
(xs.eraseIdx i)[j] = xs[j] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.getElem_eraseIdx_of_lt, *]
|
||||
|
||||
theorem getElem_eraseIdx_of_ge {xs : Array α} {i : Nat} (w : i < xs.size) {j : Nat} (h : j < (xs.eraseIdx i).size) (h' : i ≤ j) :
|
||||
(xs.eraseIdx i)[j] = xs[j + 1]'(by simp at h; omega) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.getElem_eraseIdx_of_ge, *]
|
||||
|
||||
theorem eraseIdx_set_eq {xs : Array α} {i : Nat} {a : α} {h : i < xs.size} :
|
||||
(xs.set i a).eraseIdx i (by simp; omega) = xs.eraseIdx i := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.eraseIdx_set_eq, *]
|
||||
|
||||
theorem eraseIdx_set_lt {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : j < i) :
|
||||
(xs.set i a).eraseIdx j (by simp; omega) = (xs.eraseIdx j).set (i - 1) a (by simp; omega) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.eraseIdx_set_lt, *]
|
||||
|
||||
theorem eraseIdx_set_gt {xs : Array α} {i : Nat} {j : Nat} {a : α} (h : i < j) {w : j < xs.size} :
|
||||
(xs.set i a).eraseIdx j (by simp; omega) = (xs.eraseIdx j).set i a (by simp; omega) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.eraseIdx_set_gt, *]
|
||||
|
||||
@[simp] theorem set_getElem_succ_eraseIdx_succ
|
||||
{xs : Array α} {i : Nat} (h : i + 1 < xs.size) :
|
||||
(xs.eraseIdx (i + 1)).set i xs[i + 1] (by simp; omega) = xs.eraseIdx i := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.set_getElem_succ_eraseIdx_succ, *]
|
||||
|
||||
end Array
|
||||
445
src/Init/Data/Array/Extract.lean
Normal file
445
src/Init/Data/Array/Extract.lean
Normal file
@@ -0,0 +1,445 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.extract`
|
||||
|
||||
This file follows the contents of `Init.Data.List.TakeDrop` and `Init.Data.List.Nat.TakeDrop`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Nat
|
||||
namespace Array
|
||||
|
||||
/-! ### extract -/
|
||||
|
||||
@[simp] theorem extract_of_size_lt {as : Array α} {i j : Nat} (h : as.size < j) :
|
||||
as.extract i j = as.extract i as.size := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract] at h₁ h₂
|
||||
simp [h]
|
||||
|
||||
theorem size_extract_le {as : Array α} {i j : Nat} :
|
||||
(as.extract i j).size ≤ j - i := by
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem size_extract_le' {as : Array α} {i j : Nat} :
|
||||
(as.extract i j).size ≤ as.size - i := by
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j ≤ as.size) :
|
||||
(as.extract i j).size = j - i := by
|
||||
simp
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop ≤ as.size) :
|
||||
(as.push b).extract start stop = as.extract start stop := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, size_push] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_push]
|
||||
rw [dif_pos (by omega)]
|
||||
|
||||
@[simp]
|
||||
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
|
||||
as.extract 0 stop = as.pop := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, size_pop] at h₁ h₂
|
||||
simp [getElem_extract, getElem_pop]
|
||||
|
||||
@[simp]
|
||||
theorem extract_append_extract {as : Array α} {i j k : Nat} :
|
||||
as.extract i j ++ as.extract j k = as.extract (min i j) (max j k) := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_append, size_extract] at h₁ h₂
|
||||
simp only [getElem_append, size_extract, getElem_extract]
|
||||
split <;>
|
||||
· congr 1
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem extract_eq_empty_iff {as : Array α} :
|
||||
as.extract i j = #[] ↔ min j as.size ≤ i := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h := congrArg Array.size h
|
||||
simp at h
|
||||
omega
|
||||
· intro h
|
||||
exact eq_empty_of_size_eq_zero (by simp; omega)
|
||||
|
||||
theorem extract_eq_empty_of_le {as : Array α} (h : min j as.size ≤ i) :
|
||||
as.extract i j = #[] :=
|
||||
extract_eq_empty_iff.2 h
|
||||
|
||||
theorem lt_of_extract_ne_empty {as : Array α} (h : as.extract i j ≠ #[]) :
|
||||
i < min j as.size :=
|
||||
gt_of_not_le (mt extract_eq_empty_of_le h)
|
||||
|
||||
@[simp]
|
||||
theorem extract_eq_self_iff {as : Array α} :
|
||||
as.extract i j = as ↔ as.size = 0 ∨ i = 0 ∧ as.size ≤ j := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h := congrArg Array.size h
|
||||
simp at h
|
||||
omega
|
||||
· intro h
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract] at h₁
|
||||
simp only [getElem_extract]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem extract_eq_self_of_le {as : Array α} (h : as.size ≤ j) :
|
||||
as.extract 0 j = as :=
|
||||
extract_eq_self_iff.2 (.inr ⟨rfl, h⟩)
|
||||
|
||||
theorem le_of_extract_eq_self {as : Array α} (h : as.extract i j = as) :
|
||||
as.size ≤ j := by
|
||||
replace h := congrArg Array.size h
|
||||
simp at h
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem extract_size_left {as : Array α} :
|
||||
as.extract as.size j = #[] := by
|
||||
simp
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem push_extract_getElem {as : Array α} {i j : Nat} (h : j < as.size) :
|
||||
(as.extract i j).push as[j] = as.extract (min i j) (j + 1) := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_push, size_extract] at h₁ h₂
|
||||
simp only [getElem_push, size_extract, getElem_extract]
|
||||
split <;>
|
||||
· congr
|
||||
omega
|
||||
|
||||
theorem extract_succ_right {as : Array α} {i j : Nat} (w : i < j + 1) (h : j < as.size) :
|
||||
as.extract i (j + 1) = (as.extract i j).push as[j] := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, push_extract_getElem] at h₁ h₂
|
||||
simp only [getElem_extract, push_extract_getElem]
|
||||
congr
|
||||
omega
|
||||
|
||||
theorem extract_sub_one {as : Array α} {i j : Nat} (h : j < as.size) :
|
||||
as.extract i (j - 1) = (as.extract i j).pop := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, size_pop] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_pop]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_extract_of_lt {as : Array α} {i j k : Nat} (h : k < min j as.size - i) :
|
||||
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
|
||||
simp [getElem?_extract, h]
|
||||
|
||||
theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
|
||||
(as.extract 0 (j + 1))[j]? = as[j]? := by
|
||||
simp [getElem?_extract]
|
||||
omega
|
||||
|
||||
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
|
||||
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
|
||||
ext m h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract] at h₁ h₂
|
||||
simp [Nat.add_assoc]
|
||||
|
||||
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
|
||||
as.extract i j = #[] := by
|
||||
simp [h]
|
||||
|
||||
theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract i j ≠ #[]) :
|
||||
as ≠ #[] :=
|
||||
mt extract_eq_empty_of_eq_empty h
|
||||
|
||||
theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
|
||||
(as.set k a).extract i j =
|
||||
if _ : k < i then
|
||||
as.extract i j
|
||||
else if _ : k < min j as.size then
|
||||
(as.extract i j).set (k - i) a (by simp; omega)
|
||||
else as.extract i j := by
|
||||
split
|
||||
· ext l h₁ h₂
|
||||
· simp
|
||||
· simp at h₁ h₂
|
||||
simp [getElem_set]
|
||||
omega
|
||||
· split
|
||||
· ext l h₁ h₂
|
||||
· simp
|
||||
· simp only [getElem_extract, getElem_set]
|
||||
split
|
||||
· rw [if_pos]; omega
|
||||
· rw [if_neg]; omega
|
||||
· ext l h₁ h₂
|
||||
· simp
|
||||
· simp at h₁ h₂
|
||||
simp [getElem_set]
|
||||
omega
|
||||
|
||||
theorem set_extract {as : Array α} {i j k : Nat} (h : k < (as.extract i j).size) {a : α} :
|
||||
(as.extract i j).set k a = (as.set (i + k) a (by simp at h; omega)).extract i j := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
· simp_all [getElem_set]
|
||||
|
||||
@[simp]
|
||||
theorem extract_append {as bs : Array α} {i j : Nat} :
|
||||
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, size_append] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_append, size_extract]
|
||||
split
|
||||
· split
|
||||
· rfl
|
||||
· omega
|
||||
· split
|
||||
· omega
|
||||
· congr 1
|
||||
omega
|
||||
|
||||
theorem extract_append_left {as bs : Array α} :
|
||||
(as ++ bs).extract 0 as.size = as.extract 0 as.size := by
|
||||
simp
|
||||
|
||||
@[simp] theorem extract_append_right {as bs : Array α} :
|
||||
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
|
||||
simp only [extract_append, extract_size_left, Nat.sub_self, empty_append]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
|
||||
(as.extract i j).map f = (as.map f).extract i j := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
· simp only [size_map, size_extract] at h₁ h₂
|
||||
simp only [getElem_map, getElem_extract]
|
||||
|
||||
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
|
||||
(replicate n a).extract i j = replicate (min j n - i) a := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
· simp only [size_extract, size_replicate] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_replicate]
|
||||
|
||||
@[deprecated extract_replicate (since := "2025-03-18")]
|
||||
abbrev extract_mkArray := @extract_replicate
|
||||
|
||||
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
|
||||
as.extract i j = as.extract i j' ↔ min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
|
||||
rcases as with ⟨as⟩
|
||||
simp
|
||||
|
||||
theorem extract_eq_extract_left {as : Array α} {i i' j : Nat} :
|
||||
as.extract i j = as.extract i' j ↔ min j as.size - i = min j as.size - i' := by
|
||||
constructor
|
||||
· intro h
|
||||
replace h := congrArg Array.size h
|
||||
simpa using h
|
||||
· intro h
|
||||
ext l h₁ h₂
|
||||
· simpa
|
||||
· simp only [size_extract] at h₁ h₂
|
||||
simp only [getElem_extract]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem extract_add_left {as : Array α} {i j k : Nat} :
|
||||
as.extract (i + j) k = (as.extract i k).extract j (k - i) := by
|
||||
simp [extract_eq_extract_right]
|
||||
omega
|
||||
|
||||
theorem mem_extract_iff_getElem {as : Array α} {a : α} {i j : Nat} :
|
||||
a ∈ as.extract i j ↔ ∃ (k : Nat) (hm : k < min j as.size - i), as[i + k] = a := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.mem_take_iff_getElem]
|
||||
constructor <;>
|
||||
· rintro ⟨k, h, rfl⟩
|
||||
exact ⟨k, by omega, rfl⟩
|
||||
|
||||
theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as.size) {a : α} :
|
||||
as.set i a = (as.extract 0 i).push a ++ (as.extract (i + 1) as.size) := by
|
||||
rcases as with ⟨as⟩
|
||||
simp at h
|
||||
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
|
||||
|
||||
theorem extract_reverse {as : Array α} {i j : Nat} :
|
||||
as.reverse.extract i j = (as.extract (as.size - j) (as.size - i)).reverse := by
|
||||
ext l h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [size_extract, size_reverse] at h₁ h₂
|
||||
simp only [getElem_extract, getElem_reverse, size_extract]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem reverse_extract {as : Array α} {i j : Nat} :
|
||||
(as.extract i j).reverse = as.reverse.extract (as.size - j) (as.size - i) := by
|
||||
rw [extract_reverse]
|
||||
simp
|
||||
by_cases h : j ≤ as.size
|
||||
· have : as.size - (as.size - j) = j := by omega
|
||||
simp [this, extract_eq_extract_left]
|
||||
omega
|
||||
· have : as.size - (as.size - j) = as.size := by omega
|
||||
simp only [Nat.not_le] at h
|
||||
simp [h, this, extract_eq_extract_left]
|
||||
omega
|
||||
|
||||
/-! ### takeWhile -/
|
||||
|
||||
theorem takeWhile_map {f : α → β} {p : β → Bool} {as : Array α} :
|
||||
(as.map f).takeWhile p = (as.takeWhile (p ∘ f)).map f := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.takeWhile_map]
|
||||
|
||||
theorem popWhile_map {f : α → β} {p : β → Bool} {as : Array α} :
|
||||
(as.map f).popWhile p = (as.popWhile (p ∘ f)).map f := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.dropWhile_map, ← List.map_reverse]
|
||||
|
||||
theorem takeWhile_filterMap {f : α → Option β} {p : β → Bool} {as : Array α} :
|
||||
(as.filterMap f).takeWhile p = (as.takeWhile fun a => (f a).all p).filterMap f := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.takeWhile_filterMap]
|
||||
|
||||
theorem popWhile_filterMap {f : α → Option β} {p : β → Bool} {as : Array α} :
|
||||
(as.filterMap f).popWhile p = (as.popWhile fun a => (f a).all p).filterMap f := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.dropWhile_filterMap, ← List.filterMap_reverse]
|
||||
|
||||
theorem takeWhile_filter {p q : α → Bool} {as : Array α} :
|
||||
(as.filter p).takeWhile q = (as.takeWhile fun a => !p a || q a).filter p := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.takeWhile_filter]
|
||||
|
||||
theorem popWhile_filter {p q : α → Bool} {as : Array α} :
|
||||
(as.filter p).popWhile q = (as.popWhile fun a => !p a || q a).filter p := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.dropWhile_filter, ← List.filter_reverse]
|
||||
|
||||
theorem takeWhile_append {xs ys : Array α} :
|
||||
(xs ++ ys).takeWhile p =
|
||||
if (xs.takeWhile p).size = xs.size then xs ++ ys.takeWhile p else xs.takeWhile p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp only [List.append_toArray, List.takeWhile_toArray, List.takeWhile_append, List.size_toArray]
|
||||
split <;> rfl
|
||||
|
||||
@[simp] theorem takeWhile_append_of_pos {p : α → Bool} {xs ys : Array α} (h : ∀ a ∈ xs, p a) :
|
||||
(xs ++ ys).takeWhile p = xs ++ ys.takeWhile p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp at h
|
||||
simp [List.takeWhile_append_of_pos h]
|
||||
|
||||
theorem popWhile_append {xs ys : Array α} :
|
||||
(xs ++ ys).popWhile p =
|
||||
if (ys.popWhile p).isEmpty then xs.popWhile p else xs ++ ys.popWhile p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
|
||||
List.isEmpty_iff, List.isEmpty_toArray, List.isEmpty_reverse]
|
||||
-- Why do these not fire with `simp`?
|
||||
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
|
||||
split
|
||||
· rfl
|
||||
· simp
|
||||
|
||||
@[simp] theorem popWhile_append_of_pos {p : α → Bool} {xs ys : Array α} (h : ∀ a ∈ ys, p a) :
|
||||
(xs ++ ys).popWhile p = xs.popWhile p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp at h
|
||||
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, mk.injEq,
|
||||
List.reverse_inj]
|
||||
rw [List.dropWhile_append_of_pos]
|
||||
simpa
|
||||
|
||||
@[simp] theorem takeWhile_replicate_eq_filter {p : α → Bool} :
|
||||
(replicate n a).takeWhile p = (replicate n a).filter p := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
|
||||
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
|
||||
|
||||
theorem takeWhile_replicate {p : α → Bool} :
|
||||
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by
|
||||
simp [takeWhile_replicate_eq_filter, filter_replicate]
|
||||
|
||||
@[deprecated takeWhile_replicate (since := "2025-03-18")]
|
||||
abbrev takeWhile_mkArray := @takeWhile_replicate
|
||||
|
||||
@[simp] theorem popWhile_replicate_eq_filter_not {p : α → Bool} :
|
||||
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
|
||||
simp [← List.toArray_replicate, ← List.filter_reverse]
|
||||
|
||||
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")]
|
||||
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not
|
||||
|
||||
theorem popWhile_replicate {p : α → Bool} :
|
||||
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
|
||||
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
|
||||
Bool.not_true]
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated popWhile_replicate (since := "2025-03-18")]
|
||||
abbrev popWhile_mkArray := @popWhile_replicate
|
||||
|
||||
theorem extract_takeWhile {as : Array α} {i : Nat} :
|
||||
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
|
||||
rcases as with ⟨as⟩
|
||||
simp [List.take_takeWhile]
|
||||
|
||||
@[simp] theorem all_takeWhile {as : Array α} :
|
||||
(as.takeWhile p).all p = true := by
|
||||
rcases as with ⟨as⟩
|
||||
rw [List.takeWhile_toArray] -- Not sure why this doesn't fire with `simp`.
|
||||
simp
|
||||
|
||||
@[simp] theorem any_popWhile {as : Array α} :
|
||||
(as.popWhile p).any (fun a => !p a) = !as.all p := by
|
||||
rcases as with ⟨as⟩
|
||||
rw [List.popWhile_toArray] -- Not sure why this doesn't fire with `simp`.
|
||||
simp
|
||||
|
||||
theorem takeWhile_eq_extract_findIdx_not {xs : Array α} {p : α → Bool} :
|
||||
takeWhile p xs = xs.extract 0 (xs.findIdx (fun a => !p a)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.takeWhile_eq_take_findIdx_not]
|
||||
|
||||
end Array
|
||||
@@ -5,10 +5,52 @@ Authors: François G. Dorais
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.FinRange
|
||||
import Init.Data.Array.OfFn
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/-- `finRange n` is the array of all elements of `Fin n` in order. -/
|
||||
/--
|
||||
Returns an array of all elements of `Fin n` in order, starting at `0`.
|
||||
|
||||
Examples:
|
||||
* `Array.finRange 0 = (#[] : Array (Fin 0))`
|
||||
* `Array.finRange 2 = (#[0, 1] : Array (Fin 2))`
|
||||
-/
|
||||
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
|
||||
|
||||
@[simp] theorem size_finRange {n} : (Array.finRange n).size = n := by
|
||||
simp [Array.finRange]
|
||||
|
||||
@[simp] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
|
||||
(Array.finRange n)[i] = Fin.cast size_finRange ⟨i, h⟩ := by
|
||||
simp [Array.finRange]
|
||||
|
||||
@[simp] theorem finRange_zero : Array.finRange 0 = #[] := by simp [Array.finRange]
|
||||
|
||||
theorem finRange_succ {n} : Array.finRange (n+1) = #[0] ++ (Array.finRange n).map Fin.succ := by
|
||||
ext
|
||||
· simp [Nat.add_comm]
|
||||
· simp [getElem_append]
|
||||
split <;>
|
||||
· simp; omega
|
||||
|
||||
theorem finRange_succ_last {n} :
|
||||
Array.finRange (n+1) = (Array.finRange n).map Fin.castSucc ++ #[Fin.last n] := by
|
||||
ext
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split
|
||||
· simp
|
||||
· simp_all
|
||||
omega
|
||||
|
||||
theorem finRange_reverse {n} : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
|
||||
ext i h
|
||||
· simp
|
||||
· simp
|
||||
omega
|
||||
|
||||
end Array
|
||||
|
||||
@@ -4,132 +4,145 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Find
|
||||
import Init.Data.List.Nat.Find
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.Range
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.findSome?`, `Array.find?`.
|
||||
# Lemmas about `Array.findSome?`, `Array.find?, `Array.findIdx`, `Array.findIdx?`, `Array.idxOf`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### findSome? -/
|
||||
|
||||
@[simp] theorem findSomeRev?_push_of_isSome (l : Array α) (h : (f a).isSome) : (l.push a).findSomeRev? f = f a := by
|
||||
cases l; simp_all
|
||||
@[simp] theorem findSomeRev?_push_of_isSome {xs : Array α} (h : (f a).isSome) : (xs.push a).findSomeRev? f = f a := by
|
||||
cases xs; simp_all
|
||||
|
||||
@[simp] theorem findSomeRev?_push_of_isNone (l : Array α) (h : (f a).isNone) : (l.push a).findSomeRev? f = l.findSomeRev? f := by
|
||||
cases l; simp_all
|
||||
@[simp] theorem findSomeRev?_push_of_isNone {xs : Array α} (h : (f a).isNone) : (xs.push a).findSomeRev? f = xs.findSomeRev? f := by
|
||||
cases xs; simp_all
|
||||
|
||||
theorem exists_of_findSome?_eq_some {f : α → Option β} {l : Array α} (w : l.findSome? f = some b) :
|
||||
∃ a, a ∈ l ∧ f a = b := by
|
||||
cases l; simp_all [List.exists_of_findSome?_eq_some]
|
||||
theorem exists_of_findSome?_eq_some {f : α → Option β} {xs : Array α} (w : xs.findSome? f = some b) :
|
||||
∃ a, a ∈ xs ∧ f a = b := by
|
||||
cases xs; simp_all [List.exists_of_findSome?_eq_some]
|
||||
|
||||
@[simp] theorem findSome?_eq_none_iff : findSome? p l = none ↔ ∀ x ∈ l, p x = none := by
|
||||
cases l; simp
|
||||
@[simp] theorem findSome?_eq_none_iff : findSome? p xs = none ↔ ∀ x ∈ xs, p x = none := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem findSome?_isSome_iff {f : α → Option β} {l : Array α} :
|
||||
(l.findSome? f).isSome ↔ ∃ x, x ∈ l ∧ (f x).isSome := by
|
||||
cases l; simp
|
||||
@[simp] theorem findSome?_isSome_iff {f : α → Option β} {xs : Array α} :
|
||||
(xs.findSome? f).isSome ↔ ∃ x, x ∈ xs ∧ (f x).isSome := by
|
||||
cases xs; simp
|
||||
|
||||
theorem findSome?_eq_some_iff {f : α → Option β} {l : Array α} {b : β} :
|
||||
l.findSome? f = some b ↔ ∃ (l₁ : Array α) (a : α) (l₂ : Array α), l = l₁.push a ++ l₂ ∧ f a = some b ∧ ∀ x ∈ l₁, f x = none := by
|
||||
cases l
|
||||
theorem findSome?_eq_some_iff {f : α → Option β} {xs : Array α} {b : β} :
|
||||
xs.findSome? f = some b ↔ ∃ (ys : Array α) (a : α) (zs : Array α), xs = ys.push a ++ zs ∧ f a = some b ∧ ∀ x ∈ ys, f x = none := by
|
||||
cases xs
|
||||
simp only [List.findSome?_toArray, List.findSome?_eq_some_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, a, l₂, rfl, h₁, h₂⟩
|
||||
exact ⟨l₁.toArray, a, l₂.toArray, by simp_all⟩
|
||||
· rintro ⟨l₁, a, l₂, h₀, h₁, h₂⟩
|
||||
exact ⟨l₁.toList, a, l₂.toList, by simpa using congrArg toList h₀, h₁, by simpa⟩
|
||||
· rintro ⟨xs, a, ys, h₀, h₁, h₂⟩
|
||||
exact ⟨xs.toList, a, ys.toList, by simpa using congrArg toList h₀, h₁, by simpa⟩
|
||||
|
||||
@[simp] theorem findSome?_guard (l : Array α) : findSome? (Option.guard fun x => p x) l = find? p l := by
|
||||
cases l; simp
|
||||
@[simp] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem getElem?_zero_filterMap (f : α → Option β) (l : Array α) : (l.filterMap f)[0]? = l.findSome? f := by
|
||||
cases l; simp [← List.head?_eq_getElem?]
|
||||
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
|
||||
findSome?_guard.symm
|
||||
|
||||
@[simp] theorem getElem_zero_filterMap (f : α → Option β) (l : Array α) (h) :
|
||||
(l.filterMap f)[0] = (l.findSome? f).get (by cases l; simpa [List.length_filterMap_eq_countP] using h) := by
|
||||
cases l; simp [← List.head_eq_getElem, ← getElem?_zero_filterMap]
|
||||
@[simp] theorem getElem?_zero_filterMap {f : α → Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
|
||||
cases xs; simp [← List.head?_eq_getElem?]
|
||||
|
||||
@[simp] theorem back?_filterMap (f : α → Option β) (l : Array α) : (l.filterMap f).back? = l.findSomeRev? f := by
|
||||
cases l; simp
|
||||
@[simp] theorem getElem_zero_filterMap {f : α → Option β} {xs : Array α} (h) :
|
||||
(xs.filterMap f)[0] = (xs.findSome? f).get (by cases xs; simpa [List.length_filterMap_eq_countP] using h) := by
|
||||
cases xs; simp [← List.head_eq_getElem, ← getElem?_zero_filterMap]
|
||||
|
||||
@[simp] theorem back!_filterMap [Inhabited β] (f : α → Option β) (l : Array α) :
|
||||
(l.filterMap f).back! = (l.findSomeRev? f).getD default := by
|
||||
cases l; simp
|
||||
@[simp] theorem back?_filterMap {f : α → Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem map_findSome? (f : α → Option β) (g : β → γ) (l : Array α) :
|
||||
(l.findSome? f).map g = l.findSome? (Option.map g ∘ f) := by
|
||||
cases l; simp
|
||||
@[simp] theorem back!_filterMap [Inhabited β] {f : α → Option β} {xs : Array α} :
|
||||
(xs.filterMap f).back! = (xs.findSomeRev? f).getD default := by
|
||||
cases xs; simp
|
||||
|
||||
theorem findSome?_map (f : β → γ) (l : Array β) : findSome? p (l.map f) = l.findSome? (p ∘ f) := by
|
||||
cases l; simp [List.findSome?_map]
|
||||
@[simp] theorem map_findSome? {f : α → Option β} {g : β → γ} {xs : Array α} :
|
||||
(xs.findSome? f).map g = xs.findSome? (Option.map g ∘ f) := by
|
||||
cases xs; simp
|
||||
|
||||
theorem findSome?_append {l₁ l₂ : Array α} : (l₁ ++ l₂).findSome? f = (l₁.findSome? f).or (l₂.findSome? f) := by
|
||||
cases l₁; cases l₂; simp [List.findSome?_append]
|
||||
theorem findSome?_map {f : β → γ} {xs : Array β} : findSome? p (xs.map f) = xs.findSome? (p ∘ f) := by
|
||||
cases xs; simp [List.findSome?_map]
|
||||
|
||||
theorem getElem?_zero_flatten (L : Array (Array α)) :
|
||||
(flatten L)[0]? = L.findSome? fun l => l[0]? := by
|
||||
cases L using array₂_induction
|
||||
theorem findSome?_append {xs ys : Array α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
|
||||
cases xs; cases ys; simp [List.findSome?_append]
|
||||
|
||||
theorem getElem?_zero_flatten (xss : Array (Array α)) :
|
||||
(flatten xss)[0]? = xss.findSome? fun xs => xs[0]? := by
|
||||
cases xss using array₂_induction
|
||||
simp [← List.head?_eq_getElem?, List.head?_flatten, List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem getElem_zero_flatten.proof {L : Array (Array α)} (h : 0 < L.flatten.size) :
|
||||
(L.findSome? fun l => l[0]?).isSome := by
|
||||
cases L using array₂_induction
|
||||
theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten.size) :
|
||||
(xss.findSome? fun xs => xs[0]?).isSome := by
|
||||
cases xss using array₂_induction
|
||||
simp only [List.findSome?_toArray, List.findSome?_map, Function.comp_def, List.getElem?_toArray,
|
||||
List.findSome?_isSome_iff, isSome_getElem?]
|
||||
simp only [flatten_toArray_map_toArray, size_toArray, List.length_flatten,
|
||||
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
|
||||
Nat.sum_pos_iff_exists_pos, List.mem_map] at h
|
||||
obtain ⟨_, ⟨xs, m, rfl⟩, h⟩ := h
|
||||
exact ⟨xs, m, by simpa using h⟩
|
||||
|
||||
theorem getElem_zero_flatten {L : Array (Array α)} (h) :
|
||||
(flatten L)[0] = (L.findSome? fun l => l[0]?).get (getElem_zero_flatten.proof h) := by
|
||||
have t := getElem?_zero_flatten L
|
||||
theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
|
||||
(flatten xss)[0] = (xss.findSome? fun xs => xs[0]?).get (getElem_zero_flatten.proof h) := by
|
||||
have t := getElem?_zero_flatten xss
|
||||
simp [getElem?_eq_getElem, h] at t
|
||||
simp [← t]
|
||||
|
||||
theorem back?_flatten {L : Array (Array α)} :
|
||||
(flatten L).back? = (L.findSomeRev? fun l => l.back?) := by
|
||||
cases L using array₂_induction
|
||||
simp [List.getLast?_flatten, ← List.map_reverse, List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
|
||||
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
simp [← List.toArray_replicate, List.findSome?_replicate]
|
||||
|
||||
@[simp] theorem findSome?_mkArray_of_pos (h : 0 < n) : findSome? f (mkArray n a) = f a := by
|
||||
simp [findSome?_mkArray, Nat.ne_of_gt h]
|
||||
@[deprecated findSome?_replicate (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray := @findSome?_replicate
|
||||
|
||||
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
|
||||
simp [findSome?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
|
||||
|
||||
-- Argument is unused, but used to decide whether `simp` should unfold.
|
||||
@[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
|
||||
findSome? f (mkArray n a) = if n = 0 then none else f a := by
|
||||
simp [findSome?_mkArray]
|
||||
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) :
|
||||
findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
simp [findSome?_replicate]
|
||||
|
||||
@[simp] theorem findSome?_mkArray_of_isNone (h : (f a).isNone) :
|
||||
findSome? f (mkArray n a) = none := by
|
||||
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome
|
||||
|
||||
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) :
|
||||
findSome? f (replicate n a) = none := by
|
||||
rw [Option.isNone_iff_eq_none] at h
|
||||
simp [findSome?_mkArray, h]
|
||||
simp [findSome?_replicate, h]
|
||||
|
||||
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
|
||||
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@[simp] theorem find?_singleton (a : α) (p : α → Bool) :
|
||||
@[simp] theorem find?_singleton {a : α} {p : α → Bool} :
|
||||
#[a].find? p = if p a then some a else none := by
|
||||
simp [singleton_eq_toArray_singleton]
|
||||
|
||||
@[simp] theorem findRev?_push_of_pos (l : Array α) (h : p a) :
|
||||
findRev? p (l.push a) = some a := by
|
||||
cases l; simp [h]
|
||||
@[simp] theorem findRev?_push_of_pos {xs : Array α} (h : p a) :
|
||||
findRev? p (xs.push a) = some a := by
|
||||
cases xs; simp [h]
|
||||
|
||||
@[simp] theorem findRev?_cons_of_neg (l : Array α) (h : ¬p a) :
|
||||
findRev? p (l.push a) = findRev? p l := by
|
||||
cases l; simp [h]
|
||||
@[simp] theorem findRev?_cons_of_neg {xs : Array α} (h : ¬p a) :
|
||||
findRev? p (xs.push a) = findRev? p xs := by
|
||||
cases xs; simp [h]
|
||||
|
||||
@[simp] theorem find?_eq_none : find? p l = none ↔ ∀ x ∈ l, ¬ p x := by
|
||||
cases l; simp
|
||||
@[simp] theorem find?_eq_none : find? p xs = none ↔ ∀ x ∈ xs, ¬ p x := by
|
||||
cases xs; simp
|
||||
|
||||
theorem find?_eq_some_iff_append {xs : Array α} :
|
||||
xs.find? p = some b ↔ p b ∧ ∃ (as bs : Array α), xs = as.push b ++ bs ∧ ∀ a ∈ as, !p a := by
|
||||
@@ -138,10 +151,10 @@ theorem find?_eq_some_iff_append {xs : Array α} :
|
||||
Bool.not_true, exists_and_right, and_congr_right_iff]
|
||||
intro w
|
||||
constructor
|
||||
· rintro ⟨as, ⟨⟨x, rfl⟩, h⟩⟩
|
||||
exact ⟨as.toArray, ⟨x.toArray, by simp⟩ , by simpa using h⟩
|
||||
· rintro ⟨as, ⟨⟨x, h'⟩, h⟩⟩
|
||||
exact ⟨as.toList, ⟨x.toList, by simpa using congrArg Array.toList h'⟩,
|
||||
· rintro ⟨as, ⟨⟨xs, rfl⟩, h⟩⟩
|
||||
exact ⟨as.toArray, ⟨xs.toArray, by simp⟩ , by simpa using h⟩
|
||||
· rintro ⟨as, ⟨⟨⟨l⟩, h'⟩, h⟩⟩
|
||||
exact ⟨as.toList, ⟨l, by simpa using congrArg Array.toList h'⟩,
|
||||
by simpa using h⟩
|
||||
|
||||
@[simp]
|
||||
@@ -170,58 +183,61 @@ theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h ∈ xs := by
|
||||
(xs.filter p).find? q = xs.find? (fun a => p a ∧ q a) := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem getElem?_zero_filter (p : α → Bool) (l : Array α) :
|
||||
(l.filter p)[0]? = l.find? p := by
|
||||
cases l; simp [← List.head?_eq_getElem?]
|
||||
@[simp] theorem getElem?_zero_filter {p : α → Bool} {xs : Array α} :
|
||||
(xs.filter p)[0]? = xs.find? p := by
|
||||
cases xs; simp [← List.head?_eq_getElem?]
|
||||
|
||||
@[simp] theorem getElem_zero_filter (p : α → Bool) (l : Array α) (h) :
|
||||
(l.filter p)[0] =
|
||||
(l.find? p).get (by cases l; simpa [← List.countP_eq_length_filter] using h) := by
|
||||
cases l
|
||||
@[simp] theorem getElem_zero_filter {p : α → Bool} {xs : Array α} (h) :
|
||||
(xs.filter p)[0] =
|
||||
(xs.find? p).get (by cases xs; simpa [← List.countP_eq_length_filter] using h) := by
|
||||
cases xs
|
||||
simp [List.getElem_zero_eq_head]
|
||||
|
||||
@[simp] theorem back?_filter (p : α → Bool) (l : Array α) : (l.filter p).back? = l.findRev? p := by
|
||||
cases l; simp
|
||||
@[simp] theorem back?_filter {p : α → Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem back!_filter [Inhabited α] (p : α → Bool) (l : Array α) :
|
||||
(l.filter p).back! = (l.findRev? p).get! := by
|
||||
cases l; simp [Option.get!_eq_getD]
|
||||
@[simp] theorem back!_filter [Inhabited α] {p : α → Bool} {xs : Array α} :
|
||||
(xs.filter p).back! = (xs.findRev? p).get! := by
|
||||
cases xs; simp [Option.get!_eq_getD]
|
||||
|
||||
@[simp] theorem find?_filterMap (xs : Array α) (f : α → Option β) (p : β → Bool) :
|
||||
@[simp] theorem find?_filterMap {xs : Array α} {f : α → Option β} {p : β → Bool} :
|
||||
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem find?_map (f : β → α) (xs : Array β) :
|
||||
@[simp] theorem find?_map {f : β → α} {xs : Array β} :
|
||||
find? p (xs.map f) = (xs.find? (p ∘ f)).map f := by
|
||||
cases xs; simp
|
||||
|
||||
@[simp] theorem find?_append {l₁ l₂ : Array α} :
|
||||
(l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
@[simp] theorem find?_append {xs ys : Array α} :
|
||||
(xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp
|
||||
|
||||
@[simp] theorem find?_flatten (xs : Array (Array α)) (p : α → Bool) :
|
||||
xs.flatten.find? p = xs.findSome? (·.find? p) := by
|
||||
cases xs using array₂_induction
|
||||
@[simp] theorem find?_flatten {xss : Array (Array α)} {p : α → Bool} :
|
||||
xss.flatten.find? p = xss.findSome? (·.find? p) := by
|
||||
cases xss using array₂_induction
|
||||
simp [List.findSome?_map, Function.comp_def]
|
||||
|
||||
theorem find?_flatten_eq_none {xs : Array (Array α)} {p : α → Bool} :
|
||||
xs.flatten.find? p = none ↔ ∀ ys ∈ xs, ∀ x ∈ ys, !p x := by
|
||||
theorem find?_flatten_eq_none_iff {xss : Array (Array α)} {p : α → Bool} :
|
||||
xss.flatten.find? p = none ↔ ∀ ys ∈ xss, ∀ x ∈ ys, !p x := by
|
||||
simp
|
||||
|
||||
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
|
||||
|
||||
/--
|
||||
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
|
||||
some array in `xs` contains `a`, and no earlier element of that array satisfies `p`.
|
||||
Moreover, no earlier array in `xs` has an element satisfying `p`.
|
||||
-/
|
||||
theorem find?_flatten_eq_some {xs : Array (Array α)} {p : α → Bool} {a : α} :
|
||||
xs.flatten.find? p = some a ↔
|
||||
theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a : α} :
|
||||
xss.flatten.find? p = some a ↔
|
||||
p a ∧ ∃ (as : Array (Array α)) (ys zs : Array α) (bs : Array (Array α)),
|
||||
xs = as.push (ys.push a ++ zs) ++ bs ∧
|
||||
(∀ a ∈ as, ∀ x ∈ a, !p x) ∧ (∀ x ∈ ys, !p x) := by
|
||||
cases xs using array₂_induction
|
||||
simp only [flatten_toArray_map_toArray, List.find?_toArray, List.find?_flatten_eq_some]
|
||||
xss = as.push (ys.push a ++ zs) ++ bs ∧
|
||||
(∀ ws ∈ as, ∀ x ∈ ws, !p x) ∧ (∀ x ∈ ys, !p x) := by
|
||||
cases xss using array₂_induction
|
||||
simp only [flatten_toArray_map_toArray, List.find?_toArray, List.find?_flatten_eq_some_iff]
|
||||
simp only [Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, and_congr_right_iff]
|
||||
intro w
|
||||
constructor
|
||||
@@ -235,47 +251,411 @@ theorem find?_flatten_eq_some {xs : Array (Array α)} {p : α → Bool} {a : α}
|
||||
⟨zs.toList, bs.toList.map Array.toList, by simpa using h⟩,
|
||||
by simpa using h₁, by simpa using h₂⟩
|
||||
|
||||
@[simp] theorem find?_flatMap (xs : Array α) (f : α → Array β) (p : β → Bool) :
|
||||
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
|
||||
|
||||
@[simp] theorem find?_flatMap {xs : Array α} {f : α → Array β} {p : β → Bool} :
|
||||
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
|
||||
cases xs
|
||||
simp [List.find?_flatMap, Array.flatMap_toArray]
|
||||
|
||||
theorem find?_flatMap_eq_none {xs : Array α} {f : α → Array β} {p : β → Bool} :
|
||||
theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β → Bool} :
|
||||
(xs.flatMap f).find? p = none ↔ ∀ x ∈ xs, ∀ y ∈ f x, !p y := by
|
||||
simp
|
||||
|
||||
theorem find?_mkArray :
|
||||
find? p (mkArray n a) = if n = 0 then none else if p a then some a else none := by
|
||||
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
|
||||
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
|
||||
|
||||
theorem find?_replicate :
|
||||
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate]
|
||||
|
||||
@[simp] theorem find?_mkArray_of_length_pos (h : 0 < n) :
|
||||
find? p (mkArray n a) = if p a then some a else none := by
|
||||
simp [find?_mkArray, Nat.ne_of_gt h]
|
||||
@[deprecated find?_replicate (since := "2025-03-18")]
|
||||
abbrev find?_mkArray := @find?_replicate
|
||||
|
||||
@[simp] theorem find?_mkArray_of_pos (h : p a) :
|
||||
find? p (mkArray n a) = if n = 0 then none else some a := by
|
||||
simp [find?_mkArray, h]
|
||||
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
|
||||
find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
|
||||
simp [find?_mkArray, h]
|
||||
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos
|
||||
|
||||
@[simp] theorem find?_replicate_of_pos (h : p a) :
|
||||
find? p (replicate n a) = if n = 0 then none else some a := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
|
||||
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
|
||||
|
||||
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
|
||||
theorem find?_mkArray_eq_none {n : Nat} {a : α} {p : α → Bool} :
|
||||
(mkArray n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate_eq_none, Classical.or_iff_not_imp_left]
|
||||
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = none ↔ n = 0 ∨ !p a := by
|
||||
simp [← List.toArray_replicate, List.find?_replicate_eq_none_iff, Classical.or_iff_not_imp_left]
|
||||
|
||||
@[simp] theorem find?_mkArray_eq_some {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(mkArray n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
|
||||
|
||||
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
|
||||
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[simp] theorem get_find?_mkArray (n : Nat) (a : α) (p : α → Bool) (h) :
|
||||
((mkArray n a).find? p).get h = a := by
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
|
||||
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
|
||||
|
||||
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
|
||||
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
|
||||
|
||||
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α → Bool} (h) :
|
||||
((replicate n a).find? p).get h = a := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
theorem find?_pmap {P : α → Prop} (f : (a : α) → P a → β) (xs : Array α)
|
||||
(H : ∀ (a : α), a ∈ xs → P a) (p : β → Bool) :
|
||||
@[deprecated get_find?_replicate (since := "2025-03-18")]
|
||||
abbrev get_find?_mkArray := @get_find?_replicate
|
||||
|
||||
theorem find?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||||
(H : ∀ (a : α), a ∈ xs → P a) {p : β → Bool} :
|
||||
(xs.pmap f H).find? p = (xs.attach.find? (fun ⟨a, m⟩ => p (f a (H a m)))).map fun ⟨a, m⟩ => f a (H a m) := by
|
||||
simp only [pmap_eq_map_attach, find?_map]
|
||||
rfl
|
||||
|
||||
theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
|
||||
xs.find? p = some b ↔ p b ∧ ∃ i h, xs[i] = b ∧ ∀ j : Nat, (hj : j < i) → !p xs[j] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.find?_eq_some_iff_getElem]
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
|
||||
rcases xs with ⟨xs⟩
|
||||
exact List.findIdx_of_getElem?_eq_some (by simpa using w)
|
||||
|
||||
theorem findIdx_getElem {xs : Array α} {w : xs.findIdx p < xs.size} :
|
||||
p xs[xs.findIdx p] :=
|
||||
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
|
||||
|
||||
theorem findIdx_lt_size_of_exists {xs : Array α} (h : ∃ x ∈ xs, p x) :
|
||||
xs.findIdx p < xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.findIdx_lt_length_of_exists (by simpa using h)
|
||||
|
||||
theorem findIdx_getElem?_eq_getElem_of_exists {xs : Array α} (h : ∃ x ∈ xs, p x) :
|
||||
xs[xs.findIdx p]? = some (xs[xs.findIdx p]'(xs.findIdx_lt_size_of_exists h)) :=
|
||||
getElem?_eq_getElem (findIdx_lt_size_of_exists h)
|
||||
|
||||
@[simp]
|
||||
theorem findIdx_eq_size {p : α → Bool} {xs : Array α} :
|
||||
xs.findIdx p = xs.size ↔ ∀ x ∈ xs, p x = false := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem findIdx_eq_size_of_false {p : α → Bool} {xs : Array α} (h : ∀ x ∈ xs, p x = false) :
|
||||
xs.findIdx p = xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
theorem findIdx_le_size {p : α → Bool} {xs : Array α} : xs.findIdx p ≤ xs.size := by
|
||||
by_cases e : ∃ x ∈ xs, p x
|
||||
· exact Nat.le_of_lt (findIdx_lt_size_of_exists e)
|
||||
· simp at e
|
||||
exact Nat.le_of_eq (findIdx_eq_size.mpr e)
|
||||
|
||||
@[simp]
|
||||
theorem findIdx_lt_size {p : α → Bool} {xs : Array α} :
|
||||
xs.findIdx p < xs.size ↔ ∃ x ∈ xs, p x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
|
||||
theorem not_of_lt_findIdx {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.findIdx p) :
|
||||
p (xs[i]'(Nat.le_trans h findIdx_le_size)) = false := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.not_of_lt_findIdx (by simpa using h)
|
||||
|
||||
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
|
||||
theorem le_findIdx_of_not {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
|
||||
(h2 : ∀ j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false) : i ≤ xs.findIdx p := by
|
||||
apply Decidable.byContradiction
|
||||
intro f
|
||||
simp only [Nat.not_le] at f
|
||||
exact absurd (@findIdx_getElem _ p xs (Nat.lt_trans f h)) (by simpa using h2 (xs.findIdx p) f)
|
||||
|
||||
/-- If `¬ p xs[j]` for all `j ≤ i`, then `i < xs.findIdx p`. -/
|
||||
theorem lt_findIdx_of_not {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
|
||||
(h2 : ∀ j (hji : j ≤ i), ¬p (xs[j]'(Nat.lt_of_le_of_lt hji h))) : i < xs.findIdx p := by
|
||||
apply Decidable.byContradiction
|
||||
intro f
|
||||
simp only [Nat.not_lt] at f
|
||||
exact absurd (@findIdx_getElem _ p xs (Nat.lt_of_le_of_lt f h)) (h2 (xs.findIdx p) f)
|
||||
|
||||
/-- `xs.findIdx p = i` iff `p xs[i]` and `¬ p xs [j]` for all `j < i`. -/
|
||||
theorem findIdx_eq {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size) :
|
||||
xs.findIdx p = i ↔ p xs[i] ∧ ∀ j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false := by
|
||||
refine ⟨fun f ↦ ⟨f ▸ (@findIdx_getElem _ p xs (f ▸ h)), fun _ hji ↦ not_of_lt_findIdx (f ▸ hji)⟩,
|
||||
fun ⟨_, h2⟩ ↦ ?_⟩
|
||||
apply Nat.le_antisymm _ (le_findIdx_of_not h h2)
|
||||
apply Decidable.byContradiction
|
||||
intro h3
|
||||
simp at h3
|
||||
simp_all [not_of_lt_findIdx h3]
|
||||
|
||||
theorem findIdx_append {p : α → Bool} {xs ys : Array α} :
|
||||
(xs ++ ys).findIdx p =
|
||||
if xs.findIdx p < xs.size then xs.findIdx p else ys.findIdx p + xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp [List.findIdx_append]
|
||||
|
||||
theorem findIdx_le_findIdx {xs : Array α} {p q : α → Bool} (h : ∀ x ∈ xs, p x → q x) : xs.findIdx q ≤ xs.findIdx p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all [List.findIdx_le_findIdx]
|
||||
|
||||
@[simp] theorem findIdx_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findIdx f = xs.unattach.findIdx g := by
|
||||
cases xs
|
||||
simp [hf]
|
||||
|
||||
theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x ∈ xs.extract 0 (xs.findIdx p)) :
|
||||
p x = false := by
|
||||
rcases xs with ⟨xs⟩
|
||||
exact List.false_of_mem_take_findIdx (by simpa using h)
|
||||
|
||||
@[simp] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α → Bool} :
|
||||
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem min_findIdx_findIdx {xs : Array α} {p q : α → Bool} :
|
||||
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### findIdx? -/
|
||||
|
||||
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
|
||||
|
||||
@[simp]
|
||||
theorem findIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = none ↔ ∀ x, x ∈ xs → p x = false := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem findIdx?_isSome {xs : Array α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isSome = xs.any p := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_isSome]
|
||||
|
||||
theorem findIdx?_isNone {xs : Array α} {p : α → Bool} :
|
||||
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_isNone]
|
||||
|
||||
theorem findIdx?_eq_some_iff_findIdx_eq {xs : Array α} {p : α → Bool} {i : Nat} :
|
||||
xs.findIdx? p = some i ↔ i < xs.size ∧ xs.findIdx p = i := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_some_iff_findIdx_eq]
|
||||
|
||||
theorem findIdx?_eq_some_of_exists {xs : Array α} {p : α → Bool} (h : ∃ x, x ∈ xs ∧ p x) :
|
||||
xs.findIdx? p = some (xs.findIdx p) := by
|
||||
rw [findIdx?_eq_some_iff_findIdx_eq]
|
||||
exact ⟨findIdx_lt_size_of_exists h, rfl⟩
|
||||
|
||||
theorem findIdx?_eq_none_iff_findIdx_eq {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = none ↔ xs.findIdx p = xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_none_iff_findIdx_eq]
|
||||
|
||||
theorem findIdx?_eq_guard_findIdx_lt {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = Option.guard (fun i => i < xs.size) (xs.findIdx p) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_guard_findIdx_lt]
|
||||
|
||||
theorem findIdx?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {i : Nat} :
|
||||
xs.findIdx? p = some i ↔
|
||||
∃ h : i < xs.size, p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_some_iff_getElem]
|
||||
|
||||
theorem of_findIdx?_eq_some {xs : Array α} {p : α → Bool} (w : xs.findIdx? p = some i) :
|
||||
match xs[i]? with | some a => p a | none => false := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.of_findIdx?_eq_some (by simpa using w)
|
||||
|
||||
theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p = none) :
|
||||
∀ i : Nat, match xs[i]? with | some a => ¬ p a | none => true := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.of_findIdx?_eq_none (by simpa using w)
|
||||
|
||||
@[simp] theorem findIdx?_map {f : β → α} {xs : Array β} {p : α → Bool} :
|
||||
findIdx? p (xs.map f) = xs.findIdx? (p ∘ f) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_map]
|
||||
|
||||
@[simp] theorem findIdx?_append :
|
||||
(xs ++ ys : Array α).findIdx? p =
|
||||
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.size) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp [List.findIdx?_append]
|
||||
|
||||
theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
|
||||
xss.flatten.findIdx? p =
|
||||
(xss.findIdx? (·.any p)).map
|
||||
fun i => ((xss.take i).map Array.size).sum +
|
||||
(xss[i]?.map fun xs => xs.findIdx p).getD 0 := by
|
||||
cases xss using array₂_induction
|
||||
simp [List.findIdx?_flatten, Function.comp_def]
|
||||
|
||||
@[simp] theorem findIdx?_replicate :
|
||||
(replicate n a).findIdx? p = if 0 < n ∧ p a then some 0 else none := by
|
||||
rw [← List.toArray_replicate]
|
||||
simp only [List.findIdx?_toArray]
|
||||
simp
|
||||
|
||||
@[deprecated findIdx?_replicate (since := "2025-03-18")]
|
||||
abbrev findIdx?_mkArray := @findIdx?_replicate
|
||||
|
||||
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = xs.zipIdx.findSome? fun ⟨a, i⟩ => if p a then some i else none := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_findSome?_zipIdx]
|
||||
|
||||
theorem findIdx?_eq_fst_find?_zipIdx {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx? p = (xs.zipIdx.find? fun ⟨x, _⟩ => p x).map (·.2) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_fst_find?_zipIdx]
|
||||
|
||||
-- See also `findIdx_le_findIdx`.
|
||||
theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : Array α} {p q : α → Bool} (w : ∀ x ∈ xs, p x → q x) :
|
||||
xs.findIdx? q = none → xs.findIdx? p = none := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.findIdx?_eq_none_of_findIdx?_eq_none (by simpa using w)
|
||||
|
||||
theorem findIdx_eq_getD_findIdx? {xs : Array α} {p : α → Bool} :
|
||||
xs.findIdx p = (xs.findIdx? p).getD xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx_eq_getD_findIdx?]
|
||||
|
||||
theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bool} (w : ∀ x ∈ xs, p x → q x) {i : Nat}
|
||||
(h : xs.findIdx? p = some i) : ∃ j, j ≤ i ∧ xs.findIdx? q = some j := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.findIdx?_eq_some_le_of_findIdx?_eq_some (by simpa using w) (by simpa using h)]
|
||||
|
||||
@[simp] theorem findIdx?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findIdx? f = xs.unattach.findIdx? g := by
|
||||
cases xs
|
||||
simp [hf]
|
||||
|
||||
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α → Bool} :
|
||||
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := by simp
|
||||
|
||||
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
|
||||
theorem findFinIdx?_congr {p : α → Bool} {xs ys : Array α} (w : xs = ys) :
|
||||
findFinIdx? p xs = (findFinIdx? p ys).map (fun i => i.cast (by simp [w])) := by
|
||||
subst w
|
||||
simp
|
||||
|
||||
theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α → Bool} :
|
||||
xs.findFinIdx? p =
|
||||
(xs.findIdx? p).pmap
|
||||
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact ⟨i, m.choose⟩)
|
||||
(fun i h => h) := by
|
||||
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
|
||||
|
||||
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
|
||||
xs.findFinIdx? p = none ↔ ∀ x, x ∈ xs → ¬ p x := by
|
||||
simp [findFinIdx?_eq_pmap_findIdx?]
|
||||
|
||||
@[simp]
|
||||
theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.size} :
|
||||
xs.findFinIdx? p = some i ↔
|
||||
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
|
||||
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
|
||||
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
|
||||
constructor
|
||||
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
|
||||
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
|
||||
· rintro ⟨h, w⟩
|
||||
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
|
||||
|
||||
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
|
||||
cases xs
|
||||
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
|
||||
rw [findFinIdx?_congr List.unattach_toArray]
|
||||
simp [Function.comp_def]
|
||||
|
||||
/-! ### idxOf
|
||||
|
||||
The verification API for `idxOf` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
|
||||
-/
|
||||
|
||||
theorem idxOf_append [BEq α] [LawfulBEq α] {xs ys : Array α} {a : α} :
|
||||
(xs ++ ys).idxOf a = if a ∈ xs then xs.idxOf a else ys.idxOf a + xs.size := by
|
||||
rw [idxOf, findIdx_append]
|
||||
split <;> rename_i h
|
||||
· rw [if_pos]
|
||||
simpa using h
|
||||
· rw [if_neg]
|
||||
simpa using h
|
||||
|
||||
theorem idxOf_eq_size [BEq α] [LawfulBEq α] {xs : Array α} (h : a ∉ xs) : xs.idxOf a = xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.idxOf_eq_length (by simpa using h)]
|
||||
|
||||
theorem idxOf_lt_length [BEq α] [LawfulBEq α] {xs : Array α} (h : a ∈ xs) : xs.idxOf a < xs.size := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.idxOf_lt_length (by simpa using h)]
|
||||
|
||||
|
||||
/-! ### idxOf?
|
||||
|
||||
The verification API for `idxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = none ↔ a ∉ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.idxOf?_eq_none_iff]
|
||||
|
||||
/-! ### finIdxOf?
|
||||
|
||||
The verification API for `finIdxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.finIdxOf? a = none ↔ a ∉ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.finIdxOf?_eq_none_iff]
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Fin xs.size} :
|
||||
xs.finIdxOf? a = some i ↔ xs[i] = a ∧ ∀ j (_ : j < i), ¬xs[j] = a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.finIdxOf?_eq_some_iff]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -7,40 +7,43 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### getLit -/
|
||||
|
||||
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
|
||||
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
|
||||
abbrev getLit {α : Type u} {n : Nat} (xs : Array α) (i : Nat) (h₁ : xs.size = n) (h₂ : i < n) : α :=
|
||||
have := h₁.symm ▸ h₂
|
||||
a[i]
|
||||
xs[i]
|
||||
|
||||
theorem extLit {n : Nat}
|
||||
(a b : Array α)
|
||||
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
|
||||
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
(xs ys : Array α)
|
||||
(hsz₁ : xs.size = n) (hsz₂ : ys.size = n)
|
||||
(h : (i : Nat) → (hi : i < n) → xs.getLit i hsz₁ hi = ys.getLit i hsz₂ hi) : xs = ys :=
|
||||
Array.ext (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i ≤ a.size → List α → List α
|
||||
def toListLitAux (xs : Array α) (n : Nat) (hsz : xs.size = n) : ∀ (i : Nat), i ≤ xs.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
| (i+1), hi, acc => toListLitAux xs n hsz i (Nat.le_of_succ_le hi) (xs.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux a n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
def toArrayLit (xs : Array α) (n : Nat) (hsz : xs.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux xs n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
|
||||
theorem toArrayLit_eq (xs : Array α) (n : Nat) (hsz : xs.size = n) : xs = toArrayLit xs n hsz := by
|
||||
apply ext'
|
||||
simp [toArrayLit, toList_toArray]
|
||||
have hle : n ≤ as.size := hsz ▸ Nat.le_refl _
|
||||
have hge : as.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
simp [toArrayLit, List.toList_toArray]
|
||||
have hle : n ≤ xs.size := hsz ▸ Nat.le_refl _
|
||||
have hge : xs.size ≤ n := hsz ▸ Nat.le_refl _
|
||||
have := go n hle
|
||||
rw [List.drop_eq_nil_of_le hge] at this
|
||||
rw [this]
|
||||
where
|
||||
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) ▸ h₂) :=
|
||||
getLit_eq (xs : Array α) (i : Nat) (h₁ : xs.size = n) (h₂ : i < n) : xs.getLit i h₁ h₂ = getElem xs.toList i ((id (α := xs.toList.length = n) h₁) ▸ h₂) :=
|
||||
rfl
|
||||
go (i : Nat) (hi : i ≤ as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
|
||||
go (i : Nat) (hi : i ≤ xs.size) : toListLitAux xs n hsz i hi (xs.toList.drop i) = xs.toList := by
|
||||
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop_succ_eq_drop, *]
|
||||
|
||||
end Array
|
||||
|
||||
132
src/Init/Data/Array/InsertIdx.lean
Normal file
132
src/Init/Data/Array/InsertIdx.lean
Normal file
@@ -0,0 +1,132 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.InsertIdx
|
||||
|
||||
/-!
|
||||
# insertIdx
|
||||
|
||||
Proves various lemmas about `Array.insertIdx`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
open Function
|
||||
|
||||
open Nat
|
||||
|
||||
namespace Array
|
||||
|
||||
universe u
|
||||
|
||||
variable {α : Type u}
|
||||
|
||||
section InsertIdx
|
||||
|
||||
variable {a : α}
|
||||
|
||||
@[simp] theorem toList_insertIdx {xs : Array α} {i : Nat} {x : α} (h : i ≤ xs.size) :
|
||||
(xs.insertIdx i x h).toList = xs.toList.insertIdx i x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem size_insertIdx {xs : Array α} (h : i ≤ xs.size) : (xs.insertIdx i a).size = xs.size + 1 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.length_insertIdx, h]
|
||||
|
||||
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i ≤ xs.size) :
|
||||
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
theorem insertIdx_eraseIdx_of_ge {as : Array α}
|
||||
(w₁ : i < as.size) (w₂ : j ≤ (as.eraseIdx i).size) (h : i ≤ j) :
|
||||
(as.eraseIdx i).insertIdx j a =
|
||||
(as.insertIdx (j + 1) a (by simp at w₂; omega)).eraseIdx i (by simp_all; omega) := by
|
||||
cases as
|
||||
simpa using List.insertIdx_eraseIdx_of_ge (by simpa) (by simpa)
|
||||
|
||||
theorem insertIdx_eraseIdx_of_le {as : Array α}
|
||||
(w₁ : i < as.size) (w₂ : j ≤ (as.eraseIdx i).size) (h : j ≤ i) :
|
||||
(as.eraseIdx i).insertIdx j a =
|
||||
(as.insertIdx j a (by simp at w₂; omega)).eraseIdx (i + 1) (by simp_all) := by
|
||||
cases as
|
||||
simpa using List.insertIdx_eraseIdx_of_le (by simpa) (by simpa)
|
||||
|
||||
theorem insertIdx_comm (a b : α) {i j : Nat} {xs : Array α} (_ : i ≤ j) (_ : j ≤ xs.size) :
|
||||
(xs.insertIdx i a).insertIdx (j + 1) b (by simpa) =
|
||||
(xs.insertIdx j b).insertIdx i a (by simp; omega) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.insertIdx_comm a b (by simpa) (by simpa)
|
||||
|
||||
theorem mem_insertIdx {xs : Array α} {h : i ≤ xs.size} : a ∈ xs.insertIdx i b h ↔ a = b ∨ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simpa using List.mem_insertIdx (by simpa)
|
||||
|
||||
@[simp]
|
||||
theorem insertIdx_size_self {xs : Array α} {x : α} : xs.insertIdx xs.size x = xs.push x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem getElem_insertIdx {xs : Array α} {x : α} {i k : Nat} (w : i ≤ xs.size) (h : k < (xs.insertIdx i x).size) :
|
||||
(xs.insertIdx i x)[k] =
|
||||
if h₁ : k < i then
|
||||
xs[k]'(by simp [size_insertIdx] at h; omega)
|
||||
else
|
||||
if h₂ : k = i then
|
||||
x
|
||||
else
|
||||
xs[k-1]'(by simp [size_insertIdx] at h; omega) := by
|
||||
cases xs
|
||||
simp [List.getElem_insertIdx, w]
|
||||
|
||||
theorem getElem_insertIdx_of_lt {xs : Array α} {x : α} {i k : Nat} (w : i ≤ xs.size) (h : k < i) :
|
||||
(xs.insertIdx i x)[k]'(by simp; omega) = xs[k] := by
|
||||
simp [getElem_insertIdx, w, h]
|
||||
|
||||
theorem getElem_insertIdx_self {xs : Array α} {x : α} {i : Nat} (w : i ≤ xs.size) :
|
||||
(xs.insertIdx i x)[i]'(by simp; omega) = x := by
|
||||
simp [getElem_insertIdx, w]
|
||||
|
||||
theorem getElem_insertIdx_of_gt {xs : Array α} {x : α} {i k : Nat} (w : k ≤ xs.size) (h : k > i) :
|
||||
(xs.insertIdx i x)[k]'(by simp; omega) = xs[k - 1]'(by omega) := by
|
||||
simp [getElem_insertIdx, w, h]
|
||||
rw [dif_neg (by omega), dif_neg (by omega)]
|
||||
|
||||
theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i ≤ xs.size) :
|
||||
(xs.insertIdx i x)[k]? =
|
||||
if k < i then
|
||||
xs[k]?
|
||||
else
|
||||
if k = i then
|
||||
if k ≤ xs.size then some x else none
|
||||
else
|
||||
xs[k-1]? := by
|
||||
cases xs
|
||||
simp [List.getElem?_insertIdx, h]
|
||||
|
||||
theorem getElem?_insertIdx_of_lt {xs : Array α} {x : α} {i k : Nat} (w : i ≤ xs.size) (h : k < i) :
|
||||
(xs.insertIdx i x)[k]? = xs[k]? := by
|
||||
rw [getElem?_insertIdx, if_pos h]
|
||||
|
||||
theorem getElem?_insertIdx_self {xs : Array α} {x : α} {i : Nat} (w : i ≤ xs.size) :
|
||||
(xs.insertIdx i x)[i]? = some x := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega), if_pos rfl, if_pos w]
|
||||
|
||||
theorem getElem?_insertIdx_of_ge {xs : Array α} {x : α} {i k : Nat} (w : i < k) (h : k ≤ xs.size) :
|
||||
(xs.insertIdx i x)[k]? = xs[k - 1]? := by
|
||||
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
|
||||
|
||||
end InsertIdx
|
||||
|
||||
end Array
|
||||
@@ -6,23 +6,32 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
@[inline] def Array.insertionSort (a : Array α) (lt : α → α → Bool := by exact (· < ·)) : Array α :=
|
||||
traverse a 0 a.size
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
/--
|
||||
Sorts an array using insertion sort.
|
||||
|
||||
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
|
||||
decidable to be used for sorting.
|
||||
-/
|
||||
@[inline] def Array.insertionSort (xs : Array α) (lt : α → α → Bool := by exact (· < ·)) : Array α :=
|
||||
traverse xs 0 xs.size
|
||||
where
|
||||
@[specialize] traverse (a : Array α) (i : Nat) (fuel : Nat) : Array α :=
|
||||
@[specialize] traverse (xs : Array α) (i : Nat) (fuel : Nat) : Array α :=
|
||||
match fuel with
|
||||
| 0 => a
|
||||
| 0 => xs
|
||||
| fuel+1 =>
|
||||
if h : i < a.size then
|
||||
traverse (swapLoop a i h) (i+1) fuel
|
||||
if h : i < xs.size then
|
||||
traverse (swapLoop xs i h) (i+1) fuel
|
||||
else
|
||||
a
|
||||
@[specialize] swapLoop (a : Array α) (j : Nat) (h : j < a.size) : Array α :=
|
||||
xs
|
||||
@[specialize] swapLoop (xs : Array α) (j : Nat) (h : j < xs.size) : Array α :=
|
||||
match (generalizing := false) he:j with -- using `generalizing` because we don't want to refine the type of `h`
|
||||
| 0 => a
|
||||
| 0 => xs
|
||||
| j'+1 =>
|
||||
have h' : j' < a.size := by subst j; exact Nat.lt_trans (Nat.lt_succ_self _) h
|
||||
if lt a[j] a[j'] then
|
||||
swapLoop (a.swap j j') j' (by rw [size_swap]; assumption; done)
|
||||
have h' : j' < xs.size := by subst j; exact Nat.lt_trans (Nat.lt_succ_self _) h
|
||||
if lt xs[j] xs[j'] then
|
||||
swapLoop (xs.swap j j') j' (by rw [size_swap]; assumption; done)
|
||||
else
|
||||
a
|
||||
xs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,14 +8,18 @@ import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Range
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/--
|
||||
Lexicographic comparator for arrays.
|
||||
Compares arrays lexicographically with respect to a comparison `lt` on their elements.
|
||||
|
||||
`lex as bs lt` is true if
|
||||
- `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`, or
|
||||
- there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
|
||||
Specifically, `Array.lex as bs lt` is true if
|
||||
* `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`,
|
||||
or
|
||||
* there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
|
||||
-/
|
||||
def lex [BEq α] (as bs : Array α) (lt : α → α → Bool := by exact (· < ·)) : Bool := Id.run do
|
||||
for h : i in [0 : min as.size bs.size] do
|
||||
|
||||
@@ -7,22 +7,25 @@ prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Lex
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### Lexicographic ordering -/
|
||||
|
||||
@[simp] theorem _root_.List.lt_toArray [LT α] (l₁ l₂ : List α) : l₁.toArray < l₂.toArray ↔ l₁ < l₂ := Iff.rfl
|
||||
@[simp] theorem _root_.List.le_toArray [LT α] (l₁ l₂ : List α) : l₁.toArray ≤ l₂.toArray ↔ l₁ ≤ l₂ := Iff.rfl
|
||||
@[simp] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray ↔ l₁ < l₂ := Iff.rfl
|
||||
@[simp] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray ≤ l₂.toArray ↔ l₁ ≤ l₂ := Iff.rfl
|
||||
|
||||
@[simp] theorem lt_toList [LT α] (l₁ l₂ : Array α) : l₁.toList < l₂.toList ↔ l₁ < l₂ := Iff.rfl
|
||||
@[simp] theorem le_toList [LT α] (l₁ l₂ : Array α) : l₁.toList ≤ l₂.toList ↔ l₁ ≤ l₂ := Iff.rfl
|
||||
@[simp] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList ↔ xs < ys := Iff.rfl
|
||||
@[simp] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ≤ ys.toList ↔ xs ≤ ys := Iff.rfl
|
||||
|
||||
protected theorem not_lt_iff_ge [LT α] (l₁ l₂ : List α) : ¬ l₁ < l₂ ↔ l₂ ≤ l₁ := Iff.rfl
|
||||
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
|
||||
protected theorem not_lt_iff_ge [LT α] {l₁ l₂ : List α} : ¬ l₁ < l₂ ↔ l₂ ≤ l₁ := Iff.rfl
|
||||
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {l₁ l₂ : List α} :
|
||||
¬ l₁ ≤ l₂ ↔ l₂ < l₁ :=
|
||||
Decidable.not_not
|
||||
|
||||
@[simp] theorem lex_empty [BEq α] {lt : α → α → Bool} (l : Array α) : l.lex #[] lt = false := by
|
||||
@[simp] theorem lex_empty [BEq α] {lt : α → α → Bool} {xs : Array α} : xs.lex #[] lt = false := by
|
||||
simp [lex, Id.run]
|
||||
|
||||
@[simp] theorem singleton_lex_singleton [BEq α] {lt : α → α → Bool} : #[a].lex #[b] lt = lt a b := by
|
||||
@@ -33,7 +36,7 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
(#[a] ++ xs).lex (#[b] ++ ys) lt =
|
||||
(lt a b || a == b && xs.lex ys lt) := by
|
||||
simp only [lex, Id.run]
|
||||
simp only [Std.Range.forIn'_eq_forIn'_range', size_append, size_toArray, List.length_singleton,
|
||||
simp only [Std.Range.forIn'_eq_forIn'_range', size_append, List.size_toArray, List.length_singleton,
|
||||
Nat.add_comm 1]
|
||||
simp [Nat.add_min_add_right, List.range'_succ, getElem_append_left, List.range'_succ_left,
|
||||
getElem_append_right]
|
||||
@@ -42,7 +45,7 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
cases a == b <;> simp
|
||||
· simp
|
||||
|
||||
@[simp] theorem _root_.List.lex_toArray [BEq α] (lt : α → α → Bool) (l₁ l₂ : List α) :
|
||||
@[simp] theorem _root_.List.lex_toArray [BEq α] {lt : α → α → Bool} {l₁ l₂ : List α} :
|
||||
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
|
||||
induction l₁ generalizing l₂ with
|
||||
| nil => cases l₂ <;> simp [lex, Id.run]
|
||||
@@ -52,35 +55,35 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
| cons y l₂ =>
|
||||
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
|
||||
|
||||
@[simp] theorem lex_toList [BEq α] (lt : α → α → Bool) (l₁ l₂ : Array α) :
|
||||
l₁.toList.lex l₂.toList lt = l₁.lex l₂ lt := by
|
||||
cases l₁ <;> cases l₂ <;> simp
|
||||
@[simp] theorem lex_toList [BEq α] {lt : α → α → Bool} {xs ys : Array α} :
|
||||
xs.toList.lex ys.toList lt = xs.lex ys lt := by
|
||||
cases xs <;> cases ys <;> simp
|
||||
|
||||
protected theorem lt_irrefl [LT α] [Std.Irrefl (· < · : α → α → Prop)] (l : Array α) : ¬ l < l :=
|
||||
List.lt_irrefl l.toList
|
||||
protected theorem lt_irrefl [LT α] [Std.Irrefl (· < · : α → α → Prop)] (xs : Array α) : ¬ xs < xs :=
|
||||
List.lt_irrefl xs.toList
|
||||
|
||||
instance ltIrrefl [LT α] [Std.Irrefl (· < · : α → α → Prop)] : Std.Irrefl (α := Array α) (· < ·) where
|
||||
irrefl := Array.lt_irrefl
|
||||
|
||||
@[simp] theorem not_lt_empty [LT α] (l : Array α) : ¬ l < #[] := List.not_lt_nil l.toList
|
||||
@[simp] theorem empty_le [LT α] (l : Array α) : #[] ≤ l := List.nil_le l.toList
|
||||
@[simp] theorem not_lt_empty [LT α] (xs : Array α) : ¬ xs < #[] := List.not_lt_nil xs.toList
|
||||
@[simp] theorem empty_le [LT α] (xs : Array α) : #[] ≤ xs := List.nil_le xs.toList
|
||||
|
||||
@[simp] theorem le_empty [LT α] (l : Array α) : l ≤ #[] ↔ l = #[] := by
|
||||
cases l
|
||||
@[simp] theorem le_empty [LT α] {xs : Array α} : xs ≤ #[] ↔ xs = #[] := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem empty_lt_push [LT α] (l : Array α) (a : α) : #[] < l.push a := by
|
||||
rcases l with (_ | ⟨x, l⟩) <;> simp
|
||||
@[simp] theorem empty_lt_push [LT α] (xs : Array α) (a : α) : #[] < xs.push a := by
|
||||
rcases xs with (_ | ⟨x, xs⟩) <;> simp
|
||||
|
||||
protected theorem le_refl [LT α] [i₀ : Std.Irrefl (· < · : α → α → Prop)] (l : Array α) : l ≤ l :=
|
||||
List.le_refl l.toList
|
||||
protected theorem le_refl [LT α] [i₀ : Std.Irrefl (· < · : α → α → Prop)] (xs : Array α) : xs ≤ xs :=
|
||||
List.le_refl xs.toList
|
||||
|
||||
instance [LT α] [Std.Irrefl (· < · : α → α → Prop)] : Std.Refl (· ≤ · : Array α → Array α → Prop) where
|
||||
refl := Array.le_refl
|
||||
|
||||
protected theorem lt_trans [LT α]
|
||||
[i₁ : Trans (· < · : α → α → Prop) (· < ·) (· < ·)]
|
||||
{l₁ l₂ l₃ : Array α} (h₁ : l₁ < l₂) (h₂ : l₂ < l₃) : l₁ < l₃ :=
|
||||
{xs ys zs : Array α} (h₁ : xs < ys) (h₂ : ys < zs) : xs < zs :=
|
||||
List.lt_trans h₁ h₂
|
||||
|
||||
instance [LT α] [Trans (· < · : α → α → Prop) (· < ·) (· < ·)] :
|
||||
@@ -92,7 +95,7 @@ protected theorem lt_of_le_of_lt [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[i₁ : Std.Asymm (· < · : α → α → Prop)]
|
||||
[i₂ : Std.Antisymm (¬ · < · : α → α → Prop)]
|
||||
[i₃ : Trans (¬ · < · : α → α → Prop) (¬ · < ·) (¬ · < ·)]
|
||||
{l₁ l₂ l₃ : Array α} (h₁ : l₁ ≤ l₂) (h₂ : l₂ < l₃) : l₁ < l₃ :=
|
||||
{xs ys zs : Array α} (h₁ : xs ≤ ys) (h₂ : ys < zs) : xs < zs :=
|
||||
List.lt_of_le_of_lt h₁ h₂
|
||||
|
||||
protected theorem le_trans [DecidableEq α] [LT α] [DecidableLT α]
|
||||
@@ -100,7 +103,7 @@ protected theorem le_trans [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Asymm (· < · : α → α → Prop)]
|
||||
[Std.Antisymm (¬ · < · : α → α → Prop)]
|
||||
[Trans (¬ · < · : α → α → Prop) (¬ · < ·) (¬ · < ·)]
|
||||
{l₁ l₂ l₃ : Array α} (h₁ : l₁ ≤ l₂) (h₂ : l₂ ≤ l₃) : l₁ ≤ l₃ :=
|
||||
{xs ys zs : Array α} (h₁ : xs ≤ ys) (h₂ : ys ≤ zs) : xs ≤ zs :=
|
||||
fun h₃ => h₁ (Array.lt_of_le_of_lt h₂ h₃)
|
||||
|
||||
instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
@@ -113,7 +116,7 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
|
||||
protected theorem lt_asymm [LT α]
|
||||
[i : Std.Asymm (· < · : α → α → Prop)]
|
||||
{l₁ l₂ : Array α} (h : l₁ < l₂) : ¬ l₂ < l₁ := List.lt_asymm h
|
||||
{xs ys : Array α} (h : xs < ys) : ¬ ys < xs := List.lt_asymm h
|
||||
|
||||
instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Asymm (· < · : α → α → Prop)] :
|
||||
@@ -121,26 +124,26 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
asymm _ _ := Array.lt_asymm
|
||||
|
||||
protected theorem le_total [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[i : Std.Total (¬ · < · : α → α → Prop)] (l₁ l₂ : Array α) : l₁ ≤ l₂ ∨ l₂ ≤ l₁ :=
|
||||
List.le_total _ _
|
||||
[i : Std.Total (¬ · < · : α → α → Prop)] (xs ys : Array α) : xs ≤ ys ∨ ys ≤ xs :=
|
||||
List.le_total xs.toList ys.toList
|
||||
|
||||
@[simp] protected theorem not_lt [LT α]
|
||||
{l₁ l₂ : Array α} : ¬ l₁ < l₂ ↔ l₂ ≤ l₁ := Iff.rfl
|
||||
{xs ys : Array α} : ¬ xs < ys ↔ ys ≤ xs := Iff.rfl
|
||||
|
||||
@[simp] protected theorem not_le [DecidableEq α] [LT α] [DecidableLT α]
|
||||
{l₁ l₂ : Array α} : ¬ l₂ ≤ l₁ ↔ l₁ < l₂ := Decidable.not_not
|
||||
{xs ys : Array α} : ¬ ys ≤ xs ↔ xs < ys := Decidable.not_not
|
||||
|
||||
protected theorem le_of_lt [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[i : Std.Total (¬ · < · : α → α → Prop)]
|
||||
{l₁ l₂ : Array α} (h : l₁ < l₂) : l₁ ≤ l₂ :=
|
||||
{xs ys : Array α} (h : xs < ys) : xs ≤ ys :=
|
||||
List.le_of_lt h
|
||||
|
||||
protected theorem le_iff_lt_or_eq [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Irrefl (· < · : α → α → Prop)]
|
||||
[Std.Antisymm (¬ · < · : α → α → Prop)]
|
||||
[Std.Total (¬ · < · : α → α → Prop)]
|
||||
{l₁ l₂ : Array α} : l₁ ≤ l₂ ↔ l₁ < l₂ ∨ l₁ = l₂ := by
|
||||
simpa using List.le_iff_lt_or_eq (l₁ := l₁.toList) (l₂ := l₂.toList)
|
||||
{xs ys : Array α} : xs ≤ ys ↔ xs < ys ∨ xs = ys := by
|
||||
simpa using List.le_iff_lt_or_eq (l₁ := xs.toList) (l₂ := ys.toList)
|
||||
|
||||
instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Total (¬ · < · : α → α → Prop)] :
|
||||
@@ -148,22 +151,22 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
|
||||
total := Array.le_total
|
||||
|
||||
@[simp] theorem lex_eq_true_iff_lt [DecidableEq α] [LT α] [DecidableLT α]
|
||||
{l₁ l₂ : Array α} : lex l₁ l₂ = true ↔ l₁ < l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
{xs ys : Array α} : lex xs ys = true ↔ xs < ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp
|
||||
|
||||
@[simp] theorem lex_eq_false_iff_ge [DecidableEq α] [LT α] [DecidableLT α]
|
||||
{l₁ l₂ : Array α} : lex l₁ l₂ = false ↔ l₂ ≤ l₁ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
{xs ys : Array α} : lex xs ys = false ↔ ys ≤ xs := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp [List.not_lt_iff_ge]
|
||||
|
||||
instance [DecidableEq α] [LT α] [DecidableLT α] : DecidableLT (Array α) :=
|
||||
fun l₁ l₂ => decidable_of_iff (lex l₁ l₂ = true) lex_eq_true_iff_lt
|
||||
fun xs ys => decidable_of_iff (lex xs ys = true) lex_eq_true_iff_lt
|
||||
|
||||
instance [DecidableEq α] [LT α] [DecidableLT α] : DecidableLE (Array α) :=
|
||||
fun l₁ l₂ => decidable_of_iff (lex l₂ l₁ = false) lex_eq_false_iff_ge
|
||||
fun xs ys => decidable_of_iff (lex ys xs = false) lex_eq_false_iff_ge
|
||||
|
||||
/--
|
||||
`l₁` is lexicographically less than `l₂` if either
|
||||
@@ -195,7 +198,7 @@ This formulation requires that `==` and `lt` are compatible in the following sen
|
||||
- `==` is symmetric
|
||||
(we unnecessarily further assume it is transitive, to make use of the existing typeclasses)
|
||||
- `lt` is irreflexive with respect to `==` (i.e. if `x == y` then `lt x y = false`
|
||||
- `lt` is asymmmetric (i.e. `lt x y = true → lt y x = false`)
|
||||
- `lt` is asymmetric (i.e. `lt x y = true → lt y x = false`)
|
||||
- `lt` is antisymmetric with respect to `==` (i.e. `lt x y = false → lt y x = false → x == y`)
|
||||
-/
|
||||
theorem lex_eq_false_iff_exists [BEq α] [PartialEquivBEq α] (lt : α → α → Bool)
|
||||
@@ -211,58 +214,58 @@ theorem lex_eq_false_iff_exists [BEq α] [PartialEquivBEq α] (lt : α → α
|
||||
cases l₂
|
||||
simp_all [List.lex_eq_false_iff_exists]
|
||||
|
||||
protected theorem lt_iff_exists [DecidableEq α] [LT α] [DecidableLT α] {l₁ l₂ : Array α} :
|
||||
l₁ < l₂ ↔
|
||||
(l₁ = l₂.take l₁.size ∧ l₁.size < l₂.size) ∨
|
||||
(∃ (i : Nat) (h₁ : i < l₁.size) (h₂ : i < l₂.size),
|
||||
protected theorem lt_iff_exists [DecidableEq α] [LT α] [DecidableLT α] {xs ys : Array α} :
|
||||
xs < ys ↔
|
||||
(xs = ys.take xs.size ∧ xs.size < ys.size) ∨
|
||||
(∃ (i : Nat) (h₁ : i < xs.size) (h₂ : i < ys.size),
|
||||
(∀ j, (hj : j < i) →
|
||||
l₁[j]'(Nat.lt_trans hj h₁) = l₂[j]'(Nat.lt_trans hj h₂)) ∧ l₁[i] < l₂[i]) := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
xs[j]'(Nat.lt_trans hj h₁) = ys[j]'(Nat.lt_trans hj h₂)) ∧ xs[i] < ys[i]) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp [List.lt_iff_exists]
|
||||
|
||||
protected theorem le_iff_exists [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Irrefl (· < · : α → α → Prop)]
|
||||
[Std.Asymm (· < · : α → α → Prop)]
|
||||
[Std.Antisymm (¬ · < · : α → α → Prop)] {l₁ l₂ : Array α} :
|
||||
l₁ ≤ l₂ ↔
|
||||
(l₁ = l₂.take l₁.size) ∨
|
||||
(∃ (i : Nat) (h₁ : i < l₁.size) (h₂ : i < l₂.size),
|
||||
[Std.Antisymm (¬ · < · : α → α → Prop)] {xs ys : Array α} :
|
||||
xs ≤ ys ↔
|
||||
(xs = ys.take xs.size) ∨
|
||||
(∃ (i : Nat) (h₁ : i < xs.size) (h₂ : i < ys.size),
|
||||
(∀ j, (hj : j < i) →
|
||||
l₁[j]'(Nat.lt_trans hj h₁) = l₂[j]'(Nat.lt_trans hj h₂)) ∧ l₁[i] < l₂[i]) := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
xs[j]'(Nat.lt_trans hj h₁) = ys[j]'(Nat.lt_trans hj h₂)) ∧ xs[i] < ys[i]) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp [List.le_iff_exists]
|
||||
|
||||
theorem append_left_lt [LT α] {l₁ l₂ l₃ : Array α} (h : l₂ < l₃) :
|
||||
l₁ ++ l₂ < l₁ ++ l₃ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
cases l₃
|
||||
theorem append_left_lt [LT α] {xs ys zs : Array α} (h : ys < zs) :
|
||||
xs ++ ys < xs ++ zs := by
|
||||
cases xs
|
||||
cases ys
|
||||
cases zs
|
||||
simpa using List.append_left_lt h
|
||||
|
||||
theorem append_left_le [DecidableEq α] [LT α] [DecidableLT α]
|
||||
[Std.Irrefl (· < · : α → α → Prop)]
|
||||
[Std.Asymm (· < · : α → α → Prop)]
|
||||
[Std.Antisymm (¬ · < · : α → α → Prop)]
|
||||
{l₁ l₂ l₃ : Array α} (h : l₂ ≤ l₃) :
|
||||
l₁ ++ l₂ ≤ l₁ ++ l₃ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
cases l₃
|
||||
{xs ys zs : Array α} (h : ys ≤ zs) :
|
||||
xs ++ ys ≤ xs ++ zs := by
|
||||
cases xs
|
||||
cases ys
|
||||
cases zs
|
||||
simpa using List.append_left_le h
|
||||
|
||||
theorem le_append_left [LT α] [Std.Irrefl (· < · : α → α → Prop)]
|
||||
{l₁ l₂ : Array α} : l₁ ≤ l₁ ++ l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
{xs ys : Array α} : xs ≤ xs ++ ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
simpa using List.le_append_left
|
||||
|
||||
protected theorem map_lt [LT α] [LT β]
|
||||
{l₁ l₂ : Array α} {f : α → β} (w : ∀ x y, x < y → f x < f y) (h : l₁ < l₂) :
|
||||
map f l₁ < map f l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
{xs ys : Array α} {f : α → β} (w : ∀ x y, x < y → f x < f y) (h : xs < ys) :
|
||||
map f xs < map f ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
simpa using List.map_lt w h
|
||||
|
||||
protected theorem map_le [DecidableEq α] [LT α] [DecidableLT α] [DecidableEq β] [LT β] [DecidableLT β]
|
||||
@@ -272,10 +275,10 @@ protected theorem map_le [DecidableEq α] [LT α] [DecidableLT α] [DecidableEq
|
||||
[Std.Irrefl (· < · : β → β → Prop)]
|
||||
[Std.Asymm (· < · : β → β → Prop)]
|
||||
[Std.Antisymm (¬ · < · : β → β → Prop)]
|
||||
{l₁ l₂ : Array α} {f : α → β} (w : ∀ x y, x < y → f x < f y) (h : l₁ ≤ l₂) :
|
||||
map f l₁ ≤ map f l₂ := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
{xs ys : Array α} {f : α → β} (w : ∀ x y, x < y → f x < f y) (h : xs ≤ ys) :
|
||||
map f xs ≤ map f ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
simpa using List.map_le w h
|
||||
|
||||
end Array
|
||||
|
||||
@@ -5,108 +5,501 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
-- This could also be proved from `SatisfiesM_mapIdxM` in Batteries.
|
||||
theorem mapFinIdx_induction (as : Array α) (f : Fin as.size → α → β)
|
||||
theorem mapFinIdx_induction (xs : Array α) (f : (i : Nat) → α → (h : i < xs.size) → β)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (Array.mapFinIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapFinIdx as f)[i]) := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p ⟨i, h⟩ bs[i]) (hm : motive j) :
|
||||
let arr : Array β := Array.mapFinIdxM.map (m := Id) as f i j h bs
|
||||
motive as.size ∧ ∃ eq : arr.size = as.size, ∀ i h, p ⟨i, h⟩ arr[i] := by
|
||||
(p : (i : Nat) → β → (h : i < xs.size) → Prop)
|
||||
(hs : ∀ i h, motive i → p i (f i xs[i] h) h ∧ motive (i + 1)) :
|
||||
motive xs.size ∧ ∃ eq : (Array.mapFinIdx xs f).size = xs.size,
|
||||
∀ i h, p i ((Array.mapFinIdx xs f)[i]) h := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p i bs[i] h) (hm : motive j) :
|
||||
let as : Array β := Array.mapFinIdxM.map (m := Id) xs f i j h bs
|
||||
motive xs.size ∧ ∃ eq : as.size = xs.size, ∀ i h, p i as[i] h := by
|
||||
induction i generalizing j bs with simp [mapFinIdxM.map]
|
||||
| zero =>
|
||||
have := (Nat.zero_add _).symm.trans h
|
||||
exact ⟨this ▸ hm, h₁ ▸ this, fun _ _ => h₂ ..⟩
|
||||
| succ i ih =>
|
||||
apply @ih (bs.push (f ⟨j, by omega⟩ as[j])) (j + 1) (by omega) (by simp; omega)
|
||||
apply @ih (bs.push (f j xs[j] (by omega))) (j + 1) (by omega) (by simp; omega)
|
||||
· intro i i_lt h'
|
||||
rw [getElem_push]
|
||||
split
|
||||
· apply h₂
|
||||
· simp only [size_push] at h'
|
||||
obtain rfl : i = j := by omega
|
||||
apply (hs ⟨i, by omega⟩ hm).1
|
||||
· exact (hs ⟨j, by omega⟩ hm).2
|
||||
apply (hs i (by omega) hm).1
|
||||
· exact (hs j (by omega) hm).2
|
||||
simp [mapFinIdx, mapFinIdxM]; exact go rfl nofun h0
|
||||
|
||||
theorem mapFinIdx_spec (as : Array α) (f : Fin as.size → α → β)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
∃ eq : (Array.mapFinIdx as f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((Array.mapFinIdx as f)[i]) :=
|
||||
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β}
|
||||
{p : (i : Nat) → β → (h : i < xs.size) → Prop} (hs : ∀ i h, p i (f i xs[i] h) h) :
|
||||
∃ eq : (Array.mapFinIdx xs f).size = xs.size,
|
||||
∀ i h, p i ((Array.mapFinIdx xs f)[i]) h :=
|
||||
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapFinIdx (a : Array α) (f : Fin a.size → α → β) : (a.mapFinIdx f).size = a.size :=
|
||||
(mapFinIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
@[simp] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
(xs.mapFinIdx f).size = xs.size :=
|
||||
(mapFinIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
|
||||
|
||||
@[simp] theorem size_zipWithIndex (as : Array α) : as.zipWithIndex.size = as.size :=
|
||||
Array.size_mapFinIdx _ _
|
||||
@[simp] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
|
||||
Array.size_mapFinIdx
|
||||
|
||||
@[simp] theorem getElem_mapFinIdx (a : Array α) (f : Fin a.size → α → β) (i : Nat)
|
||||
(h : i < (mapFinIdx a f).size) :
|
||||
(a.mapFinIdx f)[i] = f ⟨i, by simp_all⟩ (a[i]'(by simp_all)) :=
|
||||
(mapFinIdx_spec _ _ (fun i b => b = f i a[i]) fun _ => rfl).2 i _
|
||||
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx (a : Array α) (f : Fin a.size → α → β) (i : Nat) :
|
||||
(a.mapFinIdx f)[i]? =
|
||||
a[i]?.pbind fun b h => f ⟨i, (getElem?_eq_some_iff.1 h).1⟩ b := by
|
||||
@[simp] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {i : Nat}
|
||||
(h : i < (xs.mapFinIdx f).size) :
|
||||
(xs.mapFinIdx f)[i] = f i (xs[i]'(by simp_all)) (by simp_all) :=
|
||||
(mapFinIdx_spec (p := fun i b h => b = f i xs[i] h) fun _ _ => rfl).2 i _
|
||||
|
||||
@[simp] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {i : Nat} :
|
||||
(xs.mapFinIdx f)[i]? =
|
||||
xs[i]?.pbind fun b h => f i b (getElem?_eq_some_iff.1 h).1 := by
|
||||
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem toList_mapFinIdx (a : Array α) (f : Fin a.size → α → β) :
|
||||
(a.mapFinIdx f).toList = a.toList.mapFinIdx (fun i a => f ⟨i, by simp⟩ a) := by
|
||||
@[simp] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
(xs.mapFinIdx f).toList = xs.toList.mapFinIdx (fun i a h => f i a (by simpa)) := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
theorem mapIdx_induction (f : Nat → α → β) (as : Array α)
|
||||
(motive : Nat → Prop) (h0 : motive 0)
|
||||
(p : Fin as.size → β → Prop)
|
||||
(hs : ∀ i, motive i.1 → p i (f i as[i]) ∧ motive (i + 1)) :
|
||||
motive as.size ∧ ∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
mapFinIdx_induction as (fun i a => f i a) motive h0 p hs
|
||||
theorem mapIdx_induction {f : Nat → α → β} {xs : Array α}
|
||||
{motive : Nat → Prop} (h0 : motive 0)
|
||||
{p : (i : Nat) → β → (h : i < xs.size) → Prop}
|
||||
(hs : ∀ i h, motive i → p i (f i xs[i]) h ∧ motive (i + 1)) :
|
||||
motive xs.size ∧ ∃ eq : (xs.mapIdx f).size = xs.size,
|
||||
∀ i h, p i ((xs.mapIdx f)[i]) h :=
|
||||
mapFinIdx_induction xs (fun i a _ => f i a) motive h0 p hs
|
||||
|
||||
theorem mapIdx_spec (f : Nat → α → β) (as : Array α)
|
||||
(p : Fin as.size → β → Prop) (hs : ∀ i, p i (f i as[i])) :
|
||||
∃ eq : (as.mapIdx f).size = as.size,
|
||||
∀ i h, p ⟨i, h⟩ ((as.mapIdx f)[i]) :=
|
||||
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ => ⟨hs .., trivial⟩).2
|
||||
theorem mapIdx_spec {f : Nat → α → β} {xs : Array α}
|
||||
{p : (i : Nat) → β → (h : i < xs.size) → Prop} (hs : ∀ i h, p i (f i xs[i]) h) :
|
||||
∃ eq : (xs.mapIdx f).size = xs.size,
|
||||
∀ i h, p i ((xs.mapIdx f)[i]) h :=
|
||||
(mapIdx_induction (motive := fun _ => True) trivial fun _ _ _ => ⟨hs .., trivial⟩).2
|
||||
|
||||
@[simp] theorem size_mapIdx (f : Nat → α → β) (as : Array α) : (as.mapIdx f).size = as.size :=
|
||||
(mapIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
|
||||
@[simp] theorem size_mapIdx {f : Nat → α → β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
|
||||
(mapIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
|
||||
|
||||
@[simp] theorem getElem_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat)
|
||||
(h : i < (as.mapIdx f).size) :
|
||||
(as.mapIdx f)[i] = f i (as[i]'(by simp_all)) :=
|
||||
(mapIdx_spec _ _ (fun i b => b = f i as[i]) fun _ => rfl).2 i (by simp_all)
|
||||
@[simp] theorem getElem_mapIdx {f : Nat → α → β} {xs : Array α} {i : Nat}
|
||||
(h : i < (xs.mapIdx f).size) :
|
||||
(xs.mapIdx f)[i] = f i (xs[i]'(by simp_all)) :=
|
||||
(mapIdx_spec (p := fun i b h => b = f i xs[i]) fun _ _ => rfl).2 i (by simp_all)
|
||||
|
||||
@[simp] theorem getElem?_mapIdx (f : Nat → α → β) (as : Array α) (i : Nat) :
|
||||
(as.mapIdx f)[i]? =
|
||||
as[i]?.map (f i) := by
|
||||
@[simp] theorem getElem?_mapIdx {f : Nat → α → β} {xs : Array α} {i : Nat} :
|
||||
(xs.mapIdx f)[i]? =
|
||||
xs[i]?.map (f i) := by
|
||||
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
|
||||
|
||||
@[simp] theorem toList_mapIdx (f : Nat → α → β) (as : Array α) :
|
||||
(as.mapIdx f).toList = as.toList.mapIdx (fun i a => f i a) := by
|
||||
@[simp] theorem toList_mapIdx {f : Nat → α → β} {xs : Array α} :
|
||||
(xs.mapIdx f).toList = xs.toList.mapIdx (fun i a => f i a) := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem mapFinIdx_toArray (l : List α) (f : Fin l.length → α → β) :
|
||||
@[simp] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem mapIdx_toArray (f : Nat → α → β) (l : List α) :
|
||||
@[simp] theorem mapIdx_toArray {f : Nat → α → β} {l : List α} :
|
||||
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### zipIdx -/
|
||||
|
||||
@[simp] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
|
||||
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
|
||||
simp [zipIdx]
|
||||
|
||||
@[deprecated getElem_zipIdx (since := "2025-01-21")]
|
||||
abbrev getElem_zipWithIndex := @getElem_zipIdx
|
||||
|
||||
@[simp] theorem zipIdx_toArray {l : List α} {k : Nat} :
|
||||
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
|
||||
ext i hi₁ hi₂ <;> simp [Nat.add_comm]
|
||||
|
||||
@[deprecated zipIdx_toArray (since := "2025-01-21")]
|
||||
abbrev zipWithIndex_toArray := @zipIdx_toArray
|
||||
|
||||
@[simp] theorem toList_zipIdx {xs : Array α} {k : Nat} :
|
||||
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[deprecated toList_zipIdx (since := "2025-01-21")]
|
||||
abbrev toList_zipWithIndex := @toList_zipIdx
|
||||
|
||||
theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {xs : Array α} :
|
||||
(x, i) ∈ xs.zipIdx k ↔ k ≤ i ∧ xs[i - k]? = some x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mk_mem_zipIdx_iff_le_and_getElem?_sub]
|
||||
|
||||
/-- Variant of `mk_mem_zipIdx_iff_le_and_getElem?_sub` specialized at `k = 0`,
|
||||
to avoid the inequality and the subtraction. -/
|
||||
theorem mk_mem_zipIdx_iff_getElem? {x : α} {i : Nat} {xs : Array α} :
|
||||
(x, i) ∈ xs.zipIdx ↔ xs[i]? = x := by
|
||||
rw [mk_mem_zipIdx_iff_le_and_getElem?_sub]
|
||||
simp
|
||||
|
||||
theorem mem_zipIdx_iff_le_and_getElem?_sub {x : α × Nat} {xs : Array α} {k : Nat} :
|
||||
x ∈ xs.zipIdx k ↔ k ≤ x.2 ∧ xs[x.2 - k]? = some x.1 := by
|
||||
cases x
|
||||
simp [mk_mem_zipIdx_iff_le_and_getElem?_sub]
|
||||
|
||||
/-- Variant of `mem_zipIdx_iff_le_and_getElem?_sub` specialized at `k = 0`,
|
||||
to avoid the inequality and the subtraction. -/
|
||||
theorem mem_zipIdx_iff_getElem? {x : α × Nat} {xs : Array α} :
|
||||
x ∈ xs.zipIdx ↔ xs[x.2]? = some x.1 := by
|
||||
rw [mk_mem_zipIdx_iff_getElem?]
|
||||
|
||||
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
|
||||
abbrev mk_mem_zipWithIndex_iff_getElem? := @mk_mem_zipIdx_iff_getElem?
|
||||
|
||||
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-21")]
|
||||
abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
|
||||
|
||||
/-! ### mapFinIdx -/
|
||||
|
||||
@[congr] theorem mapFinIdx_congr {xs ys : Array α} (w : xs = ys)
|
||||
(f : (i : Nat) → α → (h : i < xs.size) → β) :
|
||||
mapFinIdx xs f = mapFinIdx ys (fun i a h => f i a (by simp [w]; omega)) := by
|
||||
subst w
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_empty {f : (i : Nat) → α → (h : i < 0) → β} : mapFinIdx #[] f = #[] :=
|
||||
rfl
|
||||
|
||||
theorem mapFinIdx_eq_ofFn {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f = Array.ofFn fun i : Fin xs.size => f i xs[i] i.2 := by
|
||||
cases xs
|
||||
simp [List.mapFinIdx_eq_ofFn]
|
||||
|
||||
theorem mapFinIdx_append {xs ys : Array α} {f : (i : Nat) → α → (h : i < (xs ++ ys).size) → β} :
|
||||
(xs ++ ys).mapFinIdx f =
|
||||
xs.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
|
||||
ys.mapFinIdx (fun i a h => f (i + xs.size) a (by simp; omega)) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp [List.mapFinIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_push {xs : Array α} {a : α} {f : (i : Nat) → α → (h : i < (xs.push a).size) → β} :
|
||||
mapFinIdx (xs.push a) f =
|
||||
(mapFinIdx xs (fun i a h => f i a (by simp; omega))).push (f xs.size a (by simp)) := by
|
||||
simp [← append_singleton, mapFinIdx_append]
|
||||
|
||||
theorem mapFinIdx_singleton {a : α} {f : (i : Nat) → α → (h : i < 1) → β} :
|
||||
#[a].mapFinIdx f = #[f 0 a (by simp)] := by
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_zipIdx_map {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f = xs.zipIdx.attach.map
|
||||
fun ⟨⟨x, i⟩, m⟩ =>
|
||||
f i x (by simp [mk_mem_zipIdx_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated mapFinIdx_eq_zipIdx_map (since := "2025-01-21")]
|
||||
abbrev mapFinIdx_eq_zipWithIndex_map := @mapFinIdx_eq_zipIdx_map
|
||||
|
||||
@[simp]
|
||||
theorem mapFinIdx_eq_empty_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f = #[] ↔ xs = #[] := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_ne_empty_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f ≠ #[] ↔ xs ≠ #[] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β}
|
||||
(h : b ∈ xs.mapFinIdx f) : ∃ (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
exact List.exists_of_mem_mapFinIdx (by simpa using h)
|
||||
|
||||
@[simp] theorem mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
b ∈ xs.mapFinIdx f ↔ ∃ (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {ys : Array β} :
|
||||
xs.mapFinIdx f = ys ↔ ∃ h : ys.size = xs.size, ∀ (i : Nat) (h : i < xs.size), ys[i] = f i xs[i] h := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simpa using List.mapFinIdx_eq_iff
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_singleton_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {b : β} :
|
||||
xs.mapFinIdx f = #[b] ↔ ∃ (a : α) (w : xs = #[a]), f 0 a (by simp [w]) = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem mapFinIdx_eq_append_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {ys zs : Array β} :
|
||||
xs.mapFinIdx f = ys ++ zs ↔
|
||||
∃ (ys' : Array α) (zs' : Array α) (w : xs = ys' ++ zs'),
|
||||
ys'.mapFinIdx (fun i a h => f i a (by simp [w]; omega)) = ys ∧
|
||||
zs'.mapFinIdx (fun i a h => f (i + ys'.size) a (by simp [w]; omega)) = zs := by
|
||||
rcases xs with ⟨l⟩
|
||||
rcases ys with ⟨l₁⟩
|
||||
rcases zs with ⟨l₂⟩
|
||||
simp only [List.mapFinIdx_toArray, List.append_toArray, mk.injEq, List.mapFinIdx_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, rfl⟩
|
||||
refine ⟨l₁.toArray, l₂.toArray, by simp_all⟩
|
||||
· rintro ⟨⟨l₁⟩, ⟨l₂⟩, rfl, h₁, h₂⟩
|
||||
simp [← toList_inj] at h₁ h₂
|
||||
obtain rfl := h₁
|
||||
obtain rfl := h₂
|
||||
refine ⟨l₁, l₂, by simp_all⟩
|
||||
|
||||
theorem mapFinIdx_eq_push_iff {xs : Array α} {b : β} {f : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f = ys.push b ↔
|
||||
∃ (zs : Array α) (a : α) (w : xs = zs.push a),
|
||||
zs.mapFinIdx (fun i a h => f i a (by simp [w]; omega)) = ys ∧ b = f (xs.size - 1) a (by simp [w]) := by
|
||||
rw [push_eq_append, mapFinIdx_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨ys', zs', rfl, rfl, h₂⟩
|
||||
simp only [mapFinIdx_eq_singleton_iff, Nat.zero_add] at h₂
|
||||
obtain ⟨a, rfl, rfl⟩ := h₂
|
||||
exact ⟨ys', a, by simp⟩
|
||||
· rintro ⟨zs, a, rfl, rfl, rfl⟩
|
||||
exact ⟨zs, #[a], by simp⟩
|
||||
|
||||
theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (h : i < xs.size) → β} :
|
||||
xs.mapFinIdx f = xs.mapFinIdx g ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] h = g i xs[i] h := by
|
||||
rw [eq_comm, mapFinIdx_eq_iff]
|
||||
simp
|
||||
|
||||
@[simp] theorem mapFinIdx_mapFinIdx {xs : Array α}
|
||||
{f : (i : Nat) → α → (h : i < xs.size) → β}
|
||||
{g : (i : Nat) → β → (h : i < (xs.mapFinIdx f).size) → γ} :
|
||||
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
|
||||
simp [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {b : β} :
|
||||
xs.mapFinIdx f = replicate xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
|
||||
rcases xs with ⟨l⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapFinIdx_eq_replicate_iff]
|
||||
|
||||
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
|
||||
|
||||
@[simp] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) → α → (h : i < xs.reverse.size) → β} :
|
||||
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (xs.size - 1 - i) a (by simp; omega))).reverse := by
|
||||
rcases xs with ⟨l⟩
|
||||
simp [List.mapFinIdx_reverse]
|
||||
|
||||
/-! ### mapIdx -/
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_empty {f : Nat → α → β} : mapIdx f #[] = #[] :=
|
||||
rfl
|
||||
|
||||
@[simp] theorem mapFinIdx_eq_mapIdx {xs : Array α} {f : (i : Nat) → α → (h : i < xs.size) → β} {g : Nat → α → β}
|
||||
(h : ∀ (i : Nat) (h : i < xs.size), f i xs[i] h = g i xs[i]) :
|
||||
xs.mapFinIdx f = xs.mapIdx g := by
|
||||
simp_all [mapFinIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapFinIdx {xs : Array α} {f : Nat → α → β} :
|
||||
xs.mapIdx f = xs.mapFinIdx (fun i a _ => f i a) := by
|
||||
simp [mapFinIdx_eq_mapIdx]
|
||||
|
||||
theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
|
||||
xs.mapIdx f = xs.zipIdx.map fun ⟨a, i⟩ => f i a := by
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
|
||||
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
|
||||
|
||||
theorem mapIdx_append {xs ys : Array α} :
|
||||
(xs ++ ys).mapIdx f = xs.mapIdx f ++ ys.mapIdx (fun i => f (i + xs.size)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp [List.mapIdx_append]
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_push {xs : Array α} {a : α} :
|
||||
mapIdx f (xs.push a) = (mapIdx f xs).push (f xs.size a) := by
|
||||
simp [← append_singleton, mapIdx_append]
|
||||
|
||||
theorem mapIdx_singleton {a : α} : mapIdx f #[a] = #[f 0 a] := by
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem mapIdx_eq_empty_iff {xs : Array α} : mapIdx f xs = #[] ↔ xs = #[] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
theorem mapIdx_ne_empty_iff {xs : Array α} :
|
||||
mapIdx f xs ≠ #[] ↔ xs ≠ #[] := by
|
||||
simp
|
||||
|
||||
theorem exists_of_mem_mapIdx {b : β} {xs : Array α}
|
||||
(h : b ∈ mapIdx f xs) : ∃ (i : Nat) (h : i < xs.size), f i xs[i] = b := by
|
||||
rw [mapIdx_eq_mapFinIdx] at h
|
||||
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
|
||||
|
||||
@[simp] theorem mem_mapIdx {b : β} {xs : Array α} :
|
||||
b ∈ mapIdx f xs ↔ ∃ (i : Nat) (h : i < xs.size), f i xs[i] = b := by
|
||||
constructor
|
||||
· intro h
|
||||
exact exists_of_mem_mapIdx h
|
||||
· rintro ⟨i, h, rfl⟩
|
||||
rw [mem_iff_getElem]
|
||||
exact ⟨i, by simpa using h, by simp⟩
|
||||
|
||||
theorem mapIdx_eq_push_iff {xs : Array α} {b : β} :
|
||||
mapIdx f xs = ys.push b ↔
|
||||
∃ (a : α) (zs : Array α), xs = zs.push a ∧ mapIdx f zs = ys ∧ f zs.size a = b := by
|
||||
rw [mapIdx_eq_mapFinIdx, mapFinIdx_eq_push_iff]
|
||||
simp only [mapFinIdx_eq_mapIdx, exists_and_left, exists_prop]
|
||||
constructor
|
||||
· rintro ⟨zs, rfl, a, rfl, rfl⟩
|
||||
exact ⟨a, zs, by simp⟩
|
||||
· rintro ⟨a, zs, rfl, rfl, rfl⟩
|
||||
exact ⟨zs, rfl, a, by simp⟩
|
||||
|
||||
@[simp] theorem mapIdx_eq_singleton_iff {xs : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f xs = #[b] ↔ ∃ (a : α), xs = #[a] ∧ f 0 a = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mapIdx_eq_singleton_iff]
|
||||
|
||||
theorem mapIdx_eq_append_iff {xs : Array α} {f : Nat → α → β} {ys zs : Array β} :
|
||||
mapIdx f xs = ys ++ zs ↔
|
||||
∃ (xs' : Array α) (zs' : Array α), xs = xs' ++ zs' ∧
|
||||
xs'.mapIdx f = ys ∧
|
||||
zs'.mapIdx (fun i => f (i + xs'.size)) = zs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
rcases zs with ⟨zs⟩
|
||||
simp only [List.mapIdx_toArray, List.append_toArray, mk.injEq, List.mapIdx_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, rfl, rfl⟩
|
||||
exact ⟨l₁.toArray, l₂.toArray, by simp⟩
|
||||
· rintro ⟨⟨l₁⟩, ⟨l₂⟩, rfl, h₁, h₂⟩
|
||||
simp only [List.mapIdx_toArray, mk.injEq, List.size_toArray] at h₁ h₂
|
||||
obtain rfl := h₁
|
||||
obtain rfl := h₂
|
||||
exact ⟨l₁, l₂, by simp⟩
|
||||
|
||||
theorem mapIdx_eq_iff {xs : Array α} : mapIdx f xs = ys ↔ ∀ i : Nat, ys[i]? = xs[i]?.map (f i) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp [List.mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
|
||||
mapIdx f xs = mapIdx g xs ↔ ∀ i : Nat, (h : i < xs.size) → f i xs[i] = g i xs[i] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mapIdx_eq_mapIdx_iff]
|
||||
|
||||
@[simp] theorem mapIdx_set {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
|
||||
(xs.set i a).mapIdx f = (xs.mapIdx f).set i (f i a) (by simpa) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mapIdx_set]
|
||||
|
||||
@[simp] theorem mapIdx_setIfInBounds {xs : Array α} {i : Nat} {a : α} :
|
||||
(xs.setIfInBounds i a).mapIdx f = (xs.mapIdx f).setIfInBounds i (f i a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mapIdx_set]
|
||||
|
||||
@[simp] theorem back?_mapIdx {xs : Array α} {f : Nat → α → β} :
|
||||
(mapIdx f xs).back? = (xs.back?).map (f (xs.size - 1)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.getLast?_mapIdx]
|
||||
|
||||
@[simp] theorem back_mapIdx {xs : Array α} {f : Nat → α → β} (h) :
|
||||
(xs.mapIdx f).back h = f (xs.size - 1) (xs.back (by simpa using h)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.getLast_mapIdx]
|
||||
|
||||
@[simp] theorem mapIdx_mapIdx {xs : Array α} {f : Nat → α → β} {g : Nat → β → γ} :
|
||||
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i ∘ f i) := by
|
||||
simp [mapIdx_eq_iff]
|
||||
|
||||
theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β} :
|
||||
mapIdx f xs = replicate xs.size b ↔ ∀ (i : Nat) (h : i < xs.size), f i xs[i] = b := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rw [← toList_inj]
|
||||
simp [List.mapIdx_eq_replicate_iff]
|
||||
|
||||
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
|
||||
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
|
||||
|
||||
@[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat → α → β} :
|
||||
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.mapIdx_reverse]
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
{f : (i : Nat) → α → (h : i < l.length) → m β} :
|
||||
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
|
||||
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
|
||||
Array.mapFinIdxM.map l.toArray f i acc.size inv acc
|
||||
= toArray <$> mapFinIdxM.go l f (l.drop acc.size) acc
|
||||
(by simp [Nat.sub_add_cancel (Nat.le.intro (Nat.add_comm _ _ ▸ inv))]) := by
|
||||
match i with
|
||||
| 0 =>
|
||||
rw [Nat.zero_add] at inv
|
||||
simp only [Array.mapFinIdxM.map, inv, drop_length, mapFinIdxM.go, map_pure]
|
||||
| k + 1 =>
|
||||
conv => enter [2, 2, 3]; rw [← getElem_cons_drop (by omega)]
|
||||
simp only [Array.mapFinIdxM.map, mapFinIdxM.go, _root_.map_bind]
|
||||
congr; funext x
|
||||
conv => enter [1, 4]; rw [← Array.size_push x]
|
||||
conv => enter [2, 2, 3]; rw [← Array.size_push x]
|
||||
refine go k (acc.push x) _
|
||||
simp only [Array.mapFinIdxM, mapFinIdxM]
|
||||
exact go _ #[] _
|
||||
|
||||
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
{f : Nat → α → m β} :
|
||||
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
|
||||
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :
|
||||
mapFinIdxM.go l (fun i a h => f i a) bs acc inv = mapIdxM.go f bs acc := by
|
||||
match bs with
|
||||
| [] => simp only [mapFinIdxM.go, mapIdxM.go]
|
||||
| x :: xs => simp only [mapFinIdxM.go, mapIdxM.go, go]
|
||||
unfold Array.mapIdxM
|
||||
rw [mapFinIdxM_toArray]
|
||||
simp only [mapFinIdxM, mapIdxM]
|
||||
rw [go]
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem toList_mapFinIdxM [Monad m] [LawfulMonad m] {xs : Array α}
|
||||
{f : (i : Nat) → α → (h : i < xs.size) → m β} :
|
||||
toList <$> xs.mapFinIdxM f = xs.toList.mapFinIdxM f := by
|
||||
rw [List.mapFinIdxM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
theorem toList_mapIdxM [Monad m] [LawfulMonad m] {xs : Array α}
|
||||
{f : Nat → α → m β} :
|
||||
toList <$> xs.mapIdxM f = xs.toList.mapIdxM f := by
|
||||
rw [List.mapIdxM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
end Array
|
||||
|
||||
@@ -8,15 +8,18 @@ import Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Data.List.BasicAux
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
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)
|
||||
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp +arith)
|
||||
|
||||
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf (as.get i h) < sizeOf as := by
|
||||
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf as[i] < sizeOf as := by
|
||||
cases as with | _ as =>
|
||||
simpa using Nat.lt_trans (List.sizeOf_get _ ⟨i, h⟩) (by simp_arith)
|
||||
simpa using Nat.lt_trans (List.sizeOf_get _ ⟨i, h⟩) (by simp +arith)
|
||||
|
||||
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :
|
||||
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _ h
|
||||
@@ -29,8 +32,8 @@ macro "array_get_dec" : tactic =>
|
||||
-- subsumed by simp
|
||||
-- | with_reducible apply sizeOf_get
|
||||
-- | with_reducible apply sizeOf_getElem
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp_arith
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp_arith
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp +arith
|
||||
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp +arith
|
||||
)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
|
||||
@@ -45,7 +48,7 @@ macro "array_mem_dec" : tactic =>
|
||||
| with_reducible
|
||||
apply Nat.lt_of_lt_of_le (Array.sizeOf_lt_of_mem ?h)
|
||||
case' h => assumption
|
||||
simp_arith)
|
||||
simp +arith)
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_mem_dec)
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ import Init.Data.List.Monadic
|
||||
# Lemmas about `Array.forIn'` and `Array.forIn`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
@@ -20,76 +23,117 @@ open Nat
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] (f : α → m β) (l : Array α) :
|
||||
mapM f l = l.foldlM (fun acc a => return (acc.push (← f a))) #[] := by
|
||||
rcases l with ⟨l⟩
|
||||
simp only [List.mapM_toArray, bind_pure_comp, size_toArray, List.foldlM_toArray']
|
||||
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Array α} {f : α → β} :
|
||||
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
|
||||
induction xs; simp_all
|
||||
|
||||
@[simp] theorem mapM_id {xs : Array α} {f : α → Id β} : xs.mapM f = xs.map f :=
|
||||
mapM_pure
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α → m β} {xs ys : Array α} :
|
||||
(xs ++ ys).mapM f = (return (← xs.mapM f) ++ (← ys.mapM f)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp
|
||||
|
||||
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] {f : α → m β} {xs : Array α} :
|
||||
mapM f xs = xs.foldlM (fun acc a => return (acc.push (← f a))) #[] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.mapM_toArray, bind_pure_comp, List.size_toArray, List.foldlM_toArray']
|
||||
rw [List.mapM_eq_reverse_foldlM_cons]
|
||||
simp only [bind_pure_comp, Functor.map_map]
|
||||
suffices ∀ (k), (fun a => a.reverse.toArray) <$> List.foldlM (fun acc a => (fun a => a :: acc) <$> f a) k l =
|
||||
List.foldlM (fun acc a => acc.push <$> f a) k.reverse.toArray l by
|
||||
suffices ∀ (l), (fun l' => l'.reverse.toArray) <$> List.foldlM (fun acc a => (fun a => a :: acc) <$> f a) l xs =
|
||||
List.foldlM (fun acc a => acc.push <$> f a) l.reverse.toArray xs by
|
||||
exact this []
|
||||
intro k
|
||||
induction l generalizing k with
|
||||
intro l
|
||||
induction xs generalizing l with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
simp [ih, List.foldlM_cons]
|
||||
|
||||
/-! ### foldlM and foldrM -/
|
||||
|
||||
theorem foldlM_map [Monad m] (f : β₁ → β₂) (g : α → β₂ → m α) (l : Array β₁) (init : α) :
|
||||
(l.map f).foldlM g init = l.foldlM (fun x y => g x (f y)) init := by
|
||||
cases l
|
||||
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldlM_map [Monad m] {f : β₁ → β₂} {g : α → β₂ → m α} {xs : Array β₁} {init : α} {w : stop = xs.size} :
|
||||
(xs.map f).foldlM g init 0 stop = xs.foldlM (fun x y => g x (f y)) init 0 stop := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem foldrM_map [Monad m] [LawfulMonad m] (f : β₁ → β₂) (g : β₂ → α → m α) (l : Array β₁)
|
||||
(init : α) : (l.map f).foldrM g init = l.foldrM (fun x y => g (f x) y) init := by
|
||||
cases l
|
||||
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldrM_map [Monad m] [LawfulMonad m] {f : β₁ → β₂} {g : β₂ → α → m α} {xs : Array β₁}
|
||||
{init : α} {w : start = xs.size} :
|
||||
(xs.map f).foldrM g init start 0 = xs.foldrM (fun x y => g (f x) y) init start 0 := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldrM_map]
|
||||
|
||||
theorem foldlM_filterMap [Monad m] [LawfulMonad m] (f : α → Option β) (g : γ → β → m γ) (l : Array α) (init : γ) :
|
||||
(l.filterMap f).foldlM g init =
|
||||
l.foldlM (fun x y => match f y with | some b => g x b | none => pure x) init := by
|
||||
cases l
|
||||
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldlM_filterMap [Monad m] [LawfulMonad m] {f : α → Option β} {g : γ → β → m γ} {xs : Array α}
|
||||
{init : γ} {w : stop = (xs.filterMap f).size} :
|
||||
(xs.filterMap f).foldlM g init 0 stop =
|
||||
xs.foldlM (fun x y => match f y with | some b => g x b | none => pure x) init := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldlM_filterMap]
|
||||
rfl
|
||||
|
||||
theorem foldrM_filterMap [Monad m] [LawfulMonad m] (f : α → Option β) (g : β → γ → m γ) (l : Array α) (init : γ) :
|
||||
(l.filterMap f).foldrM g init =
|
||||
l.foldrM (fun x y => match f x with | some b => g b y | none => pure y) init := by
|
||||
cases l
|
||||
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldrM_filterMap [Monad m] [LawfulMonad m] {f : α → Option β} {g : β → γ → m γ} {xs : Array α}
|
||||
{init : γ} {w : start = (xs.filterMap f).size} :
|
||||
(xs.filterMap f).foldrM g init start 0 =
|
||||
xs.foldrM (fun x y => match f x with | some b => g b y | none => pure y) init := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldrM_filterMap]
|
||||
rfl
|
||||
|
||||
theorem foldlM_filter [Monad m] [LawfulMonad m] (p : α → Bool) (g : β → α → m β) (l : Array α) (init : β) :
|
||||
(l.filter p).foldlM g init =
|
||||
l.foldlM (fun x y => if p y then g x y else pure x) init := by
|
||||
cases l
|
||||
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldlM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : β → α → m β} {xs : Array α}
|
||||
{init : β} {w : stop = (xs.filter p).size} :
|
||||
(xs.filter p).foldlM g init 0 stop =
|
||||
xs.foldlM (fun x y => if p y then g x y else pure x) init := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldlM_filter]
|
||||
|
||||
theorem foldrM_filter [Monad m] [LawfulMonad m] (p : α → Bool) (g : α → β → m β) (l : Array α) (init : β) :
|
||||
(l.filter p).foldrM g init =
|
||||
l.foldrM (fun x y => if p x then g x y else pure y) init := by
|
||||
cases l
|
||||
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
|
||||
theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β → m β} {xs : Array α}
|
||||
{init : β} {w : start = (xs.filter p).size} :
|
||||
(xs.filter p).foldrM g init start 0 =
|
||||
xs.foldrM (fun x y => if p x then g x y else pure y) init := by
|
||||
subst w
|
||||
cases xs
|
||||
simp [List.foldrM_filter]
|
||||
|
||||
@[simp] theorem foldlM_attachWith [Monad m]
|
||||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : β → { x // q x} → m β} {b} (w : stop = xs.size):
|
||||
(xs.attachWith q H).foldlM f b 0 stop =
|
||||
xs.attach.foldlM (fun b ⟨a, h⟩ => f b ⟨a, H _ h⟩) b := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
@[simp] theorem foldrM_attachWith [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : { x // q x} → β → m β} {b}
|
||||
{w : start = xs.size} :
|
||||
(xs.attachWith q H).foldrM f b start 0 =
|
||||
xs.attach.foldrM (fun a acc => f ⟨a.1, H _ a.2⟩ acc) b := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldrM_map]
|
||||
|
||||
/-! ### forM -/
|
||||
|
||||
@[congr] theorem forM_congr [Monad m] {as bs : Array α} (w : as = bs)
|
||||
{f : α → m PUnit} :
|
||||
forM f as = forM f bs := by
|
||||
forM as f = forM bs f := by
|
||||
cases as <;> cases bs
|
||||
simp_all
|
||||
|
||||
@[simp] theorem forM_map [Monad m] [LawfulMonad m] (l : Array α) (g : α → β) (f : β → m PUnit) :
|
||||
(l.map g).forM f = l.forM (fun a => f (g a)) := by
|
||||
cases l
|
||||
@[simp] theorem forM_append [Monad m] [LawfulMonad m] {xs ys : Array α} {f : α → m PUnit} :
|
||||
forM (xs ++ ys) f = (do forM xs f; forM ys f) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem forM_map [Monad m] [LawfulMonad m] {xs : Array α} {g : α → β} {f : β → m PUnit} :
|
||||
forM (xs.map g) f = forM xs (fun a => f (g a)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
/-! ### forIn' -/
|
||||
@@ -109,44 +153,41 @@ We can express a for loop over an array as a fold,
|
||||
in which whenever we reach `.done b` we keep that value through the rest of the fold.
|
||||
-/
|
||||
theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → m (ForInStep β)) (init : β) :
|
||||
forIn' l init f = ForInStep.value <$>
|
||||
l.attach.foldlM (fun b ⟨a, m⟩ => match b with
|
||||
{xs : Array α} (f : (a : α) → a ∈ xs → β → m (ForInStep β)) (init : β) :
|
||||
forIn' xs init f = ForInStep.value <$>
|
||||
xs.attach.foldlM (fun b ⟨a, m⟩ => match b with
|
||||
| .yield b => f a m b
|
||||
| .done b => pure (.done b)) (ForInStep.yield init) := by
|
||||
cases l
|
||||
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
|
||||
simp only [List.forIn'_toArray, List.forIn'_eq_foldlM, List.attachWith_mem_toArray, size_toArray,
|
||||
List.length_map, List.length_attach, List.foldlM_toArray', List.foldlM_map]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.forIn'_eq_foldlM, List.foldlM_map]
|
||||
congr
|
||||
|
||||
/-- We can express a for loop over an array which always yields as a fold. -/
|
||||
@[simp] theorem forIn'_yield_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → m γ) (g : (a : α) → a ∈ l → β → γ → β) (init : β) :
|
||||
forIn' l init (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
|
||||
l.attach.foldlM (fun b ⟨a, m⟩ => g a m b <$> f a m b) init := by
|
||||
cases l
|
||||
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
|
||||
{xs : Array α} (f : (a : α) → a ∈ xs → β → m γ) (g : (a : α) → a ∈ xs → β → γ → β) (init : β) :
|
||||
forIn' xs init (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
|
||||
xs.attach.foldlM (fun b ⟨a, m⟩ => g a m b <$> f a m b) init := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' l init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
cases l
|
||||
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} (f : (a : α) → a ∈ xs → β → β) (init : β) :
|
||||
forIn' xs init (fun a m b => pure (.yield (f a m b))) =
|
||||
pure (f := m) (xs.attach.foldl (fun b ⟨a, h⟩ => f a h b) init) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn'_yield_eq_foldl
|
||||
(l : Array α) (f : (a : α) → a ∈ l → β → β) (init : β) :
|
||||
forIn' (m := Id) l init (fun a m b => .yield (f a m b)) =
|
||||
l.attach.foldl (fun b ⟨a, h⟩ => f a h b) init := by
|
||||
cases l
|
||||
{xs : Array α} (f : (a : α) → a ∈ xs → β → β) (init : β) :
|
||||
forIn' (m := Id) xs init (fun a m b => .yield (f a m b)) =
|
||||
xs.attach.foldl (fun b ⟨a, h⟩ => f a h b) init := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn'_map [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (g : α → β) (f : (b : β) → b ∈ l.map g → γ → m (ForInStep γ)) :
|
||||
forIn' (l.map g) init f = forIn' l init fun a h y => f (g a) (mem_map_of_mem g h) y := by
|
||||
cases l
|
||||
{xs : Array α} (g : α → β) (f : (b : β) → b ∈ xs.map g → γ → m (ForInStep γ)) :
|
||||
forIn' (xs.map g) init f = forIn' xs init fun a h y => f (g a) (mem_map_of_mem h) y := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
/--
|
||||
@@ -154,41 +195,290 @@ We can express a for loop over an array as a fold,
|
||||
in which whenever we reach `.done b` we keep that value through the rest of the fold.
|
||||
-/
|
||||
theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(f : α → β → m (ForInStep β)) (init : β) (l : Array α) :
|
||||
forIn l init f = ForInStep.value <$>
|
||||
l.foldlM (fun b a => match b with
|
||||
{xs : Array α} (f : α → β → m (ForInStep β)) (init : β) :
|
||||
forIn xs init f = ForInStep.value <$>
|
||||
xs.foldlM (fun b a => match b with
|
||||
| .yield b => f a b
|
||||
| .done b => pure (.done b)) (ForInStep.yield init) := by
|
||||
cases l
|
||||
simp only [List.forIn_toArray, List.forIn_eq_foldlM, size_toArray, List.foldlM_toArray']
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.forIn_toArray, List.forIn_eq_foldlM, List.size_toArray, List.foldlM_toArray']
|
||||
congr
|
||||
|
||||
/-- We can express a for loop over an array which always yields as a fold. -/
|
||||
@[simp] theorem forIn_yield_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : α → β → m γ) (g : α → β → γ → β) (init : β) :
|
||||
forIn l init (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
|
||||
l.foldlM (fun b a => g a b <$> f a b) init := by
|
||||
cases l
|
||||
{xs : Array α} (f : α → β → m γ) (g : α → β → γ → β) (init : β) :
|
||||
forIn xs init (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
|
||||
xs.foldlM (fun b a => g a b <$> f a b) init := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldlM_map]
|
||||
|
||||
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (f : α → β → β) (init : β) :
|
||||
forIn l init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (l.foldl (fun b a => f a b) init) := by
|
||||
cases l
|
||||
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} (f : α → β → β) (init : β) :
|
||||
forIn xs init (fun a b => pure (.yield (f a b))) =
|
||||
pure (f := m) (xs.foldl (fun b a => f a b) init) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.forIn_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn_yield_eq_foldl
|
||||
(l : Array α) (f : α → β → β) (init : β) :
|
||||
forIn (m := Id) l init (fun a b => .yield (f a b)) =
|
||||
l.foldl (fun b a => f a b) init := by
|
||||
cases l
|
||||
{xs : Array α} (f : α → β → β) (init : β) :
|
||||
forIn (m := Id) xs init (fun a b => .yield (f a b)) =
|
||||
xs.foldl (fun b a => f a b) init := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn_map [Monad m] [LawfulMonad m]
|
||||
(l : Array α) (g : α → β) (f : β → γ → m (ForInStep γ)) :
|
||||
forIn (l.map g) init f = forIn l init fun a y => f (g a) y := by
|
||||
cases l
|
||||
{xs : Array α} {g : α → β} {f : β → γ → m (ForInStep γ)} :
|
||||
forIn (xs.map g) init f = forIn xs init fun a y => f (g a) y := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
/-! ### allM and anyM -/
|
||||
|
||||
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] {p : α → Bool} {xs : Array α} :
|
||||
xs.anyM (m := m) (pure <| p ·) = pure (xs.any p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] {p : α → Bool} {xs : Array α} :
|
||||
xs.allM (m := m) (pure <| p ·) = pure (xs.all p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/-! ### findM? and findSomeM? -/
|
||||
|
||||
@[simp]
|
||||
theorem findM?_pure {m} [Monad m] [LawfulMonad m] {p : α → Bool} {xs : Array α} :
|
||||
findM? (m := m) (pure <| p ·) xs = pure (xs.find? p) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem findSomeM?_pure [Monad m] [LawfulMonad m] {f : α → Option β} {xs : Array α} :
|
||||
findSomeM? (m := m) (pure <| f ·) xs = pure (xs.findSome? f) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
l.toArray.filterM p = toArray <$> l.filterM p := by
|
||||
simp only [Array.filterM, filterM, foldlM_toArray, bind_pure_comp, Functor.map_map]
|
||||
conv => lhs; rw [← reverse_nil]
|
||||
generalize [] = acc
|
||||
induction l generalizing acc with simp
|
||||
| cons x xs ih =>
|
||||
congr; funext b
|
||||
cases b
|
||||
· simp only [Bool.false_eq_true, ↓reduceIte, pure_bind, cond_false]
|
||||
exact ih acc
|
||||
· simp only [↓reduceIte, ← reverse_cons, pure_bind, cond_true]
|
||||
exact ih (x :: acc)
|
||||
|
||||
/-- Variant of `filterM_toArray` with a side condition for the stop position. -/
|
||||
@[simp] theorem filterM_toArray' [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} (w : stop = l.length) :
|
||||
l.toArray.filterM p 0 stop = toArray <$> l.filterM p := by
|
||||
subst w
|
||||
rw [filterM_toArray]
|
||||
|
||||
theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
|
||||
simp [Array.filterRevM, filterRevM]
|
||||
rw [← foldlM_reverse, ← foldlM_toArray, ← Array.filterM, filterM_toArray]
|
||||
simp only [filterM, bind_pure_comp, Functor.map_map, reverse_toArray, reverse_reverse]
|
||||
|
||||
/-- Variant of `filterRevM_toArray` with a side condition for the start position. -/
|
||||
@[simp] theorem filterRevM_toArray' [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} (w : start = l.length) :
|
||||
l.toArray.filterRevM p start 0 = toArray <$> l.filterRevM p := by
|
||||
subst w
|
||||
rw [filterRevM_toArray]
|
||||
|
||||
theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Option β)} :
|
||||
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
|
||||
simp [Array.filterMapM, filterMapM]
|
||||
conv => lhs; rw [← reverse_nil]
|
||||
generalize [] = acc
|
||||
induction l generalizing acc with simp [filterMapM.loop]
|
||||
| cons x xs ih =>
|
||||
congr; funext o
|
||||
cases o
|
||||
· simp only [pure_bind]; exact ih acc
|
||||
· simp only [pure_bind]; rw [← List.reverse_cons]; exact ih _
|
||||
|
||||
/-- Variant of `filterMapM_toArray` with a side condition for the stop position. -/
|
||||
@[simp] theorem filterMapM_toArray' [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Option β)} (w : stop = l.length) :
|
||||
l.toArray.filterMapM f 0 stop = toArray <$> l.filterMapM f := by
|
||||
subst w
|
||||
rw [filterMapM_toArray]
|
||||
|
||||
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Array β)} :
|
||||
l.toArray.flatMapM f = toArray <$> l.flatMapM (fun a => Array.toList <$> f a) := by
|
||||
simp only [Array.flatMapM, bind_pure_comp, foldlM_toArray, flatMapM]
|
||||
conv => lhs; arg 2; change [].reverse.flatten.toArray
|
||||
generalize [] = acc
|
||||
induction l generalizing acc with
|
||||
| nil => simp only [foldlM_nil, flatMapM.loop, map_pure]
|
||||
| cons x xs ih =>
|
||||
simp only [foldlM_cons, bind_map_left, flatMapM.loop, _root_.map_bind]
|
||||
congr; funext xs
|
||||
conv => lhs; rw [Array.toArray_append, ← flatten_concat, ← reverse_cons]
|
||||
exact ih _
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
@[congr] theorem filterM_congr [Monad m] {as bs : Array α} (w : as = bs)
|
||||
{p : α → m Bool} {q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
as.filterM p = bs.filterM q := by
|
||||
subst w
|
||||
simp [filterM, h]
|
||||
|
||||
@[congr] theorem filterRevM_congr [Monad m] {as bs : Array α} (w : as = bs)
|
||||
{p : α → m Bool} {q : α → m Bool} (h : ∀ a, p a = q a) :
|
||||
as.filterRevM p = bs.filterRevM q := by
|
||||
subst w
|
||||
simp [filterRevM, h]
|
||||
|
||||
@[congr] theorem filterMapM_congr [Monad m] {as bs : Array α} (w : as = bs)
|
||||
{f : α → m (Option β)} {g : α → m (Option β)} (h : ∀ a, f a = g a) :
|
||||
as.filterMapM f = bs.filterMapM g := by
|
||||
subst w
|
||||
simp [filterMapM, h]
|
||||
|
||||
@[congr] theorem flatMapM_congr [Monad m] {as bs : Array α} (w : as = bs)
|
||||
{f : α → m (Array β)} {g : α → m (Array β)} (h : ∀ a, f a = g a) :
|
||||
as.flatMapM f = bs.flatMapM g := by
|
||||
subst w
|
||||
simp [flatMapM, h]
|
||||
|
||||
theorem toList_filterM [Monad m] [LawfulMonad m] {xs : Array α} {p : α → m Bool} :
|
||||
toList <$> xs.filterM p = xs.toList.filterM p := by
|
||||
rw [List.filterM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
theorem toList_filterRevM [Monad m] [LawfulMonad m] {xs : Array α} {p : α → m Bool} :
|
||||
toList <$> xs.filterRevM p = xs.toList.filterRevM p := by
|
||||
rw [List.filterRevM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
theorem toList_filterMapM [Monad m] [LawfulMonad m] {xs : Array α} {f : α → m (Option β)} :
|
||||
toList <$> xs.filterMapM f = xs.toList.filterMapM f := by
|
||||
rw [List.filterMapM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
theorem toList_flatMapM [Monad m] [LawfulMonad m] {xs : Array α} {f : α → m (Array β)} :
|
||||
toList <$> xs.flatMapM f = xs.toList.flatMapM (fun a => toList <$> f a) := by
|
||||
rw [List.flatMapM_toArray]
|
||||
simp only [Functor.map_map, id_map']
|
||||
|
||||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||||
|
||||
/--
|
||||
This lemma identifies monadic folds over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldlM_subtype [Monad m] {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : β → { x // p x } → m β} {g : β → α → m β} {x : β}
|
||||
(hf : ∀ b x h, f b ⟨x, h⟩ = g b x) (w : stop = xs.size) :
|
||||
xs.foldlM f x 0 stop = xs.unattach.foldlM g x 0 stop := by
|
||||
subst w
|
||||
rcases xs with ⟨l⟩
|
||||
simp
|
||||
rw [List.foldlM_subtype hf]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_wfParam [Monad m] {xs : Array α} {f : β → α → m β} {init : β} :
|
||||
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldlM_unattach [Monad m] {P : α → Prop} {xs : Array (Subtype P)} {f : β → α → m β} {init : β} :
|
||||
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b ⟨x, h⟩ =>
|
||||
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
|
||||
f b (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
/--
|
||||
This lemma identifies monadic folds over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem foldrM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → β → m β} {g : α → β → m β} {x : β}
|
||||
(hf : ∀ x h b, f ⟨x, h⟩ b = g x b) (w : start = xs.size) :
|
||||
xs.foldrM f x start 0 = xs.unattach.foldrM g x start 0:= by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
rw [List.foldrM_subtype hf]
|
||||
|
||||
|
||||
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] {xs : Array α} {f : α → β → m β} {init : β} :
|
||||
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] {P : α → Prop} {xs : Array (Subtype P)} {f : α → β → m β} {init : β} :
|
||||
xs.unattach.foldrM f init = xs.foldrM (init := init) fun ⟨x, h⟩ b =>
|
||||
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
|
||||
f (wfParam x) b := by
|
||||
simp [wfParam]
|
||||
|
||||
/--
|
||||
This lemma identifies monadic maps over lists of subtypes, where the function only depends on the value, not the proposition,
|
||||
and simplifies these to the function directly taking the value.
|
||||
-/
|
||||
@[simp] theorem mapM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → m β} {g : α → m β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
xs.mapM f = xs.unattach.mapM g := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
rw [List.mapM_subtype hf]
|
||||
|
||||
@[wf_preprocess] theorem mapM_wfParam [Monad m] [LawfulMonad m] {xs : Array α} {f : α → m β} :
|
||||
(wfParam xs).mapM f = xs.attach.unattach.mapM f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem mapM_unattach [Monad m] [LawfulMonad m] {P : α → Prop} {xs : Array (Subtype P)} {f : α → m β} :
|
||||
xs.unattach.mapM f = xs.mapM fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
@[simp] theorem filterMapM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → m (Option β)} {g : α → m (Option β)} (hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||||
xs.filterMapM f 0 stop = xs.unattach.filterMapM g := by
|
||||
subst w
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
rw [List.filterMapM_subtype hf]
|
||||
|
||||
|
||||
@[wf_preprocess] theorem filterMapM_wfParam [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} {f : α → m (Option β)} :
|
||||
(wfParam xs).filterMapM f = xs.attach.unattach.filterMapM f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem filterMapM_unattach [Monad m] [LawfulMonad m]
|
||||
{P : α → Prop} {xs : Array (Subtype P)} {f : α → m (Option β)} :
|
||||
xs.unattach.filterMapM f = xs.filterMapM fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
@[simp] theorem flatMapM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {xs : Array { x // p x }}
|
||||
{f : { x // p x } → m (Array β)} {g : α → m (Array β)} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||||
(xs.flatMapM f) = xs.unattach.flatMapM g := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
rw [List.flatMapM_subtype]
|
||||
simp [hf]
|
||||
|
||||
@[wf_preprocess] theorem flatMapM_wfParam [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} {f : α → m (Array β)} :
|
||||
(wfParam xs).flatMapM f = xs.attach.unattach.flatMapM f := by
|
||||
simp [wfParam]
|
||||
|
||||
@[wf_preprocess] theorem flatMapM_unattach [Monad m] [LawfulMonad m]
|
||||
{P : α → Prop} {xs : Array (Subtype P)} {f : α → m (Array β)} :
|
||||
xs.unattach.flatMapM f = xs.flatMapM fun ⟨x, h⟩ =>
|
||||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||||
simp [wfParam]
|
||||
|
||||
end Array
|
||||
|
||||
53
src/Init/Data/Array/OfFn.lean
Normal file
53
src/Init/Data/Array/OfFn.lean
Normal file
@@ -0,0 +1,53 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.OfFn
|
||||
|
||||
/-!
|
||||
# Theorems about `Array.ofFn`
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
@[simp] theorem ofFn_zero {f : Fin 0 → α} : ofFn f = #[] := by
|
||||
simp [ofFn, ofFn.go]
|
||||
|
||||
theorem ofFn_succ {f : Fin (n+1) → α} :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split <;> rename_i h₃
|
||||
· rfl
|
||||
· congr
|
||||
simp at h₁ h₂
|
||||
omega
|
||||
|
||||
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n → α} : (List.ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem toList_ofFn {f : Fin n → α} : (Array.ofFn f).toList = List.ofFn f := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
|
||||
rw [← Array.toList_inj]
|
||||
simp
|
||||
|
||||
@[simp 500]
|
||||
theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i = a := by
|
||||
constructor
|
||||
· intro w
|
||||
obtain ⟨i, h, rfl⟩ := getElem_of_mem w
|
||||
exact ⟨⟨i, by simpa using h⟩, by simp⟩
|
||||
· rintro ⟨i, rfl⟩
|
||||
apply mem_of_getElem (i := i) <;> simp
|
||||
|
||||
end Array
|
||||
@@ -7,6 +7,9 @@ prelude
|
||||
import Init.Data.List.Nat.Perm
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open List
|
||||
@@ -27,38 +30,38 @@ theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs ↔ as.toList ~ bs.toL
|
||||
@[simp] theorem perm_toArray (as bs : List α) : as.toArray ~ bs.toArray ↔ as ~ bs := by
|
||||
simp [perm_iff_toList_perm]
|
||||
|
||||
@[simp, refl] protected theorem Perm.refl (l : Array α) : l ~ l := by
|
||||
cases l
|
||||
@[simp, refl] protected theorem Perm.refl (xs : Array α) : xs ~ xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
protected theorem Perm.rfl {l : List α} : l ~ l := .refl _
|
||||
protected theorem Perm.rfl {xs : List α} : xs ~ xs := .refl _
|
||||
|
||||
theorem Perm.of_eq {l₁ l₂ : Array α} (h : l₁ = l₂) : l₁ ~ l₂ := h ▸ .rfl
|
||||
theorem Perm.of_eq {xs ys : Array α} (h : xs = ys) : xs ~ ys := h ▸ .rfl
|
||||
|
||||
protected theorem Perm.symm {l₁ l₂ : Array α} (h : l₁ ~ l₂) : l₂ ~ l₁ := by
|
||||
cases l₁; cases l₂
|
||||
protected theorem Perm.symm {xs ys : Array α} (h : xs ~ ys) : ys ~ xs := by
|
||||
cases xs; cases ys
|
||||
simp only [perm_toArray] at h
|
||||
simpa using h.symm
|
||||
|
||||
protected theorem Perm.trans {l₁ l₂ l₃ : Array α} (h₁ : l₁ ~ l₂) (h₂ : l₂ ~ l₃) : l₁ ~ l₃ := by
|
||||
cases l₁; cases l₂; cases l₃
|
||||
protected theorem Perm.trans {xs ys zs : Array α} (h₁ : xs ~ ys) (h₂ : ys ~ zs) : xs ~ zs := by
|
||||
cases xs; cases ys; cases zs
|
||||
simp only [perm_toArray] at h₁ h₂
|
||||
simpa using h₁.trans h₂
|
||||
|
||||
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
|
||||
trans h₁ h₂ := Perm.trans h₁ h₂
|
||||
|
||||
theorem perm_comm {l₁ l₂ : Array α} : l₁ ~ l₂ ↔ l₂ ~ l₁ := ⟨Perm.symm, Perm.symm⟩
|
||||
theorem perm_comm {xs ys : Array α} : xs ~ ys ↔ ys ~ xs := ⟨Perm.symm, Perm.symm⟩
|
||||
|
||||
theorem Perm.push (x y : α) {l₁ l₂ : Array α} (p : l₁ ~ l₂) :
|
||||
(l₁.push x).push y ~ (l₂.push y).push x := by
|
||||
cases l₁; cases l₂
|
||||
theorem Perm.push (x y : α) {xs ys : Array α} (p : xs ~ ys) :
|
||||
(xs.push x).push y ~ (ys.push y).push x := by
|
||||
cases xs; cases ys
|
||||
simp only [perm_toArray] at p
|
||||
simp only [push_toArray, List.append_assoc, singleton_append, perm_toArray]
|
||||
exact p.append (Perm.swap' _ _ Perm.nil)
|
||||
|
||||
theorem swap_perm {as : Array α} {i j : Nat} (h₁ : i < as.size) (h₂ : j < as.size) :
|
||||
as.swap i j ~ as := by
|
||||
theorem swap_perm {xs : Array α} {i j : Nat} (h₁ : i < xs.size) (h₂ : j < xs.size) :
|
||||
xs.swap i j ~ xs := by
|
||||
simp only [swap, perm_iff_toList_perm, toList_set]
|
||||
apply set_set_perm
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ prelude
|
||||
import Init.Data.Vector.Basic
|
||||
import Init.Data.Ord
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
-- We do not enable `linter.indexVariables` because it is helpful to name index variables `lo`, `mid`, `hi`, etc.
|
||||
|
||||
namespace Array
|
||||
|
||||
private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi : Nat)
|
||||
@@ -27,6 +30,16 @@ private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi :
|
||||
(⟨i, ilo⟩, as.swap i hi)
|
||||
loop as lo lo
|
||||
|
||||
/--
|
||||
Sorts an array using the Quicksort algorithm.
|
||||
|
||||
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
|
||||
decidable to be used for sorting. Use `Array.qsortOrd` to sort the array according to the `Ord α`
|
||||
instance.
|
||||
|
||||
The optional parameters `low` and `high` delimit the region of the array that is sorted. Both are
|
||||
inclusive, and default to sorting the entire array.
|
||||
-/
|
||||
@[inline] def qsort (as : Array α) (lt : α → α → Bool := by exact (· < ·))
|
||||
(low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
|
||||
@@ -47,7 +60,7 @@ private def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi :
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
Sort an array using `compare` to compare elements.
|
||||
Sorts an array using the Quicksort algorithm, using `Ord.compare` to compare elements.
|
||||
-/
|
||||
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
|
||||
xs.qsort fun x y => compare x y |>.isLT
|
||||
|
||||
303
src/Init/Data/Array/Range.lean
Normal file
303
src/Init/Data/Array/Range.lean
Normal file
@@ -0,0 +1,303 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.Zip
|
||||
import Init.Data.List.Nat.Range
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.range'`, `Array.range`, and `Array.zipIdx`
|
||||
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
theorem range'_succ {s n step} : range' s (n + 1) step = #[s] ++ range' (s + step) n step := by
|
||||
rw [← toList_inj]
|
||||
simp [List.range'_succ]
|
||||
|
||||
@[simp] theorem range'_eq_empty_iff : range' s n step = #[] ↔ n = 0 := by
|
||||
rw [← size_eq_zero_iff, size_range']
|
||||
|
||||
theorem range'_ne_empty_iff : range' s n step ≠ #[] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
@[simp] theorem range'_zero : range' s 0 step = #[] := by
|
||||
simp
|
||||
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by
|
||||
simp [range', ofFn, ofFn.go]
|
||||
|
||||
@[simp] theorem range'_inj : range' s n = range' s' n' ↔ n = n' ∧ (n = 0 ∨ s = s') := by
|
||||
rw [← toList_inj]
|
||||
simp [List.range'_inj]
|
||||
|
||||
theorem mem_range' {n} : m ∈ range' s n step ↔ ∃ i < n, m = s + step * i := by
|
||||
simp [range']
|
||||
constructor
|
||||
· rintro ⟨⟨i, w⟩, h, h'⟩
|
||||
exact ⟨i, w, by simp_all⟩
|
||||
· rintro ⟨i, w, h'⟩
|
||||
exact ⟨⟨i, w⟩, by simp_all⟩
|
||||
|
||||
theorem pop_range' : (range' s n step).pop = range' s (n - 1) step := by
|
||||
ext <;> simp
|
||||
|
||||
theorem map_add_range' {a} (s n step) : map (a + ·) (range' s n step) = range' (a + s) n step := by
|
||||
ext <;> simp <;> omega
|
||||
|
||||
theorem range'_succ_left : range' (s + 1) n step = (range' s n step).map (· + 1) := by
|
||||
ext <;> simp <;> omega
|
||||
|
||||
theorem range'_append {s m n step : Nat} :
|
||||
range' s m step ++ range' (s + step * m) n step = range' s (m + n) step := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp only [size_append, size_range'] at h₁ h₂
|
||||
simp only [getElem_append, size_range', getElem_range', Nat.mul_sub_left_distrib, dite_eq_ite,
|
||||
ite_eq_left_iff, Nat.not_lt]
|
||||
intro h
|
||||
have : step * m ≤ step * i := by exact mul_le_mul_left step h
|
||||
omega
|
||||
|
||||
@[simp] theorem range'_append_1 {s m n : Nat} :
|
||||
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append (step := 1)
|
||||
|
||||
theorem range'_concat {s n : Nat} : range' s (n + 1) step = range' s n step ++ #[s + step * n] := by
|
||||
simpa using range'_append.symm
|
||||
|
||||
theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ #[s + n] := by
|
||||
simp [range'_concat]
|
||||
|
||||
@[simp] theorem mem_range'_1 : m ∈ range' s n ↔ s ≤ m ∧ m < s + n := by
|
||||
simp [mem_range']; exact ⟨
|
||||
fun ⟨i, h, e⟩ => e ▸ ⟨Nat.le_add_right .., Nat.add_lt_add_left h _⟩,
|
||||
fun ⟨h₁, h₂⟩ => ⟨m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm⟩⟩
|
||||
|
||||
theorem map_sub_range' {a s : Nat} (h : a ≤ s) (n : Nat) :
|
||||
map (· - a) (range' s n step) = range' (s - a) n step := by
|
||||
conv => lhs; rw [← Nat.add_sub_cancel' h]
|
||||
rw [← map_add_range', map_map, (?_ : _∘_ = _), map_id]
|
||||
funext x; apply Nat.add_sub_cancel_left
|
||||
|
||||
@[simp] theorem range'_eq_singleton_iff {s n a : Nat} : range' s n = #[a] ↔ s = a ∧ n = 1 := by
|
||||
rw [← toList_inj]
|
||||
simp
|
||||
|
||||
theorem range'_eq_append_iff : range' s n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs = range' s k ∧ ys = range' (s + k) (n - k) := by
|
||||
simp [← toList_inj, List.range'_eq_append_iff]
|
||||
|
||||
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat → Bool} :
|
||||
(range' s n).find? p = some i ↔ p i ∧ i ∈ range' s n ∧ ∀ j, s ≤ j → j < i → !p j := by
|
||||
rw [← List.toArray_range']
|
||||
simp only [List.find?_toArray, mem_toArray]
|
||||
simp [List.find?_range'_eq_some]
|
||||
|
||||
@[simp] theorem find?_range'_eq_none {s n : Nat} {p : Nat → Bool} :
|
||||
(range' s n).find? p = none ↔ ∀ i, s ≤ i → i < s + n → !p i := by
|
||||
rw [← List.toArray_range']
|
||||
simp only [List.find?_toArray]
|
||||
simp
|
||||
|
||||
theorem erase_range' :
|
||||
(range' s n).erase i =
|
||||
range' s (min n (i - s)) ++ range' (max s (i + 1)) (min s (i + 1) + n - (i + 1)) := by
|
||||
simp only [← List.toArray_range', List.erase_toArray]
|
||||
simp [List.erase_range']
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
theorem range_eq_range' {n : Nat} : range n = range' 0 n := by
|
||||
simp [range, range']
|
||||
|
||||
theorem range_succ_eq_map {n : Nat} : range (n + 1) = #[0] ++ map succ (range n) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
omega
|
||||
· simp only [getElem_range, getElem_append, List.size_toArray, List.length_cons, List.length_nil,
|
||||
Nat.zero_add, lt_one_iff, List.getElem_toArray, List.getElem_singleton, getElem_map,
|
||||
succ_eq_add_one, dite_eq_ite]
|
||||
split <;> omega
|
||||
|
||||
theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) := by
|
||||
rw [range_eq_range', map_add_range']; rfl
|
||||
|
||||
@[simp] theorem range_eq_empty_iff {n : Nat} : range n = #[] ↔ n = 0 := by
|
||||
rw [← size_eq_zero_iff, size_range]
|
||||
|
||||
theorem range_ne_empty_iff {n : Nat} : range n ≠ #[] ↔ n ≠ 0 := by
|
||||
cases n <;> simp
|
||||
|
||||
theorem range_succ {n : Nat} : range (succ n) = range n ++ #[n] := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp only [succ_eq_add_one, size_range] at h₁
|
||||
simp only [succ_eq_add_one, getElem_range, append_singleton, getElem_push, size_range,
|
||||
dite_eq_ite]
|
||||
split <;> omega
|
||||
|
||||
theorem range_add {n m : Nat} : range (n + m) = range n ++ (range m).map (n + ·) := by
|
||||
rw [← range'_eq_map_range]
|
||||
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 (s := 0)).symm
|
||||
|
||||
theorem reverse_range' {s n : Nat} : reverse (range' s n) = map (s + n - 1 - ·) (range n) := by
|
||||
simp [← toList_inj, List.reverse_range']
|
||||
|
||||
@[simp]
|
||||
theorem mem_range {m n : Nat} : m ∈ range n ↔ m < n := by
|
||||
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
|
||||
|
||||
theorem not_mem_range_self {n : Nat} : n ∉ range n := by simp
|
||||
|
||||
theorem self_mem_range_succ {n : Nat} : n ∈ range (n + 1) := by simp
|
||||
|
||||
@[simp] theorem take_range {i n : Nat} : take (range n) i = range (min i n) := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat → Bool} :
|
||||
(range n).find? p = some i ↔ p i ∧ i ∈ range n ∧ ∀ j, j < i → !p j := by
|
||||
simp [range_eq_range']
|
||||
|
||||
@[simp] theorem find?_range_eq_none {n : Nat} {p : Nat → Bool} :
|
||||
(range n).find? p = none ↔ ∀ i, i < n → !p i := by
|
||||
simp only [← List.toArray_range, List.find?_toArray, List.find?_range_eq_none]
|
||||
|
||||
theorem erase_range : (range n).erase i = range (min n i) ++ range' (i + 1) (n - (i + 1)) := by
|
||||
simp [range_eq_range', erase_range']
|
||||
|
||||
|
||||
/-! ### zipIdx -/
|
||||
|
||||
@[simp]
|
||||
theorem zipIdx_eq_empty_iff {xs : Array α} {i : Nat} : xs.zipIdx i = #[] ↔ xs = #[] := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_zipIdx {xs : Array α} {i j} : (zipIdx xs i)[j]? = xs[j]?.map fun a => (a, i + j) := by
|
||||
simp [getElem?_def]
|
||||
|
||||
theorem map_snd_add_zipIdx_eq_zipIdx {xs : Array α} {n k : Nat} :
|
||||
map (Prod.map id (· + n)) (zipIdx xs k) = zipIdx xs (n + k) :=
|
||||
ext_getElem? fun i ↦ by simp [(· ∘ ·), Nat.add_comm, Nat.add_left_comm]; rfl
|
||||
|
||||
-- Arguments are explicit for parity with `zipIdx_map_fst`.
|
||||
@[simp]
|
||||
theorem zipIdx_map_snd (i) (xs : Array α) : map Prod.snd (zipIdx xs i) = range' i xs.size := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
-- Arguments are explicit so we can rewrite from right to left.
|
||||
@[simp]
|
||||
theorem zipIdx_map_fst (i) (xs : Array α) : map Prod.fst (zipIdx xs i) = xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
theorem zipIdx_eq_zip_range' {xs : Array α} {i : Nat} : xs.zipIdx i = xs.zip (range' i xs.size) := by
|
||||
simp [zip_of_prod (zipIdx_map_fst _ _) (zipIdx_map_snd _ _)]
|
||||
|
||||
@[simp]
|
||||
theorem unzip_zipIdx_eq_prod {xs : Array α} {i : Nat} :
|
||||
(xs.zipIdx i).unzip = (xs, range' i xs.size) := by
|
||||
simp only [zipIdx_eq_zip_range', unzip_zip, size_range']
|
||||
|
||||
/-- Replace `zipIdx` with a starting index `n+1` with `zipIdx` starting from `n`,
|
||||
followed by a `map` increasing the indices by one. -/
|
||||
theorem zipIdx_succ {xs : Array α} {i : Nat} :
|
||||
xs.zipIdx (i + 1) = (xs.zipIdx i).map (fun ⟨a, j⟩ => (a, j + 1)) := by
|
||||
cases xs
|
||||
simp [List.zipIdx_succ]
|
||||
|
||||
/-- Replace `zipIdx` with a starting index with `zipIdx` starting from 0,
|
||||
followed by a `map` increasing the indices. -/
|
||||
theorem zipIdx_eq_map_add {xs : Array α} {i : Nat} :
|
||||
xs.zipIdx i = (xs.zipIdx 0).map (fun ⟨a, j⟩ => (a, i + j)) := by
|
||||
cases xs
|
||||
simp only [zipIdx_toArray, List.map_toArray, mk.injEq]
|
||||
rw [List.zipIdx_eq_map_add]
|
||||
|
||||
@[simp]
|
||||
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx #[x] k = #[(x, k)] :=
|
||||
rfl
|
||||
|
||||
theorem mk_add_mem_zipIdx_iff_getElem? {k i : Nat} {x : α} {xs : Array α} :
|
||||
(x, k + i) ∈ zipIdx xs k ↔ xs[i]? = some x := by
|
||||
simp [mem_iff_getElem?, and_left_comm]
|
||||
|
||||
theorem le_snd_of_mem_zipIdx {x : α × Nat} {k : Nat} {xs : Array α} (h : x ∈ zipIdx xs k) :
|
||||
k ≤ x.2 :=
|
||||
(mk_mem_zipIdx_iff_le_and_getElem?_sub.1 h).1
|
||||
|
||||
theorem snd_lt_add_of_mem_zipIdx {x : α × Nat} {k : Nat} {xs : Array α} (h : x ∈ zipIdx xs k) :
|
||||
x.2 < k + xs.size := by
|
||||
rcases mem_iff_getElem.1 h with ⟨i, h', rfl⟩
|
||||
simpa using h'
|
||||
|
||||
theorem snd_lt_of_mem_zipIdx {x : α × Nat} {k : Nat} {xs : Array α} (h : x ∈ zipIdx xs k) : x.2 < xs.size + k := by
|
||||
simpa [Nat.add_comm] using snd_lt_add_of_mem_zipIdx h
|
||||
|
||||
theorem map_zipIdx {f : α → β} {xs : Array α} {k : Nat} :
|
||||
map (Prod.map f id) (zipIdx xs k) = zipIdx (xs.map f) k := by
|
||||
cases xs
|
||||
simp [List.map_zipIdx]
|
||||
|
||||
theorem fst_mem_of_mem_zipIdx {x : α × Nat} {xs : Array α} {k : Nat} (h : x ∈ zipIdx xs k) : x.1 ∈ xs :=
|
||||
zipIdx_map_fst k xs ▸ mem_map_of_mem h
|
||||
|
||||
theorem fst_eq_of_mem_zipIdx {x : α × Nat} {xs : Array α} {k : Nat} (h : x ∈ zipIdx xs k) :
|
||||
x.1 = xs[x.2 - k]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) := by
|
||||
cases xs
|
||||
exact List.fst_eq_of_mem_zipIdx (by simpa using h)
|
||||
|
||||
theorem mem_zipIdx {x : α} {i : Nat} {xs : Array α} {k : Nat} (h : (x, i) ∈ xs.zipIdx k) :
|
||||
k ≤ i ∧ i < k + xs.size ∧
|
||||
x = xs[i - k]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) :=
|
||||
⟨le_snd_of_mem_zipIdx h, snd_lt_add_of_mem_zipIdx h, fst_eq_of_mem_zipIdx h⟩
|
||||
|
||||
/-- Variant of `mem_zipIdx` specialized at `k = 0`. -/
|
||||
theorem mem_zipIdx' {x : α} {i : Nat} {xs : Array α} (h : (x, i) ∈ xs.zipIdx) :
|
||||
i < xs.size ∧ x = xs[i]'(by have := le_snd_of_mem_zipIdx h; have := snd_lt_add_of_mem_zipIdx h; omega) :=
|
||||
⟨by simpa using snd_lt_add_of_mem_zipIdx h, fst_eq_of_mem_zipIdx h⟩
|
||||
|
||||
theorem zipIdx_map {xs : Array α} {k : Nat} {f : α → β} :
|
||||
zipIdx (xs.map f) k = (zipIdx xs k).map (Prod.map f id) := by
|
||||
cases xs
|
||||
simp [List.zipIdx_map]
|
||||
|
||||
theorem zipIdx_append {xs ys : Array α} {k : Nat} :
|
||||
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + xs.size) := by
|
||||
cases xs
|
||||
cases ys
|
||||
simp [List.zipIdx_append]
|
||||
|
||||
theorem zipIdx_eq_append_iff {xs : Array α} {k : Nat} :
|
||||
zipIdx xs k = ys ++ zs ↔
|
||||
∃ ys' zs', xs = ys' ++ zs' ∧ ys = zipIdx ys' k ∧ zs = zipIdx zs' (k + ys'.size) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
rcases ys with ⟨ys⟩
|
||||
rcases zs with ⟨zs⟩
|
||||
simp only [zipIdx_toArray, List.append_toArray, mk.injEq, List.zipIdx_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨l₁', l₂', rfl, rfl, rfl⟩
|
||||
exact ⟨⟨l₁'⟩, ⟨l₂'⟩, by simp⟩
|
||||
· rintro ⟨⟨l₁'⟩, ⟨l₂'⟩, rfl, h⟩
|
||||
simp only [zipIdx_toArray, mk.injEq, List.size_toArray] at h
|
||||
obtain ⟨rfl, rfl⟩ := h
|
||||
exact ⟨l₁', l₂', by simp⟩
|
||||
|
||||
end Array
|
||||
@@ -6,27 +6,40 @@ Authors: Leonardo de Moura, Mario Carneiro
|
||||
prelude
|
||||
import Init.Tactics
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
|
||||
/--
|
||||
Set an element in an array, using a proof that the index is in bounds.
|
||||
(This proof can usually be omitted, and will be synthesized automatically.)
|
||||
Replaces the element at a given index in an array.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
No bounds check is performed, but the function requires a proof that the index is in bounds. This
|
||||
proof can usually be omitted, and will be synthesized automatically.
|
||||
|
||||
The array is modified in-place if there are no other references to it.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 2].set 1 5 = #[0, 5, 2]`
|
||||
* `#["orange", "apple"].set 1 "grape" = #["orange", "grape"]`
|
||||
-/
|
||||
@[extern "lean_array_fset"]
|
||||
def Array.set (a : Array α) (i : @& Nat) (v : α) (h : i < a.size := by get_elem_tactic) :
|
||||
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) :
|
||||
Array α where
|
||||
toList := a.toList.set i v
|
||||
toList := xs.toList.set i v
|
||||
|
||||
/--
|
||||
Set an element in an array, or do nothing if the index is out of bounds.
|
||||
Replaces the element at the provided index in an array. The array is returned unmodified if the
|
||||
index is out of bounds.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
The array is modified in-place if there are no other references to it.
|
||||
|
||||
Examples:
|
||||
* `#[0, 1, 2].setIfInBounds 1 5 = #[0, 5, 2]`
|
||||
* `#["orange", "apple"].setIfInBounds 1 "grape" = #["orange", "grape"]`
|
||||
* `#["orange", "apple"].setIfInBounds 5 "grape" = #["orange", "apple"]`
|
||||
-/
|
||||
@[inline] def Array.setIfInBounds (a : Array α) (i : Nat) (v : α) : Array α :=
|
||||
dite (LT.lt i a.size) (fun h => a.set i v h) (fun _ => a)
|
||||
@[inline] def Array.setIfInBounds (xs : Array α) (i : Nat) (v : α) : Array α :=
|
||||
dite (LT.lt i xs.size) (fun h => xs.set i v h) (fun _ => xs)
|
||||
|
||||
@[deprecated Array.setIfInBounds (since := "2024-11-24")] abbrev Array.setD := @Array.setIfInBounds
|
||||
|
||||
@@ -37,5 +50,5 @@ This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_set"]
|
||||
def Array.set! (a : Array α) (i : @& Nat) (v : α) : Array α :=
|
||||
Array.setIfInBounds a i v
|
||||
def Array.set! (xs : Array α) (i : @& Nat) (v : α) : Array α :=
|
||||
Array.setIfInBounds xs i v
|
||||
|
||||
@@ -6,17 +6,45 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
set_option linter.missingDocs true
|
||||
|
||||
universe u v w
|
||||
|
||||
structure Subarray (α : Type u) where
|
||||
/--
|
||||
A region of some underlying array.
|
||||
|
||||
A subarray contains an array together with the start and end indices of a region of interest.
|
||||
Subarrays can be used to avoid copying or allocating space, while being more convenient than
|
||||
tracking the bounds by hand. The region of interest consists of every index that is both greater
|
||||
than or equal to `start` and strictly less than `stop`.
|
||||
-/
|
||||
structure Subarray (α : Type u) where
|
||||
/-- The underlying array. -/
|
||||
array : Array α
|
||||
/-- The starting index of the region of interest (inclusive). -/
|
||||
start : Nat
|
||||
/-- The ending index of the region of interest (exclusive). -/
|
||||
stop : Nat
|
||||
/--
|
||||
The starting index is no later than the ending index.
|
||||
|
||||
The ending index is exclusive. If the starting and ending indices are equal, then the subarray is
|
||||
empty.
|
||||
-/
|
||||
start_le_stop : start ≤ stop
|
||||
/-- The stopping index is no later than the end of the array.
|
||||
|
||||
The ending index is exclusive. If it is equal to the size of the array, then the last element of
|
||||
the array is in the subarray.
|
||||
-/
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
/--
|
||||
Computes the size of the subarray.
|
||||
-/
|
||||
def size (s : Subarray α) : Nat :=
|
||||
s.stop - s.start
|
||||
|
||||
@@ -26,6 +54,11 @@ theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
|
||||
apply Nat.le_trans (Nat.sub_le stop start)
|
||||
assumption
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray.
|
||||
|
||||
The index is relative to the start of the subarray, rather than the underlying array.
|
||||
-/
|
||||
def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
have : s.start + i.val < s.array.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
|
||||
@@ -38,12 +71,33 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value `v₀` when the index is out of
|
||||
bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array.
|
||||
-/
|
||||
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
|
||||
if h : i < s.size then s[i] else v₀
|
||||
|
||||
/--
|
||||
Extracts an element from the subarray, or returns a default value when the index is out of bounds.
|
||||
|
||||
The index is relative to the start and end of the subarray, rather than the underlying array. The
|
||||
default value is that provided by the `Inhabited α` instance.
|
||||
-/
|
||||
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
getD s i default
|
||||
|
||||
/--
|
||||
Shrinks the subarray by incrementing its starting index if possible, returning it unchanged if not.
|
||||
|
||||
Examples:
|
||||
* `#[1,2,3].toSubarray.popFront.toArray = #[2, 3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.toArray = #[3]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.toArray = #[]`
|
||||
* `#[1,2,3].toSubarray.popFront.popFront.popFront.popFront.toArray = #[]`
|
||||
-/
|
||||
def popFront (s : Subarray α) : Subarray α :=
|
||||
if h : s.start < s.stop then
|
||||
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
@@ -52,6 +106,8 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
|
||||
/--
|
||||
The empty subarray.
|
||||
|
||||
This empty subarray is backed by an empty array.
|
||||
-/
|
||||
protected def empty : Subarray α where
|
||||
array := #[]
|
||||
@@ -66,6 +122,12 @@ instance : EmptyCollection (Subarray α) :=
|
||||
instance : Inhabited (Subarray α) :=
|
||||
⟨{}⟩
|
||||
|
||||
/--
|
||||
The run-time implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for`
|
||||
loops in `do`-notation.
|
||||
|
||||
This definition replaces `Subarray.forIn`.
|
||||
-/
|
||||
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
@@ -78,6 +140,10 @@ instance : Inhabited (Subarray α) :=
|
||||
pure b
|
||||
loop (USize.ofNat s.start) b
|
||||
|
||||
/--
|
||||
The implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for` loops in
|
||||
`do`-notation.
|
||||
-/
|
||||
-- TODO: provide reference implementation
|
||||
@[implemented_by Subarray.forInUnsafe]
|
||||
protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (s : Subarray α) (b : β) (f : α → β → m (ForInStep β)) : m β :=
|
||||
@@ -86,46 +152,197 @@ protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Mona
|
||||
instance : ForIn m (Subarray α) α where
|
||||
forIn := Subarray.forIn
|
||||
|
||||
/--
|
||||
Folds a monadic operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn. The monad in question may permit
|
||||
early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := "") fun acc x => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(3)red (5)green (4)blue "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldlM (init := 0) fun acc x => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Folds a monadic operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
|
||||
element of the subarray with the current accumulator value in turn, moving from the end to the
|
||||
start. The monad in question may permit early termination or repetition.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := "") fun x acc => do
|
||||
let l ← Option.guard (· ≠ 0) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
some "(4)blue (5)green (3)red "
|
||||
```
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.foldrM (init := 0) fun x acc => do
|
||||
let l ← Option.guard (· ≠ 5) x.length
|
||||
return s!"{acc}({l}){x} "
|
||||
```
|
||||
```output
|
||||
none
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.anyM fun x => do
|
||||
IO.println x
|
||||
pure (x == "blue")
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
true
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.anyM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a monadic Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.allM fun x => do
|
||||
IO.println x
|
||||
pure (x.length == 5)
|
||||
```
|
||||
```output
|
||||
green
|
||||
blue
|
||||
```
|
||||
```output
|
||||
false
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.allM p (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray.
|
||||
|
||||
The elements are processed starting at the lowest index and moving up.
|
||||
-/
|
||||
@[inline]
|
||||
def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forM f (start := as.start) (stop := as.stop)
|
||||
|
||||
/--
|
||||
Runs a monadic action on each element of a subarray, in reverse order.
|
||||
|
||||
The elements are processed starting at the highest index and moving down.
|
||||
-/
|
||||
@[inline]
|
||||
def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forRevM f (start := as.stop) (stop := as.start)
|
||||
|
||||
/--
|
||||
Folds an operation from left to right over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each
|
||||
element of the subarray with the current accumulator value in turn.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.foldl (· + ·.length) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldlM f (init := init)
|
||||
|
||||
/--
|
||||
Folds an operation from right to left over the elements in a subarray.
|
||||
|
||||
An accumulator of type `β` is constructed by starting with `init` and combining each element of the
|
||||
subarray with the current accumulator value in turn, moving from the end to the start.
|
||||
|
||||
Examples:
|
||||
* `#eval #["red", "green", "blue"].toSubarray.foldr (·.length + ·) 0 = 12`
|
||||
* `#["red", "green", "blue"].toSubarray.popFront.foldlr (·.length + ·) 0 = 9`
|
||||
-/
|
||||
@[inline]
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldrM f (init := init)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.anyM p
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
|
||||
|
||||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def all {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.allM p
|
||||
|
||||
/--
|
||||
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
|
||||
element for which the function succeeds by returning a value other than `none`. The succeeding value
|
||||
is returned, or `none` if there is no success.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findSomeRevM? fun x => do
|
||||
IO.println x
|
||||
return Option.guard (· = 5) x.length
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Subarray α) (f : α → m (Option β)) : m (Option β) :=
|
||||
let rec @[specialize] find : (i : Nat) → i ≤ as.size → m (Option β)
|
||||
@@ -140,10 +357,39 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
|
||||
find i this
|
||||
find as.size (Nat.le_refl _)
|
||||
|
||||
/--
|
||||
Applies a monadic Boolean predicate to each element in a subarray in reverse order, stopping at the
|
||||
first element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies it.
|
||||
|
||||
Example:
|
||||
```lean example
|
||||
#eval #["red", "green", "blue"].toSubarray.findRevM? fun x => do
|
||||
IO.println x
|
||||
return (x.length = 5)
|
||||
```
|
||||
```output
|
||||
blue
|
||||
green
|
||||
```
|
||||
```output
|
||||
some 5
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def findRevM? {α : Type} {m : Type → Type w} [Monad m] (as : Subarray α) (p : α → m Bool) : m (Option α) :=
|
||||
as.findSomeRevM? fun a => return if (← p a) then some a else none
|
||||
|
||||
/--
|
||||
Tests each element in a subarray with a Boolean predicate in reverse order, stopping at the first
|
||||
element that satisfies the predicate. The element that satisfies the predicate is returned, or
|
||||
`none` if no element satisfies the predicate.
|
||||
|
||||
Examples:
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (·.length ≠ 4) = some "green"`
|
||||
* `#["red", "green", "blue"].toSubarray.findRev? (fun _ => true) = some "blue"`
|
||||
* `#["red", "green", "blue"].toSubarray 0 0 |>.findRev? (fun _ => true) = none`
|
||||
-/
|
||||
@[inline]
|
||||
def findRev? {α : Type} (as : Subarray α) (p : α → Bool) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
@@ -153,6 +399,12 @@ end Subarray
|
||||
namespace Array
|
||||
variable {α : Type u}
|
||||
|
||||
/--
|
||||
Returns a subarray of an array, with the given bounds.
|
||||
|
||||
If `start` or `stop` are not valid bounds for a subarray, then they are clamped to array's size.
|
||||
Additionally, the starting index is clamped to the ending index.
|
||||
-/
|
||||
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
|
||||
if h₂ : stop ≤ as.size then
|
||||
if h₁ : start ≤ stop then
|
||||
@@ -175,6 +427,9 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
|
||||
start_le_stop := Nat.le_refl _,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
|
||||
/--
|
||||
Allocates a new array that contains the contents of the subarray.
|
||||
-/
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
let mut as := mkEmpty (s.stop - s.start)
|
||||
@@ -184,8 +439,11 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
|
||||
instance : Coe (Subarray α) (Array α) := ⟨ofSubarray⟩
|
||||
|
||||
/-- A subarray with the provided bounds.-/
|
||||
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
|
||||
/-- A subarray with the provided lower bound that extends to the rest of the array. -/
|
||||
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
|
||||
/-- A subarray with the provided upper bound, starting at the index 0. -/
|
||||
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
|
||||
|
||||
macro_rules
|
||||
@@ -195,6 +453,7 @@ macro_rules
|
||||
|
||||
end Array
|
||||
|
||||
@[inherit_doc Array.ofSubarray]
|
||||
def Subarray.toArray (s : Subarray α) : Array α :=
|
||||
Array.ofSubarray s
|
||||
|
||||
|
||||
@@ -15,9 +15,13 @@ automation. Placing them in another module breaks an import cycle, because `omeg
|
||||
array library.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Subarray
|
||||
/--
|
||||
Splits a subarray into two parts.
|
||||
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
|
||||
of which contains the remainder.
|
||||
-/
|
||||
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
|
||||
let ⟨i', isLt⟩ := i
|
||||
|
||||
@@ -7,11 +7,28 @@ prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
|
||||
/-!
|
||||
These lemmas are used in the internals of HashMap.
|
||||
They should find a new home and/or be reformulated.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace List
|
||||
|
||||
theorem exists_of_set {i : Nat} {a' : α} {l : List α} (h : i < l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l[i] :: l₂ ∧ l₁.length = i ∧ l.set i a' = l₁ ++ a' :: l₂ := by
|
||||
refine ⟨l.take i, l.drop (i + 1), ⟨by simp, ⟨length_take_of_le (Nat.le_of_lt h), ?_⟩⟩⟩
|
||||
simp [set_eq_take_append_cons_drop, h]
|
||||
|
||||
end List
|
||||
|
||||
namespace Array
|
||||
|
||||
theorem exists_of_uset (self : Array α) (i d h) :
|
||||
∃ l₁ l₂, self.toList = l₁ ++ self[i] :: l₂ ∧ List.length l₁ = i.toNat ∧
|
||||
(self.uset i d h).toList = l₁ ++ d :: l₂ := by
|
||||
theorem exists_of_uset {xs : Array α} {i d} (h) :
|
||||
∃ l₁ l₂, xs.toList = l₁ ++ xs[i] :: l₂ ∧ List.length l₁ = i.toNat ∧
|
||||
(xs.uset i d h).toList = l₁ ++ d :: l₂ := by
|
||||
simpa only [ugetElem_eq_getElem, ← getElem_toList, uset, toList_set] using
|
||||
List.exists_of_set _
|
||||
|
||||
|
||||
378
src/Init/Data/Array/Zip.lean
Normal file
378
src/Init/Data/Array/Zip.lean
Normal file
@@ -0,0 +1,378 @@
|
||||
/-
|
||||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.TakeDrop
|
||||
import Init.Data.List.Zip
|
||||
|
||||
/-!
|
||||
# Lemmas about `Array.zip`, `Array.zipWith`, `Array.zipWithAll`, and `Array.unzip`.
|
||||
-/
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace Array
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Zippers -/
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
theorem zipWith_comm {f : α → β → γ} {as : Array α} {bs : Array β} :
|
||||
zipWith f as bs = zipWith (fun b a => f a b) bs as := by
|
||||
cases as
|
||||
cases bs
|
||||
simpa using List.zipWith_comm
|
||||
|
||||
theorem zipWith_comm_of_comm {f : α → α → β} (comm : ∀ x y : α, f x y = f y x) {xs ys : Array α} :
|
||||
zipWith f xs ys = zipWith f ys xs := by
|
||||
rw [zipWith_comm]
|
||||
simp only [comm]
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_self {f : α → α → δ} {xs : Array α} : zipWith f xs xs = xs.map fun a => f a a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
/--
|
||||
See also `getElem?_zipWith'` for a variant
|
||||
using `Option.map` and `Option.bind` rather than a `match`.
|
||||
-/
|
||||
theorem getElem?_zipWith {f : α → β → γ} {i : Nat} :
|
||||
(zipWith f as bs)[i]? = match as[i]?, bs[i]? with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.getElem?_zipWith]
|
||||
rfl
|
||||
|
||||
/-- Variant of `getElem?_zipWith` using `Option.map` and `Option.bind` rather than a `match`. -/
|
||||
theorem getElem?_zipWith' {f : α → β → γ} {i : Nat} :
|
||||
(zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g := by
|
||||
cases l₁
|
||||
cases l₂
|
||||
simp [List.getElem?_zipWith']
|
||||
|
||||
theorem getElem?_zipWith_eq_some {f : α → β → γ} {as : Array α} {bs : Array β} {z : γ} {i : Nat} :
|
||||
(zipWith f as bs)[i]? = some z ↔
|
||||
∃ x y, as[i]? = some x ∧ bs[i]? = some y ∧ f x y = z := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.getElem?_zipWith_eq_some]
|
||||
|
||||
theorem getElem?_zip_eq_some {as : Array α} {bs : Array β} {z : α × β} {i : Nat} :
|
||||
(zip as bs)[i]? = some z ↔ as[i]? = some z.1 ∧ bs[i]? = some z.2 := by
|
||||
cases z
|
||||
rw [zip, getElem?_zipWith_eq_some]; constructor
|
||||
· rintro ⟨x, y, h₀, h₁, h₂⟩
|
||||
simpa [h₀, h₁] using h₂
|
||||
· rintro ⟨h₀, h₁⟩
|
||||
exact ⟨_, _, h₀, h₁, rfl⟩
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_map {μ} {f : γ → δ → μ} {g : α → γ} {h : β → δ} {as : Array α} {bs : Array β} :
|
||||
zipWith f (as.map g) (bs.map h) = zipWith (fun a b => f (g a) (h b)) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWith_map]
|
||||
|
||||
theorem zipWith_map_left {as : Array α} {bs : Array β} {f : α → α'} {g : α' → β → γ} :
|
||||
zipWith g (as.map f) bs = zipWith (fun a b => g (f a) b) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWith_map_left]
|
||||
|
||||
theorem zipWith_map_right {as : Array α} {bs : Array β} {f : β → β'} {g : α → β' → γ} :
|
||||
zipWith g as (bs.map f) = zipWith (fun a b => g a (f b)) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWith_map_right]
|
||||
|
||||
theorem zipWith_foldr_eq_zip_foldr {f : α → β → γ} {i : δ} :
|
||||
(zipWith f as bs).foldr g i = (zip as bs).foldr (fun p r => g (f p.1 p.2) r) i := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWith_foldr_eq_zip_foldr]
|
||||
|
||||
theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
|
||||
(zipWith f as bs).foldl g i = (zip as bs).foldl (fun r p => g r (f p.1 p.2)) i := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWith_foldl_eq_zip_foldl]
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_eq_empty_iff {f : α → β → γ} {as : Array α} {bs : Array β} : zipWith f as bs = #[] ↔ as = #[] ∨ bs = #[] := by
|
||||
cases as <;> cases bs <;> simp
|
||||
|
||||
theorem map_zipWith {δ : Type _} {f : α → β} {g : γ → δ → α} {cs : Array γ} {ds : Array δ} :
|
||||
map f (zipWith g cs ds) = zipWith (fun x y => f (g x y)) cs ds := by
|
||||
cases cs
|
||||
cases ds
|
||||
simp [List.map_zipWith]
|
||||
|
||||
theorem take_zipWith : (zipWith f as bs).take i = zipWith f (as.take i) (bs.take i) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.take_zipWith]
|
||||
|
||||
theorem extract_zipWith : (zipWith f as bs).extract i j = zipWith f (as.extract i j) (bs.extract i j) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.drop_zipWith, List.take_zipWith]
|
||||
|
||||
theorem zipWith_append {f : α → β → γ} {as as' : Array α} {bs bs' : Array β}
|
||||
(h : as.size = bs.size) :
|
||||
zipWith f (as ++ as') (bs ++ bs') = zipWith f as bs ++ zipWith f as' bs' := by
|
||||
cases as
|
||||
cases bs
|
||||
cases as'
|
||||
cases bs'
|
||||
simp at h
|
||||
simp [List.zipWith_append, h]
|
||||
|
||||
theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array β} :
|
||||
zipWith f as bs = xs ++ ys ↔
|
||||
∃ as₁ as₂ bs₁ bs₂, as₁.size = bs₁.size ∧ as = as₁ ++ as₂ ∧ bs = bs₁ ++ bs₂ ∧ xs = zipWith f as₁ bs₁ ∧ ys = zipWith f as₂ bs₂ := by
|
||||
cases as
|
||||
cases bs
|
||||
cases xs
|
||||
cases ys
|
||||
simp only [List.zipWith_toArray, List.append_toArray, mk.injEq, List.zipWith_eq_append_iff,
|
||||
toArray_eq_append_iff]
|
||||
constructor
|
||||
· rintro ⟨ws, xs, ys, zs, h, rfl, rfl, rfl, rfl⟩
|
||||
exact ⟨ws.toArray, xs.toArray, ys.toArray, zs.toArray, by simp [h]⟩
|
||||
· rintro ⟨⟨ws⟩, ⟨xs⟩, ⟨ys⟩, ⟨zs⟩, h, rfl, rfl, h₁, h₂⟩
|
||||
exact ⟨ws, xs, ys, zs, by simp_all⟩
|
||||
|
||||
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zipWith_replicate (since := "2025-03-18")]
|
||||
abbrev zipWith_mkArray := @zipWith_replicate
|
||||
|
||||
theorem map_uncurry_zip_eq_zipWith {f : α → β → γ} {as : Array α} {bs : Array β} :
|
||||
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.map_uncurry_zip_eq_zipWith]
|
||||
|
||||
theorem map_zip_eq_zipWith {f : α × β → γ} {as : Array α} {bs : Array β} :
|
||||
map f (as.zip bs) = zipWith (Function.curry f) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.map_zip_eq_zipWith]
|
||||
|
||||
theorem lt_size_left_of_zipWith {f : α → β → γ} {i : Nat} {as : Array α} {bs : Array β}
|
||||
(h : i < (zipWith f as bs).size) : i < as.size := by rw [size_zipWith] at h; omega
|
||||
|
||||
theorem lt_size_right_of_zipWith {f : α → β → γ} {i : Nat} {as : Array α} {bs : Array β}
|
||||
(h : i < (zipWith f as bs).size) : i < bs.size := by rw [size_zipWith] at h; omega
|
||||
|
||||
theorem zipWith_eq_zipWith_take_min (as : Array α) (bs : Array β) :
|
||||
zipWith f as bs = zipWith f (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp
|
||||
rw [List.zipWith_eq_zipWith_take_min]
|
||||
|
||||
theorem reverse_zipWith (h : as.size = bs.size) :
|
||||
(zipWith f as bs).reverse = zipWith f as.reverse bs.reverse := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.reverse_zipWith (by simpa using h)]
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
theorem lt_size_left_of_zip {i : Nat} {as : Array α} {bs : Array β} (h : i < (zip as bs).size) :
|
||||
i < as.size :=
|
||||
lt_size_left_of_zipWith h
|
||||
|
||||
theorem lt_size_right_of_zip {i : Nat} {as : Array α} {bs : Array β} (h : i < (zip as bs).size) :
|
||||
i < bs.size :=
|
||||
lt_size_right_of_zipWith h
|
||||
|
||||
@[simp]
|
||||
theorem getElem_zip {as : Array α} {bs : Array β} {i : Nat} {h : i < (zip as bs).size} :
|
||||
(zip as bs)[i] =
|
||||
(as[i]'(lt_size_left_of_zip h), bs[i]'(lt_size_right_of_zip h)) :=
|
||||
getElem_zipWith (hi := by simpa using h)
|
||||
|
||||
theorem zip_eq_zipWith {as : Array α} {bs : Array β} : zip as bs = zipWith Prod.mk as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zip_eq_zipWith]
|
||||
|
||||
theorem zip_map {f : α → γ} {g : β → δ} {as : Array α} {bs : Array β} :
|
||||
zip (as.map f) (bs.map g) = (zip as bs).map (Prod.map f g) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zip_map]
|
||||
|
||||
theorem zip_map_left {f : α → γ} {as : Array α} {bs : Array β} :
|
||||
zip (as.map f) bs = (zip as bs).map (Prod.map f id) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_map_right {f : β → γ} {as : Array α} {bs : Array β} :
|
||||
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size) :
|
||||
zip (as ++ bs) (cs ++ ds) = zip as cs ++ zip bs ds := by
|
||||
cases as
|
||||
cases cs
|
||||
cases bs
|
||||
cases ds
|
||||
simp_all [List.zip_append]
|
||||
|
||||
theorem zip_map' {f : α → β} {g : α → γ} {xs : Array α} :
|
||||
zip (xs.map f) (xs.map g) = xs.map fun a => (f a, g a) := by
|
||||
cases xs
|
||||
simp [List.zip_map']
|
||||
|
||||
theorem of_mem_zip {a b} {as : Array α} {bs : Array β} : (a, b) ∈ zip as bs → a ∈ as ∧ b ∈ bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simpa using List.of_mem_zip
|
||||
|
||||
theorem map_fst_zip {as : Array α} {bs : Array β} (h : as.size ≤ bs.size) :
|
||||
map Prod.fst (zip as bs) = as := by
|
||||
cases as
|
||||
cases bs
|
||||
simp_all [List.map_fst_zip]
|
||||
|
||||
theorem map_snd_zip {as : Array α} {bs : Array β} (h : bs.size ≤ as.size) :
|
||||
map Prod.snd (zip as bs) = bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp_all [List.map_snd_zip]
|
||||
|
||||
theorem map_prod_left_eq_zip {xs : Array α} {f : α → β} :
|
||||
(xs.map fun x => (x, f x)) = xs.zip (xs.map f) := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
theorem map_prod_right_eq_zip {xs : Array α} {f : α → β} :
|
||||
(xs.map fun x => (f x, x)) = (xs.map f).zip xs := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
simp
|
||||
|
||||
@[simp] theorem zip_eq_empty_iff {as : Array α} {bs : Array β} :
|
||||
zip as bs = #[] ↔ as = #[] ∨ bs = #[] := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zip_eq_nil_iff]
|
||||
|
||||
theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
|
||||
zip as bs = xs ++ ys ↔
|
||||
∃ as₁ as₂ bs₁ bs₂, as₁.size = bs₁.size ∧ as = as₁ ++ as₂ ∧ bs = bs₁ ++ bs₂ ∧ xs = zip as₁ bs₁ ∧ ys = zip as₂ bs₂ := by
|
||||
simp [zip_eq_zipWith, zipWith_eq_append_iff]
|
||||
|
||||
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zip_replicate (since := "2025-03-18")]
|
||||
abbrev zip_mkArray := @zip_replicate
|
||||
|
||||
theorem zip_eq_zip_take_min {as : Array α} {bs : Array β} :
|
||||
zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp only [List.zip_toArray, List.size_toArray, List.take_toArray, mk.injEq]
|
||||
rw [List.zip_eq_zip_take_min]
|
||||
|
||||
|
||||
/-! ### zipWithAll -/
|
||||
|
||||
theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
|
||||
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.getElem?_zipWithAll]
|
||||
rfl
|
||||
|
||||
theorem zipWithAll_map {μ} {f : Option γ → Option δ → μ} {g : α → γ} {h : β → δ} {as : Array α} {bs : Array β} :
|
||||
zipWithAll f (as.map g) (bs.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWithAll_map]
|
||||
|
||||
theorem zipWithAll_map_left {as : Array α} {bs : Array β} {f : α → α'} {g : Option α' → Option β → γ} :
|
||||
zipWithAll g (as.map f) bs = zipWithAll (fun a b => g (f <$> a) b) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWithAll_map_left]
|
||||
|
||||
theorem zipWithAll_map_right {as : Array α} {bs : Array β} {f : β → β'} {g : Option α → Option β' → γ} :
|
||||
zipWithAll g as (bs.map f) = zipWithAll (fun a b => g a (f <$> b)) as bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp [List.zipWithAll_map_right]
|
||||
|
||||
theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option δ → α} {cs : Array γ} {ds : Array δ} :
|
||||
map f (zipWithAll g cs ds) = zipWithAll (fun x y => f (g x y)) cs ds := by
|
||||
cases cs
|
||||
cases ds
|
||||
simp [List.map_zipWithAll]
|
||||
|
||||
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
|
||||
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
|
||||
simp [← List.toArray_replicate]
|
||||
|
||||
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
|
||||
abbrev zipWithAll_mkArray := @zipWithAll_replicate
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
|
||||
induction l <;> simp_all
|
||||
|
||||
theorem unzip_eq_map {xs : Array (α × β)} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
|
||||
cases xs
|
||||
simp [List.unzip_eq_map]
|
||||
|
||||
-- The argument `xs` is explicit so we can rewrite from right to left.
|
||||
theorem zip_unzip (xs : Array (α × β)) : zip (unzip xs).1 (unzip xs).2 = xs := by
|
||||
cases xs
|
||||
simp only [List.unzip_toArray, Prod.map_fst, Prod.map_snd, List.zip_toArray, List.zip_unzip]
|
||||
|
||||
theorem unzip_zip_left {as : Array α} {bs : Array β} (h : as.size ≤ bs.size) :
|
||||
(unzip (zip as bs)).1 = as := by
|
||||
cases as
|
||||
cases bs
|
||||
simp_all only [List.size_toArray, List.zip_toArray, List.unzip_toArray, Prod.map_fst,
|
||||
List.unzip_zip_left]
|
||||
|
||||
theorem unzip_zip_right {as : Array α} {bs : Array β} (h : bs.size ≤ as.size) :
|
||||
(unzip (zip as bs)).2 = bs := by
|
||||
cases as
|
||||
cases bs
|
||||
simp_all only [List.size_toArray, List.zip_toArray, List.unzip_toArray, Prod.map_snd,
|
||||
List.unzip_zip_right]
|
||||
|
||||
theorem unzip_zip {as : Array α} {bs : Array β} (h : as.size = bs.size) :
|
||||
unzip (zip as bs) = (as, bs) := by
|
||||
cases as
|
||||
cases bs
|
||||
simp_all only [List.size_toArray, List.zip_toArray, List.unzip_toArray, List.unzip_zip, Prod.map_apply]
|
||||
|
||||
theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl : xs.map Prod.fst = as)
|
||||
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
|
||||
rw [← hl, ← hr, ← zip_unzip xs, ← unzip_fst, ← unzip_snd, zip_unzip, zip_unzip]
|
||||
|
||||
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
|
||||
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
|
||||
ext1 <;> simp
|
||||
|
||||
@[deprecated unzip_replicate (since := "2025-03-18")]
|
||||
abbrev unzip_mkArray := @unzip_replicate
|
||||
@@ -25,7 +25,7 @@ class ReflBEq (α) [BEq α] : Prop where
|
||||
refl : (a : α) == a
|
||||
|
||||
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
|
||||
class EquivBEq (α) [BEq α] extends PartialEquivBEq α, ReflBEq α : Prop
|
||||
class EquivBEq (α) [BEq α] : Prop extends PartialEquivBEq α, ReflBEq α
|
||||
|
||||
@[simp]
|
||||
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
|
||||
@@ -49,6 +49,14 @@ theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = fal
|
||||
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b → b == c → a == c :=
|
||||
PartialEquivBEq.trans
|
||||
|
||||
theorem BEq.congr_left [BEq α] [PartialEquivBEq α] {a b c : α} (h : a == b) :
|
||||
(a == c) = (b == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨BEq.trans (BEq.symm h), BEq.trans h⟩
|
||||
|
||||
theorem BEq.congr_right [BEq α] [PartialEquivBEq α] {a b c : α} (h : b == c) :
|
||||
(a == b) = (a == c) :=
|
||||
Bool.eq_iff_iff.mpr ⟨fun h' => BEq.trans h' h, fun h' => BEq.trans h' (BEq.symm h)⟩
|
||||
|
||||
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
|
||||
(a == b) = false → b == c → (a == c) = false :=
|
||||
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
|
||||
|
||||
@@ -18,20 +18,24 @@ operations on `Fin` are already defined. Some other possible representations are
|
||||
|
||||
We define many of the bitvector operations from the
|
||||
[`QF_BV` logic](https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV).
|
||||
of SMT-LIBv2.
|
||||
of SMT-LIB v2.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace BitVec
|
||||
|
||||
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
|
||||
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
|
||||
BitVec.ofNatLT i p
|
||||
|
||||
section Nat
|
||||
|
||||
instance natCastInst : NatCast (BitVec w) := ⟨BitVec.ofNat w⟩
|
||||
|
||||
/-- Theorem for normalizing the bit vector literal representation. -/
|
||||
/-- Theorem for normalizing the bitvector literal representation. -/
|
||||
-- TODO: This needs more usage data to assess which direction the simp should go.
|
||||
@[simp, bv_toNat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
|
||||
@[simp, bitvec_to_nat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
|
||||
|
||||
-- Note. Mathlib would like this to go the other direction.
|
||||
@[simp] theorem natCast_eq_ofNat (w x : Nat) : @Nat.cast (BitVec w) _ x = .ofNat w x := rfl
|
||||
@@ -44,7 +48,7 @@ section subsingleton
|
||||
instance : Subsingleton (BitVec 0) where
|
||||
allEq := by intro ⟨0, _⟩ ⟨0, _⟩; rfl
|
||||
|
||||
/-- The empty bitvector -/
|
||||
/-- The empty bitvector. -/
|
||||
abbrev nil : BitVec 0 := 0
|
||||
|
||||
/-- Every bitvector of length 0 is equal to `nil`, i.e., there is only one empty bitvector -/
|
||||
@@ -54,55 +58,49 @@ end subsingleton
|
||||
|
||||
section zero_allOnes
|
||||
|
||||
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
|
||||
protected def zero (n : Nat) : BitVec n := .ofNatLt 0 (Nat.two_pow_pos n)
|
||||
/-- Returns a bitvector of size `n` where all bits are `0`. -/
|
||||
protected def zero (n : Nat) : BitVec n := .ofNatLT 0 (Nat.two_pow_pos n)
|
||||
instance : Inhabited (BitVec n) where default := .zero n
|
||||
|
||||
/-- Bit vector of size `n` where all bits are `1`s -/
|
||||
/-- Returns a bitvector of size `n` where all bits are `1`. -/
|
||||
def allOnes (n : Nat) : BitVec n :=
|
||||
.ofNatLt (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
|
||||
.ofNatLT (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
|
||||
|
||||
end zero_allOnes
|
||||
|
||||
section getXsb
|
||||
|
||||
/--
|
||||
Return the `i`-th least significant bit.
|
||||
Returns the `i`th least significant bit.
|
||||
|
||||
This will be renamed `getLsb` after the existing deprecated alias is removed.
|
||||
-/
|
||||
@[inline] def getLsb' (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
|
||||
|
||||
/-- Return the `i`-th least significant bit or `none` if `i ≥ w`. -/
|
||||
/-- Returns the `i`th least significant bit, or `none` if `i ≥ w`. -/
|
||||
@[inline] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
|
||||
if h : i < w then some (getLsb' x ⟨i, h⟩) else none
|
||||
|
||||
/--
|
||||
Return the `i`-th most significant bit.
|
||||
Returns the `i`th most significant bit.
|
||||
|
||||
This will be renamed `getMsb` after the existing deprecated alias is removed.
|
||||
This will be renamed `BitVec.getMsb` after the existing deprecated alias is removed.
|
||||
-/
|
||||
@[inline] def getMsb' (x : BitVec w) (i : Fin w) : Bool := x.getLsb' ⟨w-1-i, by omega⟩
|
||||
|
||||
/-- Return the `i`-th most significant bit or `none` if `i ≥ w`. -/
|
||||
/-- Returns the `i`th most significant bit or `none` if `i ≥ w`. -/
|
||||
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
|
||||
if h : i < w then some (getMsb' x ⟨i, h⟩) else none
|
||||
|
||||
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
|
||||
/-- Returns the `i`th least significant bit or `false` if `i ≥ w`. -/
|
||||
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
x.toNat.testBit i
|
||||
|
||||
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
|
||||
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
|
||||
|
||||
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
|
||||
/-- Returns the `i`th most significant bit, or `false` if `i ≥ w`. -/
|
||||
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
|
||||
i < w && x.getLsbD (w-1-i)
|
||||
|
||||
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
|
||||
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
|
||||
|
||||
/-- Return most-significant bit in bitvector. -/
|
||||
/-- Returns the most significant bit in a bitvector. -/
|
||||
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
|
||||
|
||||
end getXsb
|
||||
@@ -123,6 +121,7 @@ instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
|
||||
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
|
||||
x[i] = x.toNat.testBit i := rfl
|
||||
|
||||
@[simp]
|
||||
theorem getLsbD_eq_getElem {x : BitVec w} {i : Nat} (h : i < w) :
|
||||
x.getLsbD i = x[i] := rfl
|
||||
|
||||
@@ -130,15 +129,23 @@ end getElem
|
||||
|
||||
section Int
|
||||
|
||||
/-- Interpret the bitvector as an integer stored in two's complement form. -/
|
||||
/--
|
||||
Interprets the bitvector as an integer stored in two's complement form.
|
||||
-/
|
||||
protected def toInt (x : BitVec n) : Int :=
|
||||
if 2 * x.toNat < 2^n then
|
||||
x.toNat
|
||||
else
|
||||
(x.toNat : Int) - (2^n : Nat)
|
||||
|
||||
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
|
||||
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
|
||||
/--
|
||||
Converts an integer to its two's complement representation as a bitvector of the given width `n`,
|
||||
over- and underflowing as needed.
|
||||
|
||||
The underlying `Nat` is `(2^n + (i mod 2^n)) mod 2^n`. Converting the bitvector back to an `Int`
|
||||
with `BitVec.toInt` results in the value `i.bmod (2^n)`.
|
||||
-/
|
||||
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLT (i % (Int.ofNat (2^n))).toNat (by
|
||||
apply (Int.toNat_lt _).mpr
|
||||
· apply Int.emod_lt_of_pos
|
||||
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
|
||||
@@ -153,21 +160,26 @@ end Int
|
||||
|
||||
section Syntax
|
||||
|
||||
/-- Notation for bit vector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
|
||||
/-- Notation for bitvector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
|
||||
syntax:max num noWs "#" noWs term:max : term
|
||||
macro_rules | `($i:num#$n) => `(BitVec.ofNat $n $i)
|
||||
|
||||
/-- Unexpander for bit vector literals. -/
|
||||
/-- not `ofNat_zero` -/
|
||||
recommended_spelling "zero" for "0#n" in [BitVec.ofNat, «term__#__»]
|
||||
/-- not `ofNat_one` -/
|
||||
recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
|
||||
|
||||
/-- Unexpander for bitvector literals. -/
|
||||
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $n $i:num) => `($i:num#$n)
|
||||
| _ => throw ()
|
||||
|
||||
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLt i lt`. -/
|
||||
/-- Notation for bitvector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLT i lt`. -/
|
||||
scoped syntax:max term:max noWs "#'" noWs term:max : term
|
||||
macro_rules | `($i#'$p) => `(BitVec.ofNatLt $i $p)
|
||||
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
|
||||
|
||||
/-- Unexpander for bit vector literals without truncation. -/
|
||||
@[app_unexpander BitVec.ofNatLt] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
|
||||
/-- Unexpander for bitvector literals without truncation. -/
|
||||
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $i $p) => `($i#'$p)
|
||||
| _ => throw ()
|
||||
|
||||
@@ -175,7 +187,11 @@ end Syntax
|
||||
|
||||
section repr_toString
|
||||
|
||||
/-- Convert bitvector into a fixed-width hex number. -/
|
||||
/--
|
||||
Converts a bitvector into a fixed-width hexadecimal number with enough digits to represent it.
|
||||
|
||||
If `n` is `0`, then one digit is returned. Otherwise, `⌊(n + 3) / 4⌋` digits are returned.
|
||||
-/
|
||||
protected def toHex {n : Nat} (x : BitVec n) : String :=
|
||||
let s := (Nat.toDigits 16 x.toNat).asString
|
||||
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
|
||||
@@ -189,63 +205,63 @@ end repr_toString
|
||||
section arithmetic
|
||||
|
||||
/--
|
||||
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
|
||||
modulo `2^n`.
|
||||
Negation of bitvectors. This can be interpreted as either signed or unsigned negation modulo `2^n`.
|
||||
Usually accessed via the `-` prefix operator.
|
||||
|
||||
SMT-Lib name: `bvneg`.
|
||||
SMT-LIB name: `bvneg`.
|
||||
-/
|
||||
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
|
||||
instance : Neg (BitVec n) := ⟨.neg⟩
|
||||
|
||||
/--
|
||||
Return the absolute value of a signed bitvector.
|
||||
Returns the absolute value of a signed bitvector.
|
||||
-/
|
||||
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
|
||||
|
||||
/--
|
||||
Multiplication for bit vectors. This can be interpreted as either signed or unsigned
|
||||
multiplication modulo `2^n`.
|
||||
Multiplies two bitvectors. This can be interpreted as either signed or unsigned multiplication
|
||||
modulo `2^n`. Usually accessed via the `*` operator.
|
||||
|
||||
SMT-Lib name: `bvmul`.
|
||||
SMT-LIB name: `bvmul`.
|
||||
-/
|
||||
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
|
||||
instance : Mul (BitVec n) := ⟨.mul⟩
|
||||
|
||||
/--
|
||||
Unsigned division for bit vectors using the Lean convention where division by zero returns zero.
|
||||
Unsigned division of bitvectors using the Lean convention where division by zero returns zero.
|
||||
Usually accessed via the `/` operator.
|
||||
-/
|
||||
def udiv (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat / y.toNat)#'(Nat.lt_of_le_of_lt (Nat.div_le_self _ _) x.isLt)
|
||||
instance : Div (BitVec n) := ⟨.udiv⟩
|
||||
|
||||
/--
|
||||
Unsigned modulo for bit vectors.
|
||||
Unsigned modulo for bitvectors. Usually accessed via the `%` operator.
|
||||
|
||||
SMT-Lib name: `bvurem`.
|
||||
SMT-LIB name: `bvurem`.
|
||||
-/
|
||||
def umod (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
|
||||
instance : Mod (BitVec n) := ⟨.umod⟩
|
||||
|
||||
/--
|
||||
Unsigned division for bit vectors using the
|
||||
[SMT-Lib convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
|
||||
where division by zero returns the `allOnes` bitvector.
|
||||
Unsigned division of bitvectors using the
|
||||
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml),
|
||||
where division by zero returns `BitVector.allOnes n`.
|
||||
|
||||
SMT-Lib name: `bvudiv`.
|
||||
SMT-LIB name: `bvudiv`.
|
||||
-/
|
||||
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
|
||||
|
||||
/--
|
||||
Signed t-division for bit vectors using the Lean convention where division
|
||||
by zero returns zero.
|
||||
Signed T-division (using the truncating rounding convention) for bitvectors. This function obeys the
|
||||
Lean convention that division by zero returns zero.
|
||||
|
||||
```lean
|
||||
sdiv 7#4 2 = 3#4
|
||||
sdiv (-9#4) 2 = -4#4
|
||||
sdiv 5#4 -2 = -2#4
|
||||
sdiv (-7#4) (-2) = 3#4
|
||||
```
|
||||
Examples:
|
||||
* `(7#4).sdiv 2 = 3#4`
|
||||
* `(-9#4).sdiv 2 = -4#4`
|
||||
* `(5#4).sdiv -2 = -2#4`
|
||||
* `(-7#4).sdiv (-2) = 3#4`
|
||||
-/
|
||||
def sdiv (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
@@ -255,11 +271,13 @@ def sdiv (x y : BitVec n) : BitVec n :=
|
||||
| true, true => udiv (.neg x) (.neg y)
|
||||
|
||||
/--
|
||||
Signed division for bit vectors using SMTLIB rules for division by zero.
|
||||
Signed division for bitvectors using the SMT-LIB using the
|
||||
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml),
|
||||
where division by zero returns `BitVector.allOnes n`.
|
||||
|
||||
Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
|
||||
Specifically, `x.smtSDiv 0 = if x >= 0 then -1 else 1`
|
||||
|
||||
SMT-Lib name: `bvsdiv`.
|
||||
SMT-LIB name: `bvsdiv`.
|
||||
-/
|
||||
def smtSDiv (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
@@ -271,7 +289,7 @@ def smtSDiv (x y : BitVec n) : BitVec n :=
|
||||
/--
|
||||
Remainder for signed division rounding to zero.
|
||||
|
||||
SMT_Lib name: `bvsrem`.
|
||||
SMT-LIB name: `bvsrem`.
|
||||
-/
|
||||
def srem (x y : BitVec n) : BitVec n :=
|
||||
match x.msb, y.msb with
|
||||
@@ -283,7 +301,7 @@ def srem (x y : BitVec n) : BitVec n :=
|
||||
/--
|
||||
Remainder for signed division rounded to negative infinity.
|
||||
|
||||
SMT_Lib name: `bvsmod`.
|
||||
SMT-LIB name: `bvsmod`.
|
||||
-/
|
||||
def smod (x y : BitVec m) : BitVec m :=
|
||||
match x.msb, y.msb with
|
||||
@@ -301,7 +319,7 @@ end arithmetic
|
||||
|
||||
section bool
|
||||
|
||||
/-- Turn a `Bool` into a bitvector of length `1` -/
|
||||
/-- Turns a `Bool` into a bitvector of length `1`. -/
|
||||
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
|
||||
|
||||
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
|
||||
@@ -315,34 +333,34 @@ end bool
|
||||
section relations
|
||||
|
||||
/--
|
||||
Unsigned less-than for bit vectors.
|
||||
Unsigned less-than for bitvectors.
|
||||
|
||||
SMT-Lib name: `bvult`.
|
||||
SMT-LIB name: `bvult`.
|
||||
-/
|
||||
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
|
||||
|
||||
/--
|
||||
Unsigned less-than-or-equal-to for bit vectors.
|
||||
Unsigned less-than-or-equal-to for bitvectors.
|
||||
|
||||
SMT-Lib name: `bvule`.
|
||||
SMT-LIB name: `bvule`.
|
||||
-/
|
||||
protected def ule (x y : BitVec n) : Bool := x.toNat ≤ y.toNat
|
||||
|
||||
/--
|
||||
Signed less-than for bit vectors.
|
||||
Signed less-than for bitvectors.
|
||||
|
||||
```lean
|
||||
BitVec.slt 6#4 7 = true
|
||||
BitVec.slt 7#4 8 = false
|
||||
```
|
||||
SMT-Lib name: `bvslt`.
|
||||
SMT-LIB name: `bvslt`.
|
||||
|
||||
Examples:
|
||||
* `BitVec.slt 6#4 7 = true`
|
||||
* `BitVec.slt 7#4 8 = false`
|
||||
-/
|
||||
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
|
||||
|
||||
/--
|
||||
Signed less-than-or-equal-to for bit vectors.
|
||||
Signed less-than-or-equal-to for bitvectors.
|
||||
|
||||
SMT-Lib name: `bvsle`.
|
||||
SMT-LIB name: `bvsle`.
|
||||
-/
|
||||
protected def sle (x y : BitVec n) : Bool := x.toInt ≤ y.toInt
|
||||
|
||||
@@ -350,8 +368,14 @@ end relations
|
||||
|
||||
section cast
|
||||
|
||||
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
|
||||
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq ▸ x.isLt)
|
||||
/--
|
||||
If two natural numbers `n` and `m` are equal, then a bitvector of width `n` is also a bitvector of
|
||||
width `m`.
|
||||
|
||||
Using `x.cast eq` should be preferred over `eq ▸ x` because there are special-purpose `simp` lemmas
|
||||
that can more consistently simplify `BitVec.cast` away.
|
||||
-/
|
||||
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLT x.toNat (eq ▸ x.isLt)
|
||||
|
||||
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
|
||||
(BitVec.ofNat n x).cast h = BitVec.ofNat m x := by
|
||||
@@ -364,33 +388,36 @@ section cast
|
||||
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : x.cast h = x := rfl
|
||||
|
||||
/--
|
||||
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
|
||||
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
|
||||
high bits.
|
||||
Extracts the bits `start` to `start + len - 1` from a bitvector of size `n` to yield a
|
||||
new bitvector of size `len`. If `start + len > n`, then the bitvector is zero-extended.
|
||||
-/
|
||||
def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toNat >>> start)
|
||||
|
||||
/--
|
||||
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
|
||||
yield a new bitvector of size `hi - lo + 1`.
|
||||
Extracts the bits from `hi` down to `lo` (both inclusive) from a bitvector, which is implicitly
|
||||
zero-extended if necessary.
|
||||
|
||||
SMT-Lib name: `extract`.
|
||||
The resulting bitvector has size `hi - lo + 1`.
|
||||
|
||||
SMT-LIB name: `extract`.
|
||||
-/
|
||||
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
|
||||
|
||||
/--
|
||||
A version of `setWidth` that requires a proof, but is a noop.
|
||||
Increases the width of a bitvector to one that is at least as large by zero-extending it.
|
||||
|
||||
This is a constant-time operation because the underlying `Nat` is unmodified; because the new width
|
||||
is at least as large as the old one, no overflow is possible.
|
||||
-/
|
||||
def setWidth' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
|
||||
x.toNat#'(by
|
||||
apply Nat.lt_of_lt_of_le x.isLt
|
||||
exact Nat.pow_le_pow_of_le_right (by trivial) le)
|
||||
exact Nat.pow_le_pow_right (by trivial) le)
|
||||
|
||||
@[deprecated setWidth' (since := "2024-09-18"), inherit_doc setWidth'] abbrev zeroExtend' := @setWidth'
|
||||
|
||||
/--
|
||||
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
|
||||
needing to compute `x % 2^(2+n)`.
|
||||
Returns `zeroExtend (w+n) x <<< n` without needing to compute `x % 2^(2+n)`.
|
||||
-/
|
||||
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
|
||||
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w + m) := by
|
||||
@@ -399,12 +426,20 @@ def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
|
||||
exact (Nat.two_pow_pos m)
|
||||
(msbs.toNat <<< m)#'(shiftLeftLt msbs.isLt m)
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
/--
|
||||
Transforms a bitvector of length `w` into a bitvector of length `v`, padding with `0` as needed.
|
||||
|
||||
The specific behavior depends on the relationship between the starting width `w` and the final width
|
||||
`v`:
|
||||
* If `v > w`, it is zero-extended; the high bits are padded with zeroes until the bitvector has `v`
|
||||
bits.
|
||||
* If `v = w`, the bitvector is returned unchanged.
|
||||
* If `v < w`, the high bits are truncated.
|
||||
|
||||
`BitVec.setWidth`, `BitVec.zeroExtend`, and `BitVec.truncate` are aliases for this operation.
|
||||
|
||||
SMT-LIB name: `zero_extend`.
|
||||
-/
|
||||
def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
if h : w ≤ v then
|
||||
@@ -412,29 +447,19 @@ def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
|
||||
else
|
||||
.ofNat v x.toNat
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
-/
|
||||
@[inherit_doc setWidth]
|
||||
abbrev zeroExtend := @setWidth
|
||||
|
||||
/--
|
||||
Transform `x` of length `w` into a bitvector of length `v`, by either:
|
||||
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
|
||||
- truncating the high bits, if `v < w`.
|
||||
|
||||
SMT-Lib name: `zero_extend`.
|
||||
-/
|
||||
@[inherit_doc setWidth]
|
||||
abbrev truncate := @setWidth
|
||||
|
||||
/--
|
||||
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
|
||||
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
|
||||
Transforms a bitvector of length `w` into a bitvector of length `v`, padding as needed with the most
|
||||
significant bit's value.
|
||||
|
||||
SMT-Lib name: `sign_extend`.
|
||||
If `x` is an empty bitvector, then the sign is treated as zero.
|
||||
|
||||
SMT-LIB name: `sign_extend`.
|
||||
-/
|
||||
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
|
||||
|
||||
@@ -443,69 +468,68 @@ end cast
|
||||
section bitwise
|
||||
|
||||
/--
|
||||
Bitwise AND for bit vectors.
|
||||
Bitwise and for bitvectors. Usually accessed via the `&&&` operator.
|
||||
|
||||
```lean
|
||||
0b1010#4 &&& 0b0110#4 = 0b0010#4
|
||||
```
|
||||
SMT-LIB name: `bvand`.
|
||||
|
||||
SMT-Lib name: `bvand`.
|
||||
Example:
|
||||
* `0b1010#4 &&& 0b0110#4 = 0b0010#4`
|
||||
-/
|
||||
protected def and (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
|
||||
instance : AndOp (BitVec w) := ⟨.and⟩
|
||||
|
||||
/--
|
||||
Bitwise OR for bit vectors.
|
||||
Bitwise or for bitvectors. Usually accessed via the `|||` operator.
|
||||
|
||||
```lean
|
||||
0b1010#4 ||| 0b0110#4 = 0b1110#4
|
||||
```
|
||||
SMT-LIB name: `bvor`.
|
||||
|
||||
SMT-Lib name: `bvor`.
|
||||
Example:
|
||||
* `0b1010#4 ||| 0b0110#4 = 0b1110#4`
|
||||
-/
|
||||
protected def or (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
|
||||
instance : OrOp (BitVec w) := ⟨.or⟩
|
||||
|
||||
/--
|
||||
Bitwise XOR for bit vectors.
|
||||
Bitwise xor for bitvectors. Usually accessed via the `^^^` operator.
|
||||
|
||||
```lean
|
||||
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
|
||||
```
|
||||
SMT-LIB name: `bvxor`.
|
||||
|
||||
SMT-Lib name: `bvxor`.
|
||||
Example:
|
||||
* `0b1010#4 ^^^ 0b0110#4 = 0b1100#4`
|
||||
-/
|
||||
protected def xor (x y : BitVec n) : BitVec n :=
|
||||
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
|
||||
instance : Xor (BitVec w) := ⟨.xor⟩
|
||||
|
||||
/--
|
||||
Bitwise NOT for bit vectors.
|
||||
Bitwise complement for bitvectors. Usually accessed via the `~~~` prefix operator.
|
||||
|
||||
```lean
|
||||
~~~(0b0101#4) == 0b1010
|
||||
```
|
||||
SMT-Lib name: `bvnot`.
|
||||
SMT-LIB name: `bvnot`.
|
||||
|
||||
Example:
|
||||
* `~~~(0b0101#4) == 0b1010`
|
||||
-/
|
||||
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
|
||||
instance : Complement (BitVec w) := ⟨.not⟩
|
||||
|
||||
/--
|
||||
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
|
||||
Shifts a bitvector to the left. The low bits are filled with zeros. As a numeric operation, this is
|
||||
equivalent to `x * 2^s`, modulo `2^n`.
|
||||
|
||||
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
|
||||
SMT-LIB name: `bvshl` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
|
||||
instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
|
||||
|
||||
/--
|
||||
(Logical) right shift for bit vectors. The high bits are filled with zeros.
|
||||
Shifts a bitvector to the right. This is a logical right shift - the high bits are filled with
|
||||
zeros.
|
||||
|
||||
As a numeric operation, this is equivalent to `x / 2^s`, rounding down.
|
||||
|
||||
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
|
||||
SMT-LIB name: `bvlshr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
|
||||
(x.toNat >>> s)#'(by
|
||||
@@ -517,11 +541,12 @@ def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
|
||||
instance : HShiftRight (BitVec w) Nat (BitVec w) := ⟨.ushiftRight⟩
|
||||
|
||||
/--
|
||||
Arithmetic right shift for bit vectors. The high bits are filled with the
|
||||
most-significant bit.
|
||||
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with
|
||||
most significant bit's value.
|
||||
|
||||
As a numeric operation, this is equivalent to `x.toInt >>> s`.
|
||||
|
||||
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
|
||||
SMT-LIB name: `bvashr` except this operator uses a `Nat` shift value.
|
||||
-/
|
||||
def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
|
||||
|
||||
@@ -529,11 +554,12 @@ instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x <
|
||||
instance {n} : HShiftRight (BitVec m) (BitVec n) (BitVec m) := ⟨fun x y => x >>> y.toNat⟩
|
||||
|
||||
/--
|
||||
Arithmetic right shift for bit vectors. The high bits are filled with the
|
||||
most-significant bit.
|
||||
Shifts a bitvector to the right. This is an arithmetic right shift - the high bits are filled with
|
||||
most significant bit's value.
|
||||
|
||||
As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`.
|
||||
|
||||
SMT-Lib name: `bvashr`.
|
||||
SMT-LIB name: `bvashr`.
|
||||
-/
|
||||
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
|
||||
|
||||
@@ -543,13 +569,15 @@ def rotateLeftAux (x : BitVec w) (n : Nat) : BitVec w :=
|
||||
x <<< n ||| x >>> (w - n)
|
||||
|
||||
/--
|
||||
Rotate left for bit vectors. All the bits of `x` are shifted to higher positions, with the top `n`
|
||||
bits wrapping around to fill the low bits.
|
||||
Rotates the bits in a bitvector to the left.
|
||||
|
||||
```lean
|
||||
rotateLeft 0b0011#4 3 = 0b1001
|
||||
```
|
||||
SMT-Lib name: `rotate_left` except this operator uses a `Nat` shift amount.
|
||||
All the bits of `x` are shifted to higher positions, with the top `n` bits wrapping around to fill
|
||||
the vacated low bits.
|
||||
|
||||
SMT-LIB name: `rotate_left`, except this operator uses a `Nat` shift amount.
|
||||
|
||||
Example:
|
||||
* `(0b0011#4).rotateLeft 3 = 0b1001`
|
||||
-/
|
||||
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
|
||||
|
||||
@@ -562,21 +590,26 @@ def rotateRightAux (x : BitVec w) (n : Nat) : BitVec w :=
|
||||
x >>> n ||| x <<< (w - n)
|
||||
|
||||
/--
|
||||
Rotate right for bit vectors. All the bits of `x` are shifted to lower positions, with the
|
||||
bottom `n` bits wrapping around to fill the high bits.
|
||||
Rotates the bits in a bitvector to the right.
|
||||
|
||||
```lean
|
||||
rotateRight 0b01001#5 1 = 0b10100
|
||||
```
|
||||
SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
|
||||
All the bits of `x` are shifted to lower positions, with the bottom `n` bits wrapping around to fill
|
||||
the vacated high bits.
|
||||
|
||||
SMT-LIB name: `rotate_right`, except this operator uses a `Nat` shift amount.
|
||||
|
||||
Example:
|
||||
* `rotateRight 0b01001#5 1 = 0b10100`
|
||||
-/
|
||||
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
|
||||
|
||||
/--
|
||||
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
|
||||
input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
|
||||
Concatenates two bitvectors using the “big-endian” convention that the more significant
|
||||
input is on the left. Usually accessed via the `++` operator.
|
||||
|
||||
SMT-Lib name: `concat`.
|
||||
SMT-LIB name: `concat`.
|
||||
|
||||
Example:
|
||||
* `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
|
||||
-/
|
||||
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
|
||||
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
|
||||
@@ -584,7 +617,7 @@ def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
|
||||
instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := ⟨.append⟩
|
||||
|
||||
-- TODO: write this using multiplication
|
||||
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
|
||||
/-- Concatenates `i` copies of `x` into a new vector of length `w * i`. -/
|
||||
def replicate : (i : Nat) → BitVec w → BitVec (w*i)
|
||||
| 0, _ => 0#0
|
||||
| n+1, x =>
|
||||
@@ -603,14 +636,18 @@ result of appending a single bit to the front in the naive implementation).
|
||||
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
|
||||
|
||||
/--
|
||||
`x.shiftConcat b` shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
|
||||
It is a non-dependent version of `concat` that does not change the total bitwidth.
|
||||
Shifts all bits of `x` to the left by `1` and sets the least significant bit to `b`.
|
||||
|
||||
This is a non-dependent version of `BitVec.concat` that does not change the total bitwidth.
|
||||
-/
|
||||
def shiftConcat (x : BitVec n) (b : Bool) : BitVec n :=
|
||||
(x.concat b).truncate n
|
||||
|
||||
/-- Prepend a single bit to the front of a bitvector, using big endian order (see `append`).
|
||||
That is, the new bit is the most significant bit. -/
|
||||
/--
|
||||
Prepends a single bit to the front of a bitvector, using big-endian order (see `append`).
|
||||
|
||||
The new bit is the most significant bit.
|
||||
-/
|
||||
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
|
||||
((ofBool msb) ++ lsbs).cast (Nat.add_comm ..)
|
||||
|
||||
@@ -623,15 +660,18 @@ theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
|
||||
rfl
|
||||
|
||||
/--
|
||||
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise.
|
||||
That is, 2 to the power `i`.
|
||||
For the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
|
||||
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise. In other words, it is 2 to the
|
||||
power `i`.
|
||||
|
||||
From the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
|
||||
-/
|
||||
def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
|
||||
|
||||
end bitwise
|
||||
|
||||
/-- Compute a hash of a bitvector, combining 64-bit words using `mixHash`. -/
|
||||
/--
|
||||
Computes a hash of a bitvector, combining 64-bit words using `mixHash`.
|
||||
-/
|
||||
def hash (bv : BitVec n) : UInt64 :=
|
||||
if n ≤ 64 then
|
||||
bv.toFin.val.toUInt64
|
||||
@@ -659,14 +699,80 @@ section normalization_eqs
|
||||
@[simp] theorem zero_eq : BitVec.zero n = 0#n := rfl
|
||||
end normalization_eqs
|
||||
|
||||
/-- Converts a list of `Bool`s to a big-endian `BitVec`. -/
|
||||
/-- Converts a list of `Bool`s into a big-endian `BitVec`. -/
|
||||
def ofBoolListBE : (bs : List Bool) → BitVec bs.length
|
||||
| [] => 0#0
|
||||
| b :: bs => cons b (ofBoolListBE bs)
|
||||
|
||||
/-- Converts a list of `Bool`s to a little-endian `BitVec`. -/
|
||||
/-- Converts a list of `Bool`s into a little-endian `BitVec`. -/
|
||||
def ofBoolListLE : (bs : List Bool) → BitVec bs.length
|
||||
| [] => 0#0
|
||||
| b :: bs => concat (ofBoolListLE bs) b
|
||||
|
||||
/-! ## Overflow -/
|
||||
|
||||
/--
|
||||
Checks whether addition of `x` and `y` results in *unsigned* overflow.
|
||||
|
||||
SMT-LIB name: `bvuaddo`.
|
||||
-/
|
||||
def uaddOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat + y.toNat ≥ 2 ^ w
|
||||
|
||||
/--
|
||||
Checks whether addition of `x` and `y` results in *signed* overflow, treating `x` and `y` as 2's
|
||||
complement signed bitvectors.
|
||||
|
||||
SMT-LIB name: `bvsaddo`.
|
||||
-/
|
||||
def saddOverflow {w : Nat} (x y : BitVec w) : Bool :=
|
||||
(x.toInt + y.toInt ≥ 2 ^ (w - 1)) || (x.toInt + y.toInt < - 2 ^ (w - 1))
|
||||
|
||||
/--
|
||||
Checks whether subtraction of `x` and `y` results in *unsigned* overflow.
|
||||
|
||||
SMT-Lib name: `bvusubo`.
|
||||
-/
|
||||
def usubOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat < y.toNat
|
||||
|
||||
/--
|
||||
Checks whether the subtraction of `x` and `y` results in *signed* overflow, treating `x` and `y` as
|
||||
2's complement signed bitvectors.
|
||||
|
||||
SMT-Lib name: `bvssubo`.
|
||||
-/
|
||||
def ssubOverflow {w : Nat} (x y : BitVec w) : Bool :=
|
||||
(x.toInt - y.toInt ≥ 2 ^ (w - 1)) || (x.toInt - y.toInt < - 2 ^ (w - 1))
|
||||
|
||||
/--
|
||||
Checks whether the negation of a bitvector results in overflow.
|
||||
|
||||
For a bitvector `x` with nonzero width, this only happens if `x = intMin`.
|
||||
|
||||
SMT-Lib name: `bvnego`.
|
||||
-/
|
||||
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
|
||||
x.toInt == - 2 ^ (w - 1)
|
||||
|
||||
/- ### reverse -/
|
||||
|
||||
/-- Reverses the bits in a bitvector. -/
|
||||
def reverse : {w : Nat} → BitVec w → BitVec w
|
||||
| 0, x => x
|
||||
| w + 1, x => concat (reverse (x.truncate w)) (x.msb)
|
||||
|
||||
/-- `umulOverflow x y` returns `true` if multiplying `x` and `y` results in *unsigned* overflow.
|
||||
|
||||
SMT-Lib name: `bvumulo`.
|
||||
-/
|
||||
def umulOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat * y.toNat ≥ 2 ^ w
|
||||
|
||||
/-- `smulOverflow x y` returns `true` if multiplying `x` and `y` results in *signed* overflow,
|
||||
treating `x` and `y` as 2's complement signed bitvectors.
|
||||
|
||||
SMT-Lib name: `bvsmulo`.
|
||||
-/
|
||||
|
||||
def smulOverflow {w : Nat} (x y : BitVec w) : Bool :=
|
||||
(x.toInt * y.toInt ≥ 2 ^ (w - 1)) || (x.toInt * y.toInt < - 2 ^ (w - 1))
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -17,7 +17,9 @@ namespace BitVec
|
||||
|
||||
section Nat
|
||||
|
||||
/-- The `BitVec` with value `i mod 2^n`. -/
|
||||
/--
|
||||
The bitvector with value `i mod 2^n`.
|
||||
-/
|
||||
@[match_pattern]
|
||||
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
|
||||
toFin := Fin.ofNat' (2^n) i
|
||||
@@ -32,17 +34,18 @@ end Nat
|
||||
section arithmetic
|
||||
|
||||
/--
|
||||
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
|
||||
modulo `2^n`.
|
||||
Adds two bitvectors. This can be interpreted as either signed or unsigned addition modulo `2^n`.
|
||||
Usually accessed via the `+` operator.
|
||||
|
||||
SMT-Lib name: `bvadd`.
|
||||
SMT-LIB name: `bvadd`.
|
||||
-/
|
||||
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
|
||||
instance : Add (BitVec n) := ⟨BitVec.add⟩
|
||||
|
||||
/--
|
||||
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
|
||||
modulo `2^n`.
|
||||
Subtracts one bitvector from another. This can be interpreted as either signed or unsigned subtraction
|
||||
modulo `2^n`. Usually accessed via the `-` operator.
|
||||
|
||||
-/
|
||||
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
|
||||
instance : Sub (BitVec n) := ⟨BitVec.sub⟩
|
||||
|
||||
@@ -6,9 +6,11 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
|
||||
prelude
|
||||
import Init.Data.BitVec.Folds
|
||||
import Init.Data.Nat.Mod
|
||||
import Init.Data.Int.LemmasAux
|
||||
import Init.Data.BitVec.Lemmas
|
||||
|
||||
/-!
|
||||
# Bitblasting of bitvectors
|
||||
# Bit blasting of bitvectors
|
||||
|
||||
This module provides theorems for showing the equivalence between BitVec operations using
|
||||
the `Fin 2^n` representation and Boolean vectors. It is still under development, but
|
||||
@@ -18,21 +20,21 @@ as vectors of bits into proofs about Lean `BitVec` values.
|
||||
The module is named for the bit-blasting operation in an SMT solver that converts bitvector
|
||||
expressions into expressions about individual bits in each vector.
|
||||
|
||||
### Example: How bitblasting works for multiplication
|
||||
### Example: How bit blasting works for multiplication
|
||||
|
||||
We explain how the lemmas here are used for bitblasting,
|
||||
We explain how the lemmas here are used for bit blasting,
|
||||
by using multiplication as a prototypical example.
|
||||
Other bitblasters for other operations follow the same pattern.
|
||||
To bitblast a multiplication of the form `x * y`,
|
||||
Other bit blasters for other operations follow the same pattern.
|
||||
To bit blast a multiplication of the form `x * y`,
|
||||
we must unfold the above into a form that the SAT solver understands.
|
||||
|
||||
We assume that the solver already knows how to bitblast addition.
|
||||
We assume that the solver already knows how to bit blast addition.
|
||||
This is known to `bv_decide`, by exploiting the lemma `add_eq_adc`,
|
||||
which says that `x + y : BitVec w` equals `(adc x y false).2`,
|
||||
where `adc` builds an add-carry circuit in terms of the primitive operations
|
||||
(bitwise and, bitwise or, bitwise xor) that bv_decide already understands.
|
||||
In this way, we layer bitblasters on top of each other,
|
||||
by reducing the multiplication bitblaster to an addition operation.
|
||||
In this way, we layer bit blasters on top of each other,
|
||||
by reducing the multiplication bit blaster to an addition operation.
|
||||
|
||||
The core lemma is given by `getLsbD_mul`:
|
||||
|
||||
@@ -64,7 +66,7 @@ mulRec_succ_eq
|
||||
By repeatedly applying the lemmas `mulRec_zero_eq` and `mulRec_succ_eq`,
|
||||
one obtains a circuit for multiplication.
|
||||
Note that this circuit uses `BitVec.add`, `BitVec.getLsbD`, `BitVec.shiftLeft`.
|
||||
Here, `BitVec.add` and `BitVec.shiftLeft` are (recursively) bitblasted by `bv_decide`,
|
||||
Here, `BitVec.add` and `BitVec.shiftLeft` are (recursively) bit blasted by `bv_decide`,
|
||||
using the lemmas `add_eq_adc` and `shiftLeft_eq_shiftLeftRec`,
|
||||
and `BitVec.getLsbD` is a primitive that `bv_decide` knows how to reduce to SAT.
|
||||
|
||||
@@ -87,10 +89,10 @@ computes the correct value for multiplication.
|
||||
|
||||
To zoom out, therefore, we follow two steps:
|
||||
First, we prove bitvector lemmas to unfold a high-level operation (such as multiplication)
|
||||
into already bitblastable operations (such as addition and left shift).
|
||||
into already bit blastable operations (such as addition and left shift).
|
||||
We then use these lemmas to prove the correctness of the circuit that `bv_decide` builds.
|
||||
|
||||
We use this workflow to implement bitblasting for all SMT-LIB2 operations.
|
||||
We use this workflow to implement bit blasting for all SMT-LIB v2 operations.
|
||||
|
||||
## Main results
|
||||
* `x + y : BitVec w` is `(adc x y false).2`.
|
||||
@@ -108,7 +110,12 @@ open Nat Bool
|
||||
|
||||
namespace Bool
|
||||
|
||||
/-- At least two out of three booleans are true. -/
|
||||
/--
|
||||
At least two out of three Booleans are true.
|
||||
|
||||
This function is typically used to model addition of binary numbers, to combine a carry bit with two
|
||||
addend bits.
|
||||
-/
|
||||
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
|
||||
|
||||
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
|
||||
@@ -128,14 +135,14 @@ private theorem testBit_limit {x i : Nat} (x_lt_succ : x < 2^(i+1)) :
|
||||
testBit x i = decide (x ≥ 2^i) := by
|
||||
cases xi : testBit x i with
|
||||
| true =>
|
||||
simp [testBit_implies_ge xi]
|
||||
simp [Nat.ge_two_pow_of_testBit xi]
|
||||
| false =>
|
||||
simp
|
||||
cases Nat.lt_or_ge x (2^i) with
|
||||
| inl x_lt =>
|
||||
exact x_lt
|
||||
| inr x_ge =>
|
||||
have ⟨j, ⟨j_ge, jp⟩⟩ := ge_two_pow_implies_high_bit_true x_ge
|
||||
have ⟨j, ⟨j_ge, jp⟩⟩ := exists_ge_and_testBit_of_ge_two_pow x_ge
|
||||
cases Nat.lt_or_eq_of_le j_ge with
|
||||
| inr x_eq =>
|
||||
simp [x_eq, jp] at xi
|
||||
@@ -143,8 +150,8 @@ private theorem testBit_limit {x i : Nat} (x_lt_succ : x < 2^(i+1)) :
|
||||
exfalso
|
||||
apply Nat.lt_irrefl
|
||||
calc x < 2^(i+1) := x_lt_succ
|
||||
_ ≤ 2 ^ j := Nat.pow_le_pow_of_le_right Nat.zero_lt_two x_lt
|
||||
_ ≤ x := testBit_implies_ge jp
|
||||
_ ≤ 2 ^ j := Nat.pow_le_pow_right Nat.zero_lt_two x_lt
|
||||
_ ≤ x := ge_two_pow_of_testBit jp
|
||||
|
||||
private theorem mod_two_pow_succ (x i : Nat) :
|
||||
x % 2^(i+1) = 2^i*(x.testBit i).toNat + x % (2 ^ i):= by
|
||||
@@ -255,7 +262,7 @@ theorem getLsbD_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool
|
||||
Nat.add_left_comm (_%_) (_ * _) _,
|
||||
testBit_limit (mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ x y i c)
|
||||
]
|
||||
simp [testBit_to_div_mod, carry, Nat.add_assoc]
|
||||
simp [testBit_eq_decide_div_mod_eq, carry, Nat.add_assoc]
|
||||
|
||||
theorem getLsbD_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
|
||||
getLsbD (x + y) i =
|
||||
@@ -284,7 +291,7 @@ theorem adc_spec (x y : BitVec w) (c : Bool) :
|
||||
simp [carry, Nat.mod_one]
|
||||
cases c <;> rfl
|
||||
case step =>
|
||||
simp [adcb, Prod.mk.injEq, carry_succ, getLsbD_add_add_bool]
|
||||
simp [adcb, Prod.mk.injEq, carry_succ, getElem_add_add_bool]
|
||||
|
||||
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
|
||||
simp [adc_spec]
|
||||
@@ -294,7 +301,7 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
|
||||
theorem getMsbD_add {i : Nat} {i_lt : i < w} {x y : BitVec w} :
|
||||
getMsbD (x + y) i =
|
||||
Bool.xor (getMsbD x i) (Bool.xor (getMsbD y i) (carry (w - 1 - i) x y false)) := by
|
||||
simp [getMsbD, getLsbD_add, i_lt, show w - 1 - i < w by omega]
|
||||
simp [getMsbD, getElem_add, i_lt, show w - 1 - i < w by omega]
|
||||
|
||||
theorem msb_add {w : Nat} {x y: BitVec w} :
|
||||
(x + y).msb =
|
||||
@@ -336,13 +343,13 @@ theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
|
||||
theorem getLsbD_sub {i : Nat} {i_lt : i < w} {x y : BitVec w} :
|
||||
(x - y).getLsbD i
|
||||
= (x.getLsbD i ^^ ((~~~y + 1#w).getLsbD i ^^ carry i x (~~~y + 1#w) false)) := by
|
||||
rw [sub_toAdd, BitVec.neg_eq_not_add, getLsbD_add]
|
||||
rw [sub_eq_add_neg, BitVec.neg_eq_not_add, getLsbD_add]
|
||||
omega
|
||||
|
||||
theorem getMsbD_sub {i : Nat} {i_lt : i < w} {x y : BitVec w} :
|
||||
(x - y).getMsbD i =
|
||||
(x.getMsbD i ^^ ((~~~y + 1).getMsbD i ^^ carry (w - 1 - i) x (~~~y + 1) false)) := by
|
||||
rw [sub_toAdd, neg_eq_not_add, getMsbD_add]
|
||||
rw [sub_eq_add_neg, neg_eq_not_add, getMsbD_add]
|
||||
· rfl
|
||||
· omega
|
||||
|
||||
@@ -353,31 +360,32 @@ theorem getElem_sub {i : Nat} {x y : BitVec w} (h : i < w) :
|
||||
theorem msb_sub {x y: BitVec w} :
|
||||
(x - y).msb
|
||||
= (x.msb ^^ ((~~~y + 1#w).msb ^^ carry (w - 1 - 0) x (~~~y + 1#w) false)) := by
|
||||
simp [sub_toAdd, BitVec.neg_eq_not_add, msb_add]
|
||||
simp [sub_eq_add_neg, BitVec.neg_eq_not_add, msb_add]
|
||||
|
||||
/-! ### Negation -/
|
||||
|
||||
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
|
||||
getLsbD (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) i.val = !(getLsbD x i.val) := by
|
||||
(((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd)[i.val] = !(getLsbD x i.val) := by
|
||||
apply iunfoldr_getLsbD (fun _ => ()) i (by simp)
|
||||
|
||||
theorem bit_not_add_self (x : BitVec w) :
|
||||
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd + x = -1 := by
|
||||
((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd + x = -1 := by
|
||||
simp only [add_eq_adc]
|
||||
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
|
||||
intro i; simp only [ BitVec.not, adcb, testBit_toNat]
|
||||
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd)]
|
||||
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsbD_allOnes]
|
||||
intro i; simp only [adcb, Fin.is_lt, getLsbD_eq_getElem, atLeastTwo_false_right, bne_false,
|
||||
ofNat_eq_ofNat, Fin.getElem_fin, Prod.mk.injEq, and_eq_false_imp]
|
||||
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x[i.val])))) ()).snd)]
|
||||
<;> simp [bit_not_testBit, neg_one_eq_allOnes, getElem_allOnes]
|
||||
|
||||
theorem bit_not_eq_not (x : BitVec w) :
|
||||
((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd = ~~~ x := by
|
||||
simp [←allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), ←negOne_eq_allOnes]
|
||||
((iunfoldr (fun i c => (c, !(x[i])))) ()).snd = ~~~ x := by
|
||||
simp [←allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), ←neg_one_eq_allOnes]
|
||||
|
||||
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
|
||||
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
|
||||
simp only [← add_eq_adc]
|
||||
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) _ rfl]
|
||||
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_toAdd, BitVec.add_comm _ (-x)]
|
||||
simp [← sub_toAdd, BitVec.sub_add_cancel]
|
||||
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) _ rfl]
|
||||
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_eq_add_neg, BitVec.add_comm _ (-x)]
|
||||
simp [← sub_eq_add_neg, BitVec.sub_add_cancel]
|
||||
· simp [bit_not_testBit x _]
|
||||
|
||||
/--
|
||||
@@ -412,7 +420,7 @@ theorem getLsbD_neg {i : Nat} {x : BitVec w} :
|
||||
· rintro h j hj; exact And.right <| h j (by omega)
|
||||
· rintro h j hj; exact ⟨by omega, h j (by omega)⟩
|
||||
· have h_ge : w ≤ i := by omega
|
||||
simp [getLsbD_ge _ _ h_ge, h_ge, hi]
|
||||
simp [getLsbD_of_ge _ _ h_ge, h_ge, hi]
|
||||
|
||||
theorem getElem_neg {i : Nat} {x : BitVec w} (h : i < w) :
|
||||
(-x)[i] = (x[i] ^^ decide (∃ j < i, x.getLsbD j = true)) := by
|
||||
@@ -476,6 +484,39 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
|
||||
case zero => exact hmsb
|
||||
case succ => exact getMsbD_x _ hi (by omega)
|
||||
|
||||
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_ne_intMin`. -/
|
||||
@[simp] theorem signExtend_neg_of_le {v w : Nat} (h : w ≤ v) (b : BitVec v) :
|
||||
(-b).signExtend w = -b.signExtend w := by
|
||||
apply BitVec.eq_of_getElem_eq
|
||||
intro i hi
|
||||
simp only [getElem_signExtend, getElem_neg]
|
||||
rw [dif_pos (by omega), dif_pos (by omega)]
|
||||
simp only [getLsbD_signExtend, Bool.and_eq_true, decide_eq_true_eq, Bool.ite_eq_true_distrib,
|
||||
Bool.bne_right_inj, decide_eq_decide]
|
||||
exact ⟨fun ⟨j, hj₁, hj₂⟩ => ⟨j, ⟨hj₁, ⟨by omega, by rwa [if_pos (by omega)]⟩⟩⟩,
|
||||
fun ⟨j, hj₁, hj₂, hj₃⟩ => ⟨j, hj₁, by rwa [if_pos (by omega)] at hj₃⟩⟩
|
||||
|
||||
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_le`. -/
|
||||
@[simp] theorem signExtend_neg_of_ne_intMin {v w : Nat} (b : BitVec v) (hb : b ≠ intMin v) :
|
||||
(-b).signExtend w = -b.signExtend w := by
|
||||
refine (by omega : w ≤ v ∨ v < w).elim (fun h => signExtend_neg_of_le h b) (fun h => ?_)
|
||||
apply BitVec.eq_of_toInt_eq
|
||||
rw [toInt_signExtend_of_le (by omega), toInt_neg_of_ne_intMin hb, toInt_neg_of_ne_intMin,
|
||||
toInt_signExtend_of_le (by omega)]
|
||||
apply ne_of_apply_ne BitVec.toInt
|
||||
rw [toInt_signExtend_of_le (by omega), toInt_intMin_of_pos (by omega)]
|
||||
have := b.le_two_mul_toInt
|
||||
have : -2 ^ w < -2 ^ v := by
|
||||
apply Int.neg_lt_neg
|
||||
norm_cast
|
||||
rwa [Nat.pow_lt_pow_iff_right (by omega)]
|
||||
have : 2 * b.toInt ≠ -2 ^ w := by omega
|
||||
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
|
||||
omega
|
||||
|
||||
@[simp] theorem BitVec.setWidth_neg_of_le {x : BitVec v} (h : w ≤ v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
|
||||
simp [← BitVec.signExtend_eq_setWidth_of_le _ h, BitVec.signExtend_neg_of_le h]
|
||||
|
||||
/-! ### abs -/
|
||||
|
||||
theorem msb_abs {w : Nat} {x : BitVec w} :
|
||||
@@ -527,30 +568,64 @@ theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
|
||||
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
|
||||
cases y.msb <;> (simp; omega)
|
||||
|
||||
theorem slt_eq_ult (x y : BitVec w) :
|
||||
theorem slt_eq_ult {x y : BitVec w} :
|
||||
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
|
||||
by_cases h : x.msb = y.msb
|
||||
· simp [h, slt_eq_ult_of_msb_eq]
|
||||
· have h' : x.msb != y.msb := by simp_all
|
||||
simp [slt_eq_not_ult_of_msb_neq h, h']
|
||||
|
||||
theorem slt_eq_not_carry (x y : BitVec w) :
|
||||
theorem slt_eq_not_carry {x y : BitVec w} :
|
||||
x.slt y = (x.msb == y.msb).xor (carry w x (~~~y) true) := by
|
||||
simp only [slt_eq_ult, bne, ult_eq_not_carry]
|
||||
cases x.msb == y.msb <;> simp
|
||||
|
||||
theorem sle_eq_not_slt (x y : BitVec w) : x.sle y = !y.slt x := by
|
||||
theorem sle_eq_not_slt {x y : BitVec w} : x.sle y = !y.slt x := by
|
||||
simp only [BitVec.sle, BitVec.slt, ← decide_not, decide_eq_decide]; omega
|
||||
|
||||
theorem sle_eq_carry (x y : BitVec w) :
|
||||
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
|
||||
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
|
||||
|
||||
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x ↔ x.msb = false := by
|
||||
simp [zero_sle_eq_not_msb]
|
||||
|
||||
theorem toNat_toInt_of_sle {w : Nat} {x : BitVec w} (hx : BitVec.sle 0#w x) : x.toInt.toNat = x.toNat :=
|
||||
toNat_toInt_of_msb x (zero_sle_iff_msb_eq_false.1 hx)
|
||||
|
||||
theorem sle_eq_carry {x y : BitVec w} :
|
||||
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
|
||||
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
|
||||
|
||||
/-! ### mul recurrence for bitblasting -/
|
||||
theorem neg_slt_zero (h : 0 < w) {x : BitVec w} :
|
||||
(-x).slt 0#w = ((x == intMin w) || (0#w).slt x) := by
|
||||
rw [slt_zero_eq_msb, msb_neg, slt_eq_sle_and_ne, zero_sle_eq_not_msb]
|
||||
apply Bool.eq_iff_iff.2
|
||||
cases hmsb : x.msb with
|
||||
| false => simpa [ne_intMin_of_msb_eq_false h hmsb] using Decidable.not_iff_not.2 (eq_comm)
|
||||
| true =>
|
||||
simp only [Bool.bne_true, Bool.not_and, Bool.or_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true,
|
||||
bne_eq_false_iff_eq, Bool.false_and, Bool.or_false, beq_iff_eq,
|
||||
_root_.or_iff_right_iff_imp]
|
||||
rintro rfl
|
||||
simp at hmsb
|
||||
|
||||
theorem neg_sle_zero (h : 0 < w) {x : BitVec w} :
|
||||
(-x).sle 0#w = (x == intMin w || (0#w).sle x) := by
|
||||
rw [sle_eq_slt_or_eq, neg_slt_zero h, sle_eq_slt_or_eq]
|
||||
simp [Bool.beq_eq_decide_eq (-x), Bool.beq_eq_decide_eq _ x, Eq.comm (a := x), Bool.or_assoc]
|
||||
|
||||
theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := by
|
||||
rw [sle_eq_not_slt, slt_eq_ult, ← Bool.xor_not, ← ule_eq_not_ult, bne_comm]
|
||||
|
||||
theorem sle_eq_ule_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) : x.sle y = x.ule y := by
|
||||
simp [BitVec.sle_eq_ule, h]
|
||||
|
||||
/-! ### mul recurrence for bit blasting -/
|
||||
|
||||
/--
|
||||
A recurrence that describes multiplication as repeated addition.
|
||||
Is useful for bitblasting multiplication.
|
||||
|
||||
This function is useful for bit blasting multiplication.
|
||||
-/
|
||||
def mulRec (x y : BitVec w) (s : Nat) : BitVec w :=
|
||||
let cur := if y.getLsbD s then (x <<< s) else 0
|
||||
@@ -574,16 +649,18 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i
|
||||
setWidth w (x.setWidth i) + (x &&& twoPow w i) := by
|
||||
rw [add_eq_or_of_and_eq_zero]
|
||||
· ext k h
|
||||
simp only [getLsbD_setWidth, h, decide_true, Bool.true_and, getLsbD_or, getLsbD_and]
|
||||
simp only [getElem_setWidth, getLsbD_setWidth, h, getLsbD_eq_getElem, getElem_or, getElem_and,
|
||||
getElem_twoPow]
|
||||
by_cases hik : i = k
|
||||
· subst hik
|
||||
simp [h]
|
||||
· simp only [getLsbD_twoPow, hik, decide_false, Bool.and_false, Bool.or_false]
|
||||
by_cases hik' : k < (i + 1)
|
||||
· by_cases hik' : k < (i + 1)
|
||||
· have hik'' : k < i := by omega
|
||||
simp [hik', hik'']
|
||||
omega
|
||||
· have hik'' : ¬ (k < i) := by omega
|
||||
simp [hik', hik'']
|
||||
omega
|
||||
· ext k
|
||||
simp only [and_twoPow, getLsbD_and, getLsbD_setWidth, Fin.is_lt, decide_true, Bool.true_and,
|
||||
getLsbD_zero, and_eq_false_imp, and_eq_true, decide_eq_true_eq, and_imp]
|
||||
@@ -597,7 +674,7 @@ abbrev zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow :=
|
||||
/--
|
||||
Recurrence lemma: multiplying `x` with the first `s` bits of `y` is the
|
||||
same as truncating `y` to `s` bits, then zero extending to the original length,
|
||||
and performing the multplication. -/
|
||||
and performing the multiplication. -/
|
||||
theorem mulRec_eq_mul_signExtend_setWidth (x y : BitVec w) (s : Nat) :
|
||||
mulRec x y s = x * ((y.setWidth (s + 1)).setWidth w) := by
|
||||
induction s
|
||||
@@ -631,19 +708,33 @@ theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
· simp
|
||||
· omega
|
||||
|
||||
theorem mul_eq_mulRec {x y : BitVec w} :
|
||||
x * y = mulRec x y w := by
|
||||
apply eq_of_getLsbD_eq
|
||||
intro i hi
|
||||
apply getLsbD_mul
|
||||
|
||||
theorem getMsbD_mul (x y : BitVec w) (i : Nat) :
|
||||
(x * y).getMsbD i = (mulRec x y w).getMsbD i := by
|
||||
simp only [mulRec_eq_mul_signExtend_setWidth]
|
||||
rw [setWidth_setWidth_of_le]
|
||||
· simp
|
||||
· omega
|
||||
|
||||
theorem getElem_mul {x y : BitVec w} {i : Nat} (h : i < w) :
|
||||
(x * y)[i] = (mulRec x y w)[i] := by
|
||||
simp [mulRec_eq_mul_signExtend_setWidth]
|
||||
|
||||
/-! ## shiftLeft recurrence for bitblasting -/
|
||||
/-! ## shiftLeft recurrence for bit blasting -/
|
||||
|
||||
/--
|
||||
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
|
||||
Shifts `x` to the left by the first `n` bits of `y`.
|
||||
|
||||
The theorem `shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and `shiftLeftRec`.
|
||||
The theorem `BitVec.shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and
|
||||
`BitVec.shiftLeftRec x y`.
|
||||
|
||||
Together with equations `shiftLeftRec_zero`, `shiftLeftRec_succ`,
|
||||
this allows us to unfold `shiftLeft` into a circuit for bitblasting.
|
||||
Together with equations `BitVec.shiftLeftRec_zero` and `BitVec.shiftLeftRec_succ`, this allows
|
||||
`BitVec.shiftLeft` to be unfolded into a circuit for bit blasting.
|
||||
-/
|
||||
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
|
||||
let shiftAmt := (y &&& (twoPow w₂ n))
|
||||
@@ -697,7 +788,7 @@ theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
|
||||
|
||||
/--
|
||||
Show that `x <<< y` can be written in terms of `shiftLeftRec`.
|
||||
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bitblasting.
|
||||
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bit blasting.
|
||||
-/
|
||||
theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
x <<< y = shiftLeftRec x y (w₂ - 1) := by
|
||||
@@ -705,7 +796,7 @@ theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
· simp [of_length_zero]
|
||||
· simp [shiftLeftRec_eq]
|
||||
|
||||
/-! # udiv/urem recurrence for bitblasting
|
||||
/-! # udiv/urem recurrence for bit blasting
|
||||
|
||||
In order to prove the correctness of the division algorithm on the integers,
|
||||
one shows that `n.div d = q` and `n.mod d = r` iff `n = d * q + r` and `0 ≤ r < d`.
|
||||
@@ -896,7 +987,7 @@ The input to the shift subtractor is a legal input to `divrem`, and we also need
|
||||
input bit to perform shift subtraction on, and thus we need `0 < wn`.
|
||||
-/
|
||||
structure DivModState.Poised {w : Nat} (args : DivModArgs w) (qr : DivModState w)
|
||||
extends DivModState.Lawful args qr : Type where
|
||||
extends DivModState.Lawful args qr where
|
||||
/-- Only perform a round of shift-subtract if we have dividend bits. -/
|
||||
hwn_lt : 0 < qr.wn
|
||||
|
||||
@@ -912,8 +1003,9 @@ def DivModState.wr_lt_w {qr : DivModState w} (h : qr.Poised args) : qr.wr < w :=
|
||||
/-! ### Division shift subtractor -/
|
||||
|
||||
/--
|
||||
One round of the division algorithm, that tries to perform a subtract shift.
|
||||
Note that this should only be called when `r.msb = false`, so we will not overflow.
|
||||
One round of the division algorithm. It tries to perform a subtract shift.
|
||||
|
||||
This should only be called when `r.msb = false`, so it will not overflow.
|
||||
-/
|
||||
def divSubtractShift (args : DivModArgs w) (qr : DivModState w) : DivModState w :=
|
||||
let {n, d} := args
|
||||
@@ -1003,7 +1095,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
|
||||
|
||||
/-! ### Core division algorithm circuit -/
|
||||
|
||||
/-- A recursive definition of division for bitblasting, in terms of a shift-subtraction circuit. -/
|
||||
/-- A recursive definition of division for bit blasting, in terms of a shift-subtraction circuit. -/
|
||||
def divRec {w : Nat} (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
DivModState w :=
|
||||
match m with
|
||||
@@ -1023,11 +1115,10 @@ theorem divRec_succ (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
theorem lawful_divRec {args : DivModArgs w} {qr : DivModState w}
|
||||
(h : DivModState.Lawful args qr) :
|
||||
DivModState.Lawful args (divRec qr.wn args qr) := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
induction hm : qr.wn generalizing qr with
|
||||
| zero =>
|
||||
exact h
|
||||
case succ wn' ih =>
|
||||
| succ wn' ih =>
|
||||
simp only [divRec_succ]
|
||||
apply ih
|
||||
· apply lawful_divSubtractShift
|
||||
@@ -1041,11 +1132,10 @@ theorem lawful_divRec {args : DivModArgs w} {qr : DivModState w}
|
||||
@[simp]
|
||||
theorem wn_divRec (args : DivModArgs w) (qr : DivModState w) :
|
||||
(divRec qr.wn args qr).wn = 0 := by
|
||||
generalize hm : qr.wn = m
|
||||
induction m generalizing qr
|
||||
case zero =>
|
||||
induction hm : qr.wn generalizing qr with
|
||||
| zero =>
|
||||
assumption
|
||||
case succ wn' ih =>
|
||||
| succ wn' ih =>
|
||||
apply ih
|
||||
simp only [divSubtractShift, hm]
|
||||
split <;> rfl
|
||||
@@ -1084,13 +1174,30 @@ theorem divRec_succ' (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
|
||||
divRec m args input := by
|
||||
simp [divRec_succ, divSubtractShift]
|
||||
|
||||
theorem getElem_udiv (n d : BitVec w) (hy : 0#w < d) (i : Nat) (hi : i < w) :
|
||||
(n / d)[i] = (divRec w {n, d} (DivModState.init w)).q[i] := by
|
||||
rw [udiv_eq_divRec (by assumption)]
|
||||
|
||||
theorem getLsbD_udiv (n d : BitVec w) (hy : 0#w < d) (i : Nat) :
|
||||
(n / d).getLsbD i = (decide (i < w) && (divRec w {n, d} (DivModState.init w)).q.getLsbD i) := by
|
||||
by_cases hi : i < w
|
||||
· simp [udiv_eq_divRec (by assumption)]
|
||||
omega
|
||||
· simp_all
|
||||
|
||||
theorem getMsbD_udiv (n d : BitVec w) (hd : 0#w < d) (i : Nat) :
|
||||
(n / d).getMsbD i = (decide (i < w) && (divRec w {n, d} (DivModState.init w)).q.getMsbD i) := by
|
||||
simp [getMsbD_eq_getLsbD, getLsbD_udiv, udiv_eq_divRec (by assumption)]
|
||||
|
||||
/- ### Arithmetic shift right (sshiftRight) recurrence -/
|
||||
|
||||
/--
|
||||
`sshiftRightRec x y n` shifts `x` arithmetically/signed to the right by the first `n` bits of `y`.
|
||||
The theorem `sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and `sshiftRightRec`.
|
||||
Together with equations `sshiftRightRec_zero`, `sshiftRightRec_succ`,
|
||||
this allows us to unfold `sshiftRight` into a circuit for bitblasting.
|
||||
Shifts `x` arithmetically (signed) to the right by the first `n` bits of `y`.
|
||||
|
||||
The theorem `BitVec.sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and
|
||||
`BitVec.sshiftRightRec x y`. Together with equations `BitVec.sshiftRightRec_zero`, and
|
||||
`BitVec.sshiftRightRec_succ`, this allows `BitVec.sshiftRight` to be unfolded into a circuit for
|
||||
bit blasting.
|
||||
-/
|
||||
def sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
|
||||
let shiftAmt := (y &&& (twoPow w₂ n))
|
||||
@@ -1137,7 +1244,7 @@ theorem sshiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
|
||||
|
||||
/--
|
||||
Show that `x.sshiftRight y` can be written in terms of `sshiftRightRec`.
|
||||
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bitblasting.
|
||||
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bit blasting.
|
||||
-/
|
||||
theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
(x.sshiftRight' y).getLsbD i = (sshiftRightRec x y (w₂ - 1)).getLsbD i := by
|
||||
@@ -1145,16 +1252,16 @@ theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
· simp [of_length_zero]
|
||||
· simp [sshiftRightRec_eq]
|
||||
|
||||
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
|
||||
/- ### Logical shift right (ushiftRight) recurrence for bit blasting -/
|
||||
|
||||
/--
|
||||
`ushiftRightRec x y n` shifts `x` logically to the right by the first `n` bits of `y`.
|
||||
Shifts `x` logically to the right by the first `n` bits of `y`.
|
||||
|
||||
The theorem `shiftRight_eq_ushiftRightRec` proves the equivalence
|
||||
of `(x >>> y)` and `ushiftRightRec`.
|
||||
The theorem `BitVec.shiftRight_eq_ushiftRightRec` proves the equivalence
|
||||
of `(x >>> y)` and `BitVec.ushiftRightRec`.
|
||||
|
||||
Together with equations `ushiftRightRec_zero`, `ushiftRightRec_succ`,
|
||||
this allows us to unfold `ushiftRight` into a circuit for bitblasting.
|
||||
Together with equations `BitVec.ushiftRightRec_zero` and `BitVec.ushiftRightRec_succ`,
|
||||
this allows `BitVec.ushiftRight` to be unfolded into a circuit for bit blasting.
|
||||
-/
|
||||
def ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
|
||||
let shiftAmt := (y &&& (twoPow w₂ n))
|
||||
@@ -1200,7 +1307,7 @@ theorem ushiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
|
||||
|
||||
/--
|
||||
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
|
||||
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bitblasting.
|
||||
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bit blasting.
|
||||
-/
|
||||
theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
x >>> y = ushiftRightRec x y (w₂ - 1) := by
|
||||
@@ -1208,4 +1315,485 @@ theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
· simp [of_length_zero]
|
||||
· simp [ushiftRightRec_eq]
|
||||
|
||||
/-! ### Overflow definitions -/
|
||||
|
||||
/-- Unsigned addition overflows iff the final carry bit of the addition circuit is `true`. -/
|
||||
theorem uaddOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
uaddOverflow x y = (x.setWidth (w + 1) + y.setWidth (w + 1)).msb := by
|
||||
simp [uaddOverflow, msb_add, msb_setWidth, carry]
|
||||
|
||||
theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
saddOverflow x y = (x.msb == y.msb && !((x + y).msb == x.msb)) := by
|
||||
simp only [saddOverflow]
|
||||
rcases w with _|w
|
||||
· revert x y; decide
|
||||
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x)
|
||||
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
|
||||
simp only [← decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, ← decide_not, ← decide_and,
|
||||
decide_eq_decide]
|
||||
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]
|
||||
simp
|
||||
omega
|
||||
|
||||
theorem usubOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
usubOverflow x y = decide (x < y) := rfl
|
||||
|
||||
theorem ssubOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
ssubOverflow x y = ((!x.msb && y.msb && (x - y).msb) || (x.msb && !y.msb && !(x - y).msb)) := by
|
||||
simp only [ssubOverflow]
|
||||
rcases w with _|w
|
||||
· simp [BitVec.of_length_zero]
|
||||
· have h₁ := BitVec.toInt_sub_toInt_lt_twoPow_iff (x := x) (y := y)
|
||||
have h₂ := BitVec.twoPow_le_toInt_sub_toInt_iff (x := x) (y := y)
|
||||
simp only [Nat.add_one_sub_one] at h₁ h₂
|
||||
simp only [Nat.add_one_sub_one, ge_iff_le, msb_eq_toInt, ← decide_not, Int.not_lt, toInt_sub]
|
||||
simp only [bool_to_prop]
|
||||
omega
|
||||
|
||||
theorem negOverflow_eq {w : Nat} (x : BitVec w) :
|
||||
(negOverflow x) = (decide (0 < w) && (x == intMin w)) := by
|
||||
simp only [negOverflow]
|
||||
rcases w with _|w
|
||||
· simp [toInt_of_zero_length, Int.min_eq_right]
|
||||
· suffices - 2 ^ w = (intMin (w + 1)).toInt by simp [beq_eq_decide_eq, ← toInt_inj, this]
|
||||
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod, Int.neg_inj]
|
||||
rw_mod_cast [Nat.mod_eq_of_lt (by simp [Nat.pow_lt_pow_succ])]
|
||||
|
||||
theorem umulOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
umulOverflow x y =
|
||||
(0 < w && BitVec.twoPow (w * 2) w ≤ x.zeroExtend (w * 2) * y.zeroExtend (w * 2)) := by
|
||||
simp only [umulOverflow, toNat_twoPow, le_def, toNat_mul, toNat_setWidth, mod_mul_mod]
|
||||
rcases w with _|w
|
||||
· simp [of_length_zero, toInt_zero, mul_mod_mod]
|
||||
· simp only [ge_iff_le, show 0 < w + 1 by omega, decide_true, mul_mod_mod, Bool.true_and,
|
||||
decide_eq_decide]
|
||||
rw [Nat.mod_eq_of_lt BitVec.toNat_mul_toNat_lt, Nat.mod_eq_of_lt]
|
||||
have := Nat.pow_lt_pow_of_lt (a := 2) (n := w + 1) (m := (w + 1) * 2)
|
||||
omega
|
||||
|
||||
theorem smulOverflow_eq {w : Nat} (x y : BitVec w) :
|
||||
smulOverflow x y =
|
||||
(0 < w &&
|
||||
((signExtend (w * 2) (intMax w)).slt (signExtend (w * 2) x * signExtend (w * 2) y) ||
|
||||
(signExtend (w * 2) x * signExtend (w * 2) y).slt (signExtend (w * 2) (intMin w)))) := by
|
||||
simp only [smulOverflow]
|
||||
rcases w with _|w
|
||||
· simp [of_length_zero, toInt_zero]
|
||||
· have h₁ := BitVec.two_pow_le_toInt_mul_toInt_iff (x := x) (y := y)
|
||||
have h₂ := BitVec.toInt_mul_toInt_lt_neg_two_pow_iff (x := x) (y := y)
|
||||
simp only [Nat.add_one_sub_one] at h₁ h₂
|
||||
simp [h₁, h₂]
|
||||
|
||||
/- ### umod -/
|
||||
|
||||
theorem getElem_umod {n d : BitVec w} (hi : i < w) :
|
||||
(n % d)[i]
|
||||
= if d = 0#w then n[i]
|
||||
else (divRec w { n := n, d := d } (DivModState.init w)).r[i] := by
|
||||
by_cases hd : d = 0#w
|
||||
· simp [hd]
|
||||
· have := (BitVec.not_le (x := d) (y := 0#w)).mp
|
||||
rw [← BitVec.umod_eq_divRec (by simp [hd, this])]
|
||||
simp [hd]
|
||||
|
||||
theorem getLsbD_umod {n d : BitVec w}:
|
||||
(n % d).getLsbD i
|
||||
= if d = 0#w then n.getLsbD i
|
||||
else (divRec w { n := n, d := d } (DivModState.init w)).r.getLsbD i := by
|
||||
by_cases hi : i < w
|
||||
· simp only [BitVec.getLsbD_eq_getElem hi, getElem_umod]
|
||||
· simp [show w ≤ i by omega]
|
||||
|
||||
theorem getMsbD_umod {n d : BitVec w}:
|
||||
(n % d).getMsbD i
|
||||
= if d = 0#w then n.getMsbD i
|
||||
else (divRec w { n := n, d := d } (DivModState.init w)).r.getMsbD i := by
|
||||
by_cases hi : i < w
|
||||
· rw [BitVec.getMsbD_eq_getLsbD, getLsbD_umod]
|
||||
simp [BitVec.getMsbD_eq_getLsbD, hi]
|
||||
· simp [show w ≤ i by omega]
|
||||
|
||||
|
||||
/-! ### Mappings to and from BitVec -/
|
||||
|
||||
theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x, g (f x) = x) :
|
||||
∀ x y, x = y ↔ f x = f y := by
|
||||
intro x y
|
||||
constructor
|
||||
· intro h'
|
||||
rw [h']
|
||||
· intro h'
|
||||
have := congrArg g h'
|
||||
simpa [h] using this
|
||||
|
||||
@[simp]
|
||||
theorem ne_intMin_of_lt_of_msb_false {x : BitVec w} (hw : 0 < w) (hx : x.msb = false) :
|
||||
x ≠ intMin w := by
|
||||
have := toNat_lt_of_msb_false hx
|
||||
simp [toNat_eq, Nat.two_pow_pred_mod_two_pow hw]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem ne_zero_of_msb_true {x : BitVec w} (hx : x.msb = true) :
|
||||
x ≠ 0#w := by
|
||||
have := Nat.two_pow_pos (w-1)
|
||||
have := le_toNat_of_msb_true hx
|
||||
simp [toNat_eq]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem msb_neg_of_ne_intMin_of_ne_zero {x : BitVec w} (h : x ≠ intMin w) (h' : x ≠ 0#w) :
|
||||
(-x).msb = !x.msb := by
|
||||
simp only [msb_neg, bool_to_prop]
|
||||
simp [h, h']
|
||||
|
||||
@[simp]
|
||||
theorem udiv_intMin_of_msb_false {x : BitVec w} (h : x.msb = false) :
|
||||
x / intMin w = 0#w := by
|
||||
by_cases hw : w = 0
|
||||
· subst hw
|
||||
decide +revert
|
||||
have wpos : 0 < w := by omega
|
||||
have := Nat.two_pow_pos (w-1)
|
||||
simp [toNat_eq, wpos]
|
||||
rw [Nat.div_eq_zero_iff_lt (by omega)]
|
||||
exact toNat_lt_of_msb_false h
|
||||
|
||||
theorem sdiv_intMin {x : BitVec w} :
|
||||
x.sdiv (intMin w) = if x = intMin w then 1#w else 0#w := by
|
||||
by_cases hw : w = 0
|
||||
· subst hw
|
||||
decide +revert
|
||||
have wpos : 0 < w := by omega
|
||||
by_cases h : x = intMin w
|
||||
· subst h
|
||||
simp
|
||||
omega
|
||||
· simp only [sdiv_eq, msb_intMin, show 0 < w by omega, h]
|
||||
have := Nat.two_pow_pos (w-1)
|
||||
by_cases hx : x.msb
|
||||
· simp [msb_neg_of_ne_intMin_of_ne_zero (by simp [h])
|
||||
(BitVec.ne_zero_of_msb_true hx), hx]
|
||||
· simp [hx]
|
||||
|
||||
theorem sdiv_neg {x y : BitVec w} (h : y ≠ intMin w) :
|
||||
x.sdiv (-y) = -(x.sdiv y) := by
|
||||
by_cases h' : y = 0#w
|
||||
· subst h'
|
||||
simp
|
||||
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [h'])]
|
||||
cases x.msb <;> cases y.msb <;> simp
|
||||
|
||||
theorem neg_sdiv {x y : BitVec w} (h : x ≠ intMin w) :
|
||||
(-x).sdiv y = -(x.sdiv y) := by
|
||||
by_cases hx0 : x = 0#w
|
||||
· subst hx0
|
||||
simp
|
||||
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0])]
|
||||
cases x.msb <;> cases y.msb <;> simp
|
||||
|
||||
theorem neg_sdiv_neg {x y : BitVec w} (h : x ≠ intMin w) :
|
||||
(-x).sdiv (-y) = x.sdiv y := by
|
||||
by_cases h' : y = intMin w
|
||||
· subst h'
|
||||
simp [sdiv_intMin, neg_intMin]
|
||||
· by_cases hy0 : y = 0#w
|
||||
· subst hy0
|
||||
simp
|
||||
· by_cases hx0 : x = 0#w
|
||||
· subst hx0
|
||||
simp
|
||||
· simp only [BitVec.sdiv,
|
||||
msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0]),
|
||||
msb_neg_of_ne_intMin_of_ne_zero h' (by simp [hy0])]
|
||||
cases x.msb <;> cases y.msb <;> simp
|
||||
|
||||
theorem intMin_eq_neg_two_pow : intMin w = BitVec.ofInt w (-2 ^ (w - 1)) := by
|
||||
apply BitVec.eq_of_toInt_eq
|
||||
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [BitVec.toInt_zero_length]) (fun hw => ?_)
|
||||
rw [BitVec.toInt_intMin_of_pos hw, BitVec.toInt_ofInt_eq_self hw (Int.le_refl _)]
|
||||
have := Nat.two_pow_pos (w - 1)
|
||||
norm_cast
|
||||
omega
|
||||
|
||||
theorem toInt_intMin_eq_bmod : (intMin w).toInt = (-2 ^ (w - 1)).bmod (2 ^ w) := by
|
||||
rw [intMin_eq_neg_two_pow, toInt_ofInt]
|
||||
|
||||
@[simp]
|
||||
theorem toInt_bmod_cancel(b : BitVec w) : b.toInt.bmod (2 ^ w) = b.toInt := by
|
||||
rw [toInt_eq_toNat_bmod, Int.bmod_bmod]
|
||||
|
||||
theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x ≠ intMin w) :
|
||||
x.sdiv y ≠ intMin w := by
|
||||
by_cases hw : w = 0
|
||||
· subst hw
|
||||
simp [BitVec.eq_nil x] at h
|
||||
contradiction
|
||||
simp only [sdiv, udiv_eq, neg_eq]
|
||||
by_cases hx : x.msb <;> by_cases hy : y.msb
|
||||
<;> simp only [hx, hy, neg_ne_intMin_inj]
|
||||
<;> simp only [Bool.not_eq_true] at hx hy
|
||||
<;> apply ne_intMin_of_lt_of_msb_false (by omega)
|
||||
<;> rw [msb_udiv]
|
||||
<;> try simp only [hx, Bool.false_and]
|
||||
· simp [h, ne_zero_of_msb_true, hx]
|
||||
· simp [h, ne_zero_of_msb_true, hx]
|
||||
|
||||
theorem toInt_eq_neg_toNat_neg_of_msb_true {x : BitVec w} (h : x.msb = true) :
|
||||
x.toInt = -((-x).toNat) := by
|
||||
simp only [toInt_eq_msb_cond, h, ↓reduceIte, toNat_neg, Int.ofNat_emod]
|
||||
norm_cast
|
||||
rw [Nat.mod_eq_of_lt]
|
||||
· omega
|
||||
· have := @BitVec.isLt w x
|
||||
have ne_zero := ne_zero_of_msb_true h
|
||||
simp only [ne_eq, toNat_eq, toNat_ofNat, zero_mod] at ne_zero
|
||||
omega
|
||||
|
||||
theorem toInt_eq_neg_toNat_neg_of_nonpos {x : BitVec w} (h : x = 0#w ∨ x.msb = true) :
|
||||
x.toInt = -((-x).toNat) := by
|
||||
cases h
|
||||
case inl h' =>
|
||||
simp [h']
|
||||
case inr h' =>
|
||||
simp [toInt_eq_neg_toNat_neg_of_msb_true h']
|
||||
|
||||
theorem intMin_udiv_eq_intMin_iff (x : BitVec w) :
|
||||
intMin w / x = intMin w ↔ x = 1#w := by
|
||||
by_cases hw : w = 0; subst hw; decide +revert
|
||||
by_cases hx : x = 1#w; subst hx; simp
|
||||
have wpos : 0 < w := by omega
|
||||
|
||||
have : 0 ≤ (2 ^ (w - 1) / x.toNat) := by simp
|
||||
have := Nat.two_pow_pos (w - 1)
|
||||
|
||||
constructor
|
||||
· intro h
|
||||
rw [← toInt_inj, toInt_eq_msb_cond] at h
|
||||
have : (intMin w / x).msb = false := by simp [msb_udiv, msb_intMin, wpos, hx]
|
||||
simp [this, wpos, toInt_intMin] at h
|
||||
omega
|
||||
· intro h
|
||||
subst h
|
||||
simp
|
||||
|
||||
theorem intMin_udiv_ne_zero_of_ne_zero {b : BitVec w} (hb : b.msb = false) (hb0 : b ≠ 0#w) :
|
||||
intMin w / b ≠ 0#w := by
|
||||
by_cases hw : w = 0; subst hw; decide +revert
|
||||
have wpos : 0 < w := by omega
|
||||
|
||||
simp [toNat_eq] at hb0
|
||||
have := @Nat.div_eq_zero_iff_lt b.toNat (2 ^ (w-1)) (by omega)
|
||||
have := toNat_lt_of_msb_false hb
|
||||
|
||||
simp [toNat_eq, wpos]
|
||||
omega
|
||||
|
||||
theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w ∨ b ≠ -1#w) :
|
||||
(a.sdiv b).toInt = a.toInt.tdiv b.toInt := by
|
||||
by_cases hw0 : w = 0; subst hw0; decide +revert
|
||||
by_cases hw1 : w = 1; subst hw1; decide +revert
|
||||
by_cases ha0 : a = 0#w; subst ha0; simp
|
||||
by_cases hb0 : b = 0#w; subst hb0; simp
|
||||
by_cases hb1 : b = 1#w; subst hb1; simp [show 1 < w by omega]
|
||||
|
||||
have wpos : 0 < w := by omega
|
||||
have := Nat.two_pow_pos (w - 1)
|
||||
|
||||
by_cases hbintMin : b = intMin w
|
||||
· simp only [ne_eq, Decidable.not_not] at hbintMin
|
||||
subst hbintMin
|
||||
have toIntA_lt := @BitVec.toInt_lt w a; norm_cast at toIntA_lt
|
||||
have le_toIntA := @BitVec.le_toInt w a; norm_cast at le_toIntA
|
||||
simp only [sdiv_intMin, h, ↓reduceIte, toInt_zero, toInt_intMin, wpos,
|
||||
Nat.two_pow_pred_mod_two_pow, Int.tdiv_neg]
|
||||
· by_cases ha_intMin : a = intMin w
|
||||
· simp only [ha_intMin, ↓reduceIte, show 1 < w by omega, toInt_one, toInt_intMin, wpos,
|
||||
Nat.two_pow_pred_mod_two_pow, Int.neg_tdiv, Int.neg_neg]
|
||||
rw [Int.tdiv_self (by omega)]
|
||||
· by_cases ha_nonneg : 0 ≤ a.toInt
|
||||
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin]
|
||||
· simp only [ne_eq, ← toInt_inj, toInt_intMin, wpos, Nat.two_pow_pred_mod_two_pow] at h
|
||||
rw [← Int.neg_tdiv, Int.tdiv_eq_zero_of_lt (by omega)]
|
||||
· simp [ha_intMin]
|
||||
· simp [wpos, ← toInt_ne, toInt_intMin] at ha_intMin
|
||||
omega
|
||||
|
||||
· by_cases ha : a.msb <;> by_cases hb : b.msb
|
||||
<;> simp only [not_eq_true] at ha hb
|
||||
· simp only [sdiv_eq, ha, hb, udiv_eq]
|
||||
rw [toInt_eq_neg_toNat_neg_of_nonpos (x := a) (by simp [ha]),
|
||||
toInt_eq_neg_toNat_neg_of_nonpos (x := b) (by simp [hb]),
|
||||
Int.neg_tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
|
||||
rw [toInt_eq_toNat_of_msb]
|
||||
· rfl
|
||||
· by_cases ha_intMin : a = intMin w
|
||||
· simp only [ha_intMin, ne_eq, not_true_eq_false, _root_.false_or] at h
|
||||
simp [msb_udiv, neg_eq_iff_eq_neg, h]
|
||||
· simp [msb_udiv, ha_intMin, ha]
|
||||
· have sdiv_toInt_of_msb_true_of_msb_false :
|
||||
(a.sdiv b).toInt = -((-a).toNat / b.toNat) := by
|
||||
simp only [sdiv_eq, ha, hb, udiv_eq]
|
||||
rw [toInt_eq_neg_toNat_neg_of_nonpos]
|
||||
· rw [neg_neg, toNat_udiv, toNat_neg, Int.ofNat_emod, Int.neg_inj]
|
||||
norm_cast
|
||||
· rw [neg_eq_zero_iff]
|
||||
by_cases h' : -a / b = 0#w
|
||||
· simp [h']
|
||||
· by_cases ha_intMin : a = intMin w
|
||||
· have ry := (intMin_udiv_eq_intMin_iff b).mp
|
||||
simp only [hb1, imp_false] at ry
|
||||
simp [msb_udiv, ha_intMin, hb1, ry, intMin_udiv_ne_zero_of_ne_zero, hb, hb0]
|
||||
· have := @BitVec.ne_intMin_of_lt_of_msb_false w ((-a) / b) wpos (by simp [ha, ha0, ha_intMin])
|
||||
simp [msb_neg, h', this, ha, ha_intMin]
|
||||
rw [toInt_eq_toNat_of_msb hb, toInt_eq_neg_toNat_neg_of_msb_true ha, Int.neg_tdiv,
|
||||
Int.tdiv_eq_ediv_of_nonneg (by omega), sdiv_toInt_of_msb_true_of_msb_false]
|
||||
· rw [← @BitVec.neg_neg w (a.sdiv b), ← sdiv_neg hbintMin]
|
||||
have hmb : (-b).msb = false := by simp [hbintMin, hb]
|
||||
rw [toInt_neg_of_ne_intMin]
|
||||
· simp [sdiv, ha, hmb]
|
||||
rw [toInt_udiv_of_msb ha, toInt_eq_toNat_of_msb ha]
|
||||
rw [toInt_eq_neg_toNat_neg_of_msb_true hb, Int.tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
|
||||
· apply sdiv_ne_intMin_of_ne_intMin
|
||||
apply ne_intMin_of_lt_of_msb_false (by omega) ha
|
||||
· rw [sdiv, Int.tdiv_cases, udiv_eq, neg_eq, if_pos (toInt_nonneg_of_msb_false ha),
|
||||
if_pos (toInt_nonneg_of_msb_false hb), ha, hb, toInt_udiv_of_msb ha,
|
||||
toInt_eq_toNat_of_msb ha, toInt_eq_toNat_of_msb hb]
|
||||
|
||||
theorem intMin_sdiv_neg_one : (intMin w).sdiv (-1#w) = intMin w := by
|
||||
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; exact Subsingleton.elim _ _) (fun hw => ?_)
|
||||
apply BitVec.eq_of_toNat_eq
|
||||
rw [sdiv]
|
||||
simp [msb_intMin, hw, neg_one_eq_allOnes, msb_allOnes]
|
||||
have : 2 ≤ 2 ^ w := Nat.pow_one 2 ▸ (Nat.pow_le_pow_iff_right (by omega)).2 (by omega)
|
||||
rw [Nat.sub_sub_self (by omega), Nat.mod_eq_of_lt, Nat.div_one]
|
||||
omega
|
||||
|
||||
theorem toInt_sdiv (a b : BitVec w) : (a.sdiv b).toInt = (a.toInt.tdiv b.toInt).bmod (2 ^ w) := by
|
||||
by_cases h : a = intMin w ∧ b = -1#w
|
||||
· rcases h with ⟨rfl, rfl⟩
|
||||
rw [BitVec.intMin_sdiv_neg_one]
|
||||
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [toInt_of_zero_length]) (fun hw => ?_)
|
||||
rw [toInt_intMin_of_pos hw, neg_one_eq_allOnes, toInt_allOnes, if_pos hw, Int.tdiv_neg,
|
||||
Int.tdiv_one, Int.neg_neg, Int.bmod_eq_neg (Int.pow_nonneg (by omega))]
|
||||
conv => lhs; rw [(by omega: w = (w - 1) + 1)]
|
||||
simp [Nat.pow_succ, Int.natCast_pow, Int.mul_comm]
|
||||
· rw [← toInt_bmod_cancel]
|
||||
rw [BitVec.toInt_sdiv_of_ne_or_ne _ _ (by simpa only [Decidable.not_and_iff_not_or_not] using h)]
|
||||
|
||||
theorem msb_umod_eq_false_of_left {x : BitVec w} (hx : x.msb = false) (y : BitVec w) : (x % y).msb = false := by
|
||||
rw [msb_eq_false_iff_two_mul_lt] at hx ⊢
|
||||
rw [toNat_umod]
|
||||
refine Nat.lt_of_le_of_lt ?_ hx
|
||||
rw [Nat.mul_le_mul_left_iff (by decide)]
|
||||
exact Nat.mod_le _ _
|
||||
|
||||
theorem msb_umod_of_le_of_ne_zero_of_le {x y : BitVec w}
|
||||
(hx : x ≤ intMin w) (hy : y ≠ 0#w) (hy' : y ≤ intMin w) : (x % y).msb = false := by
|
||||
simp only [msb_umod, Bool.and_eq_false_imp, Bool.or_eq_false_iff, decide_eq_false_iff_not,
|
||||
BitVec.not_lt, beq_eq_false_iff_ne, ne_eq, hy, not_false_eq_true, _root_.and_true]
|
||||
intro h
|
||||
rw [← intMin_le_iff_msb_eq_true (length_pos_of_ne hy)] at h
|
||||
rwa [BitVec.le_antisymm hx h]
|
||||
|
||||
@[simp]
|
||||
theorem toInt_srem (x y : BitVec w) : (x.srem y).toInt = x.toInt.tmod y.toInt := by
|
||||
rw [srem_eq]
|
||||
by_cases hyz : y = 0#w
|
||||
· simp only [hyz, ofNat_eq_ofNat, msb_zero, umod_zero, neg_zero, neg_neg, toInt_zero, Int.tmod_zero]
|
||||
cases x.msb <;> rfl
|
||||
cases h : x.msb
|
||||
· cases h' : y.msb
|
||||
· dsimp only
|
||||
rw [toInt_eq_toNat_of_msb (msb_umod_eq_false_of_left h y), toNat_umod]
|
||||
rw [toInt_eq_toNat_of_msb h, toInt_eq_toNat_of_msb h', ← Int.ofNat_tmod]
|
||||
· dsimp only
|
||||
rw [toInt_eq_toNat_of_msb (msb_umod_eq_false_of_left h _), toNat_umod]
|
||||
rw [toInt_eq_toNat_of_msb h, toInt_eq_neg_toNat_neg_of_msb_true h']
|
||||
rw [Int.tmod_neg, ← Int.ofNat_tmod]
|
||||
· cases h' : y.msb
|
||||
· dsimp only
|
||||
rw [toInt_eq_neg_toNat_neg_of_msb_true h, toInt_eq_toNat_of_msb h', Int.neg_tmod]
|
||||
rw [← Int.ofNat_tmod, ← toNat_umod, toInt_neg_eq_of_msb ?msb, toInt_eq_toNat_of_msb ?msb]
|
||||
rw [BitVec.msb_umod_of_le_of_ne_zero_of_le (neg_le_intMin_of_msb_eq_true h) hyz]
|
||||
exact le_intMin_of_msb_eq_false h'
|
||||
· dsimp only
|
||||
rw [toInt_eq_neg_toNat_neg_of_msb_true h, toInt_eq_neg_toNat_neg_of_msb_true h', Int.neg_tmod, Int.tmod_neg]
|
||||
rw [← Int.ofNat_tmod, ← toNat_umod, toInt_neg_eq_of_msb ?msb', toInt_eq_toNat_of_msb ?msb']
|
||||
rw [BitVec.msb_umod_of_le_of_ne_zero_of_le (neg_le_intMin_of_msb_eq_true h)
|
||||
((not_congr neg_eq_zero_iff).mpr hyz)]
|
||||
exact neg_le_intMin_of_msb_eq_true h'
|
||||
|
||||
/-! ### Lemmas that use bit blasting circuits -/
|
||||
|
||||
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by
|
||||
apply eq_of_toNat_eq
|
||||
simp only [toNat_sub, toNat_add, add_mod_mod, mod_add_mod]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem sub_add_comm {x y : BitVec w} : x - y + z = x + z - y := by
|
||||
rw [add_sub_comm]
|
||||
|
||||
theorem not_add_one {x : BitVec w} : ~~~ (x + 1#w) = ~~~ x - 1#w := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
|
||||
|
||||
theorem not_add_eq_not_neg {x y : BitVec w} : ~~~ (x + y) = ~~~ x - y := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
|
||||
simp only [sub_eq_add_neg]
|
||||
rw [BitVec.add_assoc, @BitVec.add_comm _ (-y), ← BitVec.add_assoc]
|
||||
|
||||
theorem not_sub_one_eq_not_add_one {x : BitVec w} : ~~~ (x - 1#w) = ~~~ x + 1#w := by
|
||||
rw [not_eq_neg_add, not_eq_neg_add, neg_sub,
|
||||
BitVec.add_sub_cancel, BitVec.sub_add_cancel]
|
||||
|
||||
theorem not_sub_eq_not_add {x y : BitVec w} : ~~~ (x - y) = ~~~ x + y := by
|
||||
rw [BitVec.sub_eq_add_neg, not_add_eq_not_neg, sub_neg]
|
||||
|
||||
/-- The value of `(carry i x y false)` can be computed by truncating `x` and `y`
|
||||
to `len` bits where `len ≥ i`. -/
|
||||
theorem carry_extractLsb'_eq_carry {w i len : Nat} (hi : i < len)
|
||||
{x y : BitVec w} {b : Bool}:
|
||||
(carry i (extractLsb' 0 len x) (extractLsb' 0 len y) b)
|
||||
= (carry i x y b) := by
|
||||
simp only [carry, extractLsb'_toNat, shiftRight_zero, toNat_false, Nat.add_zero, ge_iff_le,
|
||||
decide_eq_decide]
|
||||
have : 2 ^ i ∣ 2^len := by
|
||||
apply Nat.pow_dvd_pow
|
||||
omega
|
||||
rw [Nat.mod_mod_of_dvd _ this, Nat.mod_mod_of_dvd _ this]
|
||||
|
||||
/--
|
||||
The `[0..len)` low bits of `x + y` can be computed by truncating `x` and `y`
|
||||
to `len` bits and then adding.
|
||||
-/
|
||||
theorem extractLsb'_add {w len : Nat} {x y : BitVec w} (hlen : len ≤ w) :
|
||||
(x + y).extractLsb' 0 len = x.extractLsb' 0 len + y.extractLsb' 0 len := by
|
||||
ext i hi
|
||||
rw [getElem_extractLsb', Nat.zero_add, getLsbD_add (by omega)]
|
||||
simp [getElem_add, carry_extractLsb'_eq_carry hi, getElem_extractLsb', Nat.zero_add]
|
||||
|
||||
/-- `extractLsb'` commutes with multiplication. -/
|
||||
theorem extractLsb'_mul {w len} {x y : BitVec w} (hlen : len ≤ w) :
|
||||
(x * y).extractLsb' 0 len = (x.extractLsb' 0 len) * (y.extractLsb' 0 len) := by
|
||||
simp [← setWidth_eq_extractLsb' hlen, setWidth_mul _ _ hlen]
|
||||
|
||||
/-- Adding bitvectors that are zero in complementary positions equals concatenation. -/
|
||||
theorem append_add_append_eq_append {v w : Nat} {x : BitVec v} {y : BitVec w} :
|
||||
(x ++ 0#w) + (0#v ++ y) = x ++ y := by
|
||||
rw [add_eq_or_of_and_eq_zero] <;> ext i <;> simp
|
||||
|
||||
/-- Heuristically, `y <<< x` is much larger than `x`,
|
||||
and hence low bits of `y <<< x`. Thus, `x + (y <<< x) = x ||| (y <<< x).` -/
|
||||
theorem add_shiftLeft_eq_or_shiftLeft {x y : BitVec w} :
|
||||
x + (y <<< x) = x ||| (y <<< x) := by
|
||||
rw [add_eq_or_of_and_eq_zero]
|
||||
ext i hi
|
||||
simp only [shiftLeft_eq', getElem_and, getElem_shiftLeft, getElem_zero, and_eq_false_imp,
|
||||
not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, Nat.not_lt]
|
||||
intros hxi hxval
|
||||
have : 2^i ≤ x.toNat := two_pow_le_toNat_of_getElem_eq_true hi hxi
|
||||
have : i < 2^i := by exact Nat.lt_two_pow_self
|
||||
omega
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -13,15 +13,18 @@ set_option linter.missingDocs true
|
||||
namespace BitVec
|
||||
|
||||
/--
|
||||
iunfoldr is an iterative operation that applies a function `f` repeatedly.
|
||||
Constructs a bitvector by iteratively computing a state for each bit using the function `f`,
|
||||
starting with the initial state `s`. At each step, the prior state and the current bit index are
|
||||
passed to `f`, and it produces a bit along with the next state value. These bits are assembled into
|
||||
the final bitvector.
|
||||
|
||||
It produces a sequence of state values `[s_0, s_1 .. s_w]` and a bitvector
|
||||
`v` where `f i s_i = (s_{i+1}, b_i)` and `b_i` is bit `i`th least-significant bit
|
||||
in `v` (e.g., `getLsb v i = b_i`).
|
||||
It produces a sequence of state values `[s_0, s_1 .. s_w]` and a bitvector `v` where `f i s_i =
|
||||
(s_{i+1}, b_i)` and `b_i` is bit `i`th least-significant bit in `v` (e.g., `getLsb v i = b_i`).
|
||||
|
||||
Theorems involving `iunfoldr` can be eliminated using `iunfoldr_replace` below.
|
||||
The theorem `iunfoldr_replace` allows uses of `BitVec.iunfoldr` to be replaced wiht declarative
|
||||
specifications that are easier to reason about.
|
||||
-/
|
||||
def iunfoldr (f : Fin w -> α → α × Bool) (s : α) : α × BitVec w :=
|
||||
def iunfoldr (f : Fin w → α → α × Bool) (s : α) : α × BitVec w :=
|
||||
Fin.hIterate (fun i => α × BitVec i) (s, nil) fun i q =>
|
||||
(fun p => ⟨p.fst, cons p.snd q.snd⟩) (f i q.fst)
|
||||
|
||||
@@ -96,19 +99,24 @@ theorem iunfoldr_getLsbD {f : Fin w → α → α × Bool} (state : Nat → α)
|
||||
exact (iunfoldr_getLsbD' state ind).1 i
|
||||
|
||||
/--
|
||||
Correctness theorem for `iunfoldr`.
|
||||
Given a function `state` that provides the correct state for every potential iteration count and a
|
||||
function that computes these states from the correct initial state, the result of applying
|
||||
`BitVec.iunfoldr f` to the initial state is the state corresponding to the bitvector's width paired
|
||||
with the bitvector that consists of each computed bit.
|
||||
|
||||
This theorem can be used to prove properties of functions that are defined using `BitVec.iunfoldr`.
|
||||
-/
|
||||
theorem iunfoldr_replace
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (value : BitVec w) (a : α)
|
||||
(init : state 0 = a)
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
|
||||
iunfoldr f a = (state w, value) := by
|
||||
simp [iunfoldr.eq_test state value a init step]
|
||||
|
||||
theorem iunfoldr_replace_snd
|
||||
{f : Fin w → α → α × Bool} (state : Nat → α) (value : BitVec w) (a : α)
|
||||
(init : state 0 = a)
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
|
||||
(step : ∀(i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
|
||||
(iunfoldr f a).snd = value := by
|
||||
simp [iunfoldr.eq_test state value a init step]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,11 +9,25 @@ import Init.NotationExtra
|
||||
|
||||
namespace Bool
|
||||
|
||||
/-- Boolean exclusive or -/
|
||||
/--
|
||||
Boolean “exclusive or”. `xor x y` can be written `x ^^ y`.
|
||||
|
||||
`x ^^ y` is `true` when precisely one of `x` or `y` is `true`. Unlike `and` and `or`, it does not
|
||||
have short-circuiting behavior, because one argument's value never determines the final value. Also
|
||||
unlike `and` and `or`, there is no commonly-used corresponding propositional connective.
|
||||
|
||||
Examples:
|
||||
* `false ^^ false = false`
|
||||
* `true ^^ false = true`
|
||||
* `false ^^ true = true`
|
||||
* `true ^^ true = false`
|
||||
-/
|
||||
abbrev xor : Bool → Bool → Bool := bne
|
||||
|
||||
@[inherit_doc] infixl:33 " ^^ " => xor
|
||||
|
||||
recommended_spelling "xor" for "^^" in [xor, «term_^^_»]
|
||||
|
||||
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∀ x, p x) :=
|
||||
match inst true, inst false with
|
||||
| isFalse ht, _ => isFalse fun h => absurd (h _) ht
|
||||
@@ -89,15 +103,39 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem and_iff_left_iff_imp : ∀ {a b : Bool}, ((a && b) = a) ↔ (a → b) := by decide
|
||||
@[simp] theorem and_iff_right_iff_imp : ∀ {a b : Bool}, ((a && b) = b) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_self_and : ∀ {a b : Bool}, (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_and_self : ∀ {a b : Bool}, (b = (a && b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem and_eq_left_iff_imp : ∀ {a b : Bool}, ((a && b) = a) ↔ (a → b) := by decide
|
||||
@[simp] theorem and_eq_right_iff_imp : ∀ {a b : Bool}, ((a && b) = b) ↔ (b → a) := by decide
|
||||
@[simp] theorem eq_self_and : ∀ {a b : Bool}, (a = (a && b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem eq_and_self : ∀ {a b : Bool}, (b = (a && b)) ↔ (b → a) := by decide
|
||||
|
||||
@[simp] theorem not_and_iff_left_iff_imp : ∀ {a b : Bool}, ((!a && b) = a) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem and_not_iff_right_iff_imp : ∀ {a b : Bool}, ((a && !b) = b) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_not_self_and : ∀ {a b : Bool}, (a = (!a && b)) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem iff_and_not_self : ∀ {a b : Bool}, (b = (a && !b)) ↔ !a ∧ !b := by decide
|
||||
@[deprecated and_eq_left_iff_imp (since := "2025-04-04")]
|
||||
abbrev and_iff_left_iff_imp := @and_eq_left_iff_imp
|
||||
|
||||
@[deprecated and_eq_right_iff_imp (since := "2025-04-04")]
|
||||
abbrev and_iff_right_iff_imp := @and_eq_right_iff_imp
|
||||
|
||||
@[deprecated eq_self_and (since := "2025-04-04")]
|
||||
abbrev iff_self_and := @eq_self_and
|
||||
|
||||
@[deprecated eq_and_self (since := "2025-04-04")]
|
||||
abbrev iff_and_self := @eq_and_self
|
||||
|
||||
@[simp] theorem not_and_eq_left_iff_and : ∀ {a b : Bool}, ((!a && b) = a) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem and_not_eq_right_iff_and : ∀ {a b : Bool}, ((a && !b) = b) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem eq_not_self_and : ∀ {a b : Bool}, (a = (!a && b)) ↔ !a ∧ !b := by decide
|
||||
@[simp] theorem eq_and_not_self : ∀ {a b : Bool}, (b = (a && !b)) ↔ !a ∧ !b := by decide
|
||||
|
||||
@[deprecated not_and_eq_left_iff_and (since := "2025-04-04")]
|
||||
abbrev not_and_iff_left_iff_imp := @not_and_eq_left_iff_and
|
||||
|
||||
@[deprecated and_not_eq_right_iff_and (since := "2025-04-04")]
|
||||
abbrev and_not_iff_right_iff_imp := @and_not_eq_right_iff_and
|
||||
|
||||
@[deprecated eq_not_self_and (since := "2025-04-04")]
|
||||
abbrev iff_not_self_and := @eq_not_self_and
|
||||
|
||||
@[deprecated eq_and_not_self (since := "2025-04-04")]
|
||||
abbrev iff_and_not_self := @eq_and_not_self
|
||||
|
||||
/-! ### or -/
|
||||
|
||||
@@ -123,15 +161,39 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
|
||||
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
|
||||
`and_iff_left_iff_imp`.
|
||||
-/
|
||||
@[simp] theorem or_iff_left_iff_imp : ∀ {a b : Bool}, ((a || b) = a) ↔ (b → a) := by decide
|
||||
@[simp] theorem or_iff_right_iff_imp : ∀ {a b : Bool}, ((a || b) = b) ↔ (a → b) := by decide
|
||||
@[simp] theorem iff_self_or : ∀ {a b : Bool}, (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem iff_or_self : ∀ {a b : Bool}, (b = (a || b)) ↔ (a → b) := by decide
|
||||
@[simp] theorem or_eq_left_iff_imp : ∀ {a b : Bool}, ((a || b) = a) ↔ (b → a) := by decide
|
||||
@[simp] theorem or_eq_right_iff_imp : ∀ {a b : Bool}, ((a || b) = b) ↔ (a → b) := by decide
|
||||
@[simp] theorem eq_self_or : ∀ {a b : Bool}, (a = (a || b)) ↔ (b → a) := by decide
|
||||
@[simp] theorem eq_or_self : ∀ {a b : Bool}, (b = (a || b)) ↔ (a → b) := by decide
|
||||
|
||||
@[simp] theorem not_or_iff_left_iff_imp : ∀ {a b : Bool}, ((!a || b) = a) ↔ a ∧ b := by decide
|
||||
@[simp] theorem or_not_iff_right_iff_imp : ∀ {a b : Bool}, ((a || !b) = b) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_not_self_or : ∀ {a b : Bool}, (a = (!a || b)) ↔ a ∧ b := by decide
|
||||
@[simp] theorem iff_or_not_self : ∀ {a b : Bool}, (b = (a || !b)) ↔ a ∧ b := by decide
|
||||
@[deprecated or_eq_left_iff_imp (since := "2025-04-04")]
|
||||
abbrev or_iff_left_iff_imp := @or_eq_left_iff_imp
|
||||
|
||||
@[deprecated or_eq_right_iff_imp (since := "2025-04-04")]
|
||||
abbrev or_iff_right_iff_imp := @or_eq_right_iff_imp
|
||||
|
||||
@[deprecated eq_self_or (since := "2025-04-04")]
|
||||
abbrev iff_self_or := @eq_self_or
|
||||
|
||||
@[deprecated eq_or_self (since := "2025-04-04")]
|
||||
abbrev iff_or_self := @eq_or_self
|
||||
|
||||
@[simp] theorem not_or_eq_left_iff_and : ∀ {a b : Bool}, ((!a || b) = a) ↔ a ∧ b := by decide
|
||||
@[simp] theorem or_not_eq_right_iff_and : ∀ {a b : Bool}, ((a || !b) = b) ↔ a ∧ b := by decide
|
||||
@[simp] theorem eq_not_self_or : ∀ {a b : Bool}, (a = (!a || b)) ↔ a ∧ b := by decide
|
||||
@[simp] theorem eq_or_not_self : ∀ {a b : Bool}, (b = (a || !b)) ↔ a ∧ b := by decide
|
||||
|
||||
@[deprecated not_or_eq_left_iff_and (since := "2025-04-04")]
|
||||
abbrev not_or_iff_left_iff_imp := @not_or_eq_left_iff_and
|
||||
|
||||
@[deprecated or_not_eq_right_iff_and (since := "2025-04-04")]
|
||||
abbrev or_not_iff_right_iff_imp := @or_not_eq_right_iff_and
|
||||
|
||||
@[deprecated eq_not_self_or (since := "2025-04-04")]
|
||||
abbrev iff_not_self_or := @eq_not_self_or
|
||||
|
||||
@[deprecated eq_or_not_self (since := "2025-04-04")]
|
||||
abbrev iff_or_not_self := @eq_or_not_self
|
||||
|
||||
theorem or_comm : ∀ (x y : Bool), (x || y) = (y || x) := by decide
|
||||
instance : Std.Commutative (· || ·) := ⟨or_comm⟩
|
||||
@@ -365,17 +427,19 @@ theorem and_or_inj_left_iff :
|
||||
|
||||
/-! ## toNat -/
|
||||
|
||||
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
def toNat (b : Bool) : Nat := cond b 1 0
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_false : false.toNat = 0 := rfl
|
||||
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_true : true.toNat = 1 := rfl
|
||||
@[simp, bitvec_to_nat] theorem toNat_true : true.toNat = 1 := rfl
|
||||
|
||||
theorem toNat_le (c : Bool) : c.toNat ≤ 1 := by
|
||||
cases c <;> trivial
|
||||
|
||||
@[bv_toNat]
|
||||
@[bitvec_to_nat]
|
||||
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
Nat.lt_succ_of_le (toNat_le _)
|
||||
|
||||
@@ -386,7 +450,9 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
|
||||
|
||||
/-! ## toInt -/
|
||||
|
||||
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
|
||||
/--
|
||||
Converts `true` to `1` and `false` to `0`.
|
||||
-/
|
||||
def toInt (b : Bool) : Int := cond b 1 0
|
||||
|
||||
@[simp] theorem toInt_false : false.toInt = 0 := rfl
|
||||
@@ -537,8 +603,8 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
|
||||
@[simp] theorem cond_eq_false_distrib : ∀(c t f : Bool),
|
||||
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
|
||||
|
||||
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
|
||||
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
|
||||
protected theorem cond_true {α : Sort u} {a b : α} : cond true a b = a := cond_true a b
|
||||
protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := cond_false a b
|
||||
|
||||
@[simp] theorem cond_true_left : ∀(c f : Bool), cond c true f = ( c || f) := by decide
|
||||
@[simp] theorem cond_false_left : ∀(c f : Bool), cond c false f = (!c && f) := by decide
|
||||
@@ -546,11 +612,16 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
|
||||
@[simp] theorem cond_false_right : ∀(c t : Bool), cond c t false = ( c && t) := by decide
|
||||
|
||||
-- These restore confluence between the above lemmas and `cond_not`.
|
||||
@[simp] theorem cond_true_not_same : ∀ (c b : Bool), cond c (!c) b = (!c && b) := by decide
|
||||
@[simp] theorem cond_false_not_same : ∀ (c b : Bool), cond c b (!c) = (!c || b) := by decide
|
||||
@[simp] theorem cond_then_not_self : ∀ (c b : Bool), cond c (!c) b = (!c && b) := by decide
|
||||
@[simp] theorem cond_else_not_self : ∀ (c b : Bool), cond c b (!c) = (!c || b) := by decide
|
||||
|
||||
@[simp] theorem cond_true_same : ∀(c b : Bool), cond c c b = (c || b) := by decide
|
||||
@[simp] theorem cond_false_same : ∀(c b : Bool), cond c b c = (c && b) := by decide
|
||||
@[simp] theorem cond_then_self : ∀ (c b : Bool), cond c c b = (c || b) := by decide
|
||||
@[simp] theorem cond_else_self : ∀ (c b : Bool), cond c b c = (c && b) := by decide
|
||||
|
||||
@[deprecated cond_then_not_self (since := "2025-04-04")] abbrev cond_true_not_same := @cond_then_not_self
|
||||
@[deprecated cond_else_not_self (since := "2025-04-04")] abbrev cond_false_not_same := @cond_else_not_self
|
||||
@[deprecated cond_then_self (since := "2025-04-04")] abbrev cond_true_same := @cond_then_self
|
||||
@[deprecated cond_else_self (since := "2025-04-04")] abbrev cond_false_same := @cond_else_self
|
||||
|
||||
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
|
||||
rw [h, cond_true]
|
||||
@@ -578,17 +649,13 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
|
||||
decide (p ↔ q) = (decide p == decide q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p ∧ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p && q) = decide (p ∧ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
@[bool_to_prop]
|
||||
theorem and_eq_decide (p q : Bool) : (p && q) = decide (p ∧ q) := by simp
|
||||
|
||||
@[boolToPropSimps]
|
||||
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p ∨ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(p || q) = decide (p ∨ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
@[bool_to_prop]
|
||||
theorem or_eq_decide (p q : Bool) : (p || q) = decide (p ∨ q) := by simp
|
||||
|
||||
@[boolToPropSimps]
|
||||
@[bool_to_prop]
|
||||
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
|
||||
(decide p == decide q) = decide (p ↔ q) := by
|
||||
cases dp with | _ p => simp [p]
|
||||
@@ -620,3 +687,12 @@ but may be used locally.
|
||||
-/
|
||||
def boolRelToRel : Coe (α → α → Bool) (α → α → Prop) where
|
||||
coe r := fun a b => Eq (r a b) true
|
||||
|
||||
/-! ### subtypes -/
|
||||
|
||||
@[simp] theorem Subtype.beq_iff {α : Type u} [DecidableEq α] {p : α → Prop} {x y : {a : α // p a}} :
|
||||
(x == y) = (x.1 == y.1) := by
|
||||
cases x
|
||||
cases y
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [beq_iff_eq]
|
||||
|
||||
@@ -18,10 +18,13 @@ attribute [extern "lean_byte_array_data"] ByteArray.data
|
||||
|
||||
namespace ByteArray
|
||||
@[extern "lean_mk_empty_byte_array"]
|
||||
def mkEmpty (c : @& Nat) : ByteArray :=
|
||||
def emptyWithCapacity (c : @& Nat) : ByteArray :=
|
||||
{ data := #[] }
|
||||
|
||||
def empty : ByteArray := mkEmpty 0
|
||||
@[deprecated emptyWithCapacity (since := "2025-03-12")]
|
||||
abbrev mkEmpty := emptyWithCapacity
|
||||
|
||||
def empty : ByteArray := emptyWithCapacity 0
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
@@ -47,7 +50,7 @@ def uget : (a : @& ByteArray) → (i : USize) → (h : i.toNat < a.size := by ge
|
||||
|
||||
@[extern "lean_byte_array_get"]
|
||||
def get! : (@& ByteArray) → (@& Nat) → UInt8
|
||||
| ⟨bs⟩, i => bs.get! i
|
||||
| ⟨bs⟩, i => bs[i]!
|
||||
|
||||
@[extern "lean_byte_array_fget"]
|
||||
def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem_tactic) → UInt8
|
||||
@@ -56,7 +59,7 @@ def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem
|
||||
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get i
|
||||
|
||||
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
|
||||
instance : GetElem ByteArray USize UInt8 fun xs i => i.toFin < xs.size where
|
||||
getElem xs i h := xs.uget i h
|
||||
|
||||
@[extern "lean_byte_array_set"]
|
||||
@@ -334,6 +337,9 @@ def prevn : Iterator → Nat → Iterator
|
||||
end Iterator
|
||||
end ByteArray
|
||||
|
||||
/--
|
||||
Converts a list of bytes into a `ByteArray`.
|
||||
-/
|
||||
def List.toByteArray (bs : List UInt8) : ByteArray :=
|
||||
let rec loop
|
||||
| [], r => r
|
||||
|
||||
@@ -44,25 +44,26 @@ 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`. -/
|
||||
/--
|
||||
The canonical homomorphism `Nat → R`. In most use cases, the target type will have a (semi)ring
|
||||
structure, and this homomorphism should be a (semi)ring homomorphism.
|
||||
|
||||
`NatCast` 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`.
|
||||
|
||||
The prototypical example is `Int.ofNat`.
|
||||
-/
|
||||
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 :=
|
||||
@[coe, reducible, match_pattern, inherit_doc NatCast]
|
||||
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
|
||||
|
||||
@@ -15,7 +15,15 @@ Note that values in `[0xd800, 0xdfff]` are reserved for [UTF-16 surrogate pairs]
|
||||
|
||||
namespace Char
|
||||
|
||||
/--
|
||||
One character is less than another if its code point is strictly less than the other's.
|
||||
-/
|
||||
protected def lt (a b : Char) : Prop := a.val < b.val
|
||||
|
||||
/--
|
||||
One character is less than or equal to another if its code point is less than or equal to the
|
||||
other's.
|
||||
-/
|
||||
protected def le (a b : Char) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : LT Char := ⟨Char.lt⟩
|
||||
@@ -27,7 +35,10 @@ instance (a b : Char) : Decidable (a < b) :=
|
||||
instance (a b : Char) : Decidable (a ≤ b) :=
|
||||
UInt32.decLe _ _
|
||||
|
||||
/-- Determines if the given nat is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).-/
|
||||
/--
|
||||
True for natural numbers that are valid [Unicode scalar
|
||||
values](https://www.unicode.org/glossary/#unicode_scalar_value).
|
||||
-/
|
||||
abbrev isValidCharNat (n : Nat) : Prop :=
|
||||
n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)
|
||||
|
||||
@@ -40,65 +51,103 @@ theorem isValidUInt32 (n : Nat) (h : isValidCharNat n) : n < UInt32.size := by
|
||||
apply Nat.lt_trans h₂
|
||||
decide
|
||||
|
||||
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNat' n (isValidUInt32 n h)) :=
|
||||
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNatLT n (isValidUInt32 n h)) :=
|
||||
match h with
|
||||
| Or.inl h =>
|
||||
Or.inl (UInt32.ofNat'_lt_of_lt _ (by decide) h)
|
||||
Or.inl (UInt32.ofNatLT_lt_of_lt _ (by decide) h)
|
||||
| Or.inr ⟨h₁, h₂⟩ =>
|
||||
Or.inr ⟨UInt32.lt_ofNat'_of_lt _ (by decide) h₁, UInt32.ofNat'_lt_of_lt _ (by decide) h₂⟩
|
||||
Or.inr ⟨UInt32.lt_ofNatLT_of_lt _ (by decide) h₁, UInt32.ofNatLT_lt_of_lt _ (by decide) h₂⟩
|
||||
|
||||
theorem isValidChar_zero : isValidChar 0 :=
|
||||
Or.inl (by decide)
|
||||
|
||||
/-- Underlying unicode code point as a `Nat`. -/
|
||||
/--
|
||||
The character's Unicode code point as a `Nat`.
|
||||
-/
|
||||
@[inline] def toNat (c : Char) : Nat :=
|
||||
c.val.toNat
|
||||
|
||||
/-- Convert a character into a `UInt8`, by truncating (reducing modulo 256) if necessary. -/
|
||||
/--
|
||||
Converts a character into a `UInt8` that contains its code point.
|
||||
|
||||
If the code point is larger than 255, it is truncated (reduced modulo 256).
|
||||
-/
|
||||
@[inline] def toUInt8 (c : Char) : UInt8 :=
|
||||
c.val.toUInt8
|
||||
|
||||
/-- The numbers from 0 to 256 are all valid UTF-8 characters, so we can embed one in the other. -/
|
||||
/--
|
||||
Converts an 8-bit unsigned integer into a character.
|
||||
|
||||
The integer's value is interpreted as a Unicode code point.
|
||||
-/
|
||||
def ofUInt8 (n : UInt8) : Char := ⟨n.toUInt32, .inl (Nat.lt_trans n.toBitVec.isLt (by decide))⟩
|
||||
|
||||
instance : Inhabited Char where
|
||||
default := 'A'
|
||||
|
||||
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
|
||||
/--
|
||||
Returns `true` if the character is a space `(' ', U+0020)`, a tab `('\t', U+0009)`, a carriage
|
||||
return `('\r', U+000D)`, or a newline `('\n', U+000A)`.
|
||||
-/
|
||||
@[inline] def isWhitespace (c : Char) : Bool :=
|
||||
c = ' ' || c = '\t' || c = '\r' || c = '\n'
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
|
||||
/--
|
||||
Returns `true` if the character is a uppercase ASCII letter.
|
||||
|
||||
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||
-/
|
||||
@[inline] def isUpper (c : Char) : Bool :=
|
||||
c.val ≥ 65 && c.val ≤ 90
|
||||
|
||||
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
|
||||
/--
|
||||
Returns `true` if the character is a lowercase ASCII letter.
|
||||
|
||||
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
@[inline] def isLower (c : Char) : Bool :=
|
||||
c.val ≥ 97 && c.val ≤ 122
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII letter.
|
||||
|
||||
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
@[inline] def isAlpha (c : Char) : Bool :=
|
||||
c.isUpper || c.isLower
|
||||
|
||||
/-- Is the character in `0123456789`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII digit.
|
||||
|
||||
The ASCII digits are the following: `0123456789`.
|
||||
-/
|
||||
@[inline] def isDigit (c : Char) : Bool :=
|
||||
c.val ≥ 48 && c.val ≤ 57
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
|
||||
/--
|
||||
Returns `true` if the character is an ASCII letter or digit.
|
||||
|
||||
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
|
||||
The ASCII digits are the following: `0123456789`.
|
||||
-/
|
||||
@[inline] def isAlphanum (c : Char) : Bool :=
|
||||
c.isAlpha || c.isDigit
|
||||
|
||||
/-- Convert an upper case character to its lower case character.
|
||||
/--
|
||||
Converts an uppercase ASCII letter to the corresponding lowercase letter. Letters outside the ASCII
|
||||
alphabet are returned unchanged.
|
||||
|
||||
Only works on basic latin letters.
|
||||
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||
-/
|
||||
def toLower (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
if n >= 65 ∧ n <= 90 then ofNat (n + 32) else c
|
||||
|
||||
/-- Convert a lower case character to its upper case character.
|
||||
/--
|
||||
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
|
||||
alphabet are returned unchanged.
|
||||
|
||||
Only works on basic latin letters.
|
||||
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
|
||||
-/
|
||||
def toUpper (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
|
||||
@@ -70,5 +70,3 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 ∨ c.utf8Size = 2 ∨ c.utf8Siz
|
||||
rfl
|
||||
|
||||
end Char
|
||||
|
||||
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size
|
||||
|
||||
@@ -14,22 +14,23 @@ instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
|
||||
The type `Fin 0` is uninhabited, so it can be used to derive any result whatsoever.
|
||||
|
||||
This is similar to `Empty.elim`. It can be thought of as a compiler-checked assertion that a code
|
||||
path is unreachable, or a logical contradiction from which `False` and thus anything else could be
|
||||
derived.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
/--
|
||||
Returns the successor of the argument.
|
||||
The successor, with an increased bound.
|
||||
|
||||
The bound in the result type is increased:
|
||||
```
|
||||
(2 : Fin 3).succ = (3 : Fin 4)
|
||||
```
|
||||
This differs from addition, which wraps around:
|
||||
```
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
This differs from adding `1`, which instead wraps around.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 3).succ = (3 : Fin 4)`
|
||||
* `(2 : Fin 3) + 1 = (0 : Fin 3)`
|
||||
-/
|
||||
def succ : Fin n → Fin (n + 1)
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
@@ -51,21 +52,54 @@ Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
|
||||
⟨a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)⟩
|
||||
|
||||
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
|
||||
-- `i.toNat` to `i.val`.
|
||||
/--
|
||||
Extracts the underlying `Nat` value.
|
||||
|
||||
This function is a synonym for `Fin.val`, which is the simp normal form. `Fin.val` is also a
|
||||
coercion, so values of type `Fin n` are automatically converted to `Nat`s as needed.
|
||||
-/
|
||||
@[inline]
|
||||
protected def toNat (i : Fin n) : Nat :=
|
||||
i.val
|
||||
|
||||
@[simp] theorem toNat_eq_val {i : Fin n} : i.toNat = i.val := rfl
|
||||
|
||||
private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
| 0, h => Nat.mod_lt _ h
|
||||
| _+1, h =>
|
||||
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
|
||||
Nat.mod_lt _ this
|
||||
|
||||
/-- Addition modulo `n` -/
|
||||
/--
|
||||
Addition modulo `n`, usually invoked via the `+` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 8) + (2 : Fin 8) = (4 : Fin 8)`
|
||||
* `(2 : Fin 3) + (2 : Fin 3) = (1 : Fin 3)`
|
||||
-/
|
||||
protected def add : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + b) % n, mlt h⟩
|
||||
|
||||
/-- Multiplication modulo `n` -/
|
||||
/--
|
||||
Multiplication modulo `n`, usually invoked via the `*` operator.
|
||||
|
||||
Examples:
|
||||
* `(2 : Fin 10) * (2 : Fin 10) = (4 : Fin 10)`
|
||||
* `(2 : Fin 10) * (7 : Fin 10) = (4 : Fin 10)`
|
||||
* `(3 : Fin 10) * (7 : Fin 10) = (1 : Fin 10)`
|
||||
-/
|
||||
protected def mul : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a * b) % n, mlt h⟩
|
||||
|
||||
/-- Subtraction modulo `n` -/
|
||||
/--
|
||||
Subtraction modulo `n`, usually invoked via the `-` operator.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 11) - (3 : Fin 11) = (2 : Fin 11)`
|
||||
* `(3 : Fin 11) - (5 : Fin 11) = (9 : Fin 11)`
|
||||
-/
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
/-
|
||||
The definition of `Fin.sub` has been updated to improve performance.
|
||||
@@ -92,27 +126,76 @@ we are trying to minimize the number of Nat theorems
|
||||
needed to bootstrap Lean.
|
||||
-/
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers, usually invoked via the `%` operator.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
protected def mod : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Division of bounded numbers, usually invoked via the `/` operator.
|
||||
|
||||
The resulting value is that computed by the `/` operator on `Nat`. In particular, the result of
|
||||
division by `0` is `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 10) / (2 : Fin 10) = (2 : Fin 10)`
|
||||
* `(5 : Fin 10) / (0 : Fin 10) = (0 : Fin 10)`
|
||||
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
|
||||
-/
|
||||
protected def div : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h⟩
|
||||
|
||||
/--
|
||||
Modulus of bounded numbers with respect to a `Nat`.
|
||||
|
||||
The resulting value is that computed by the `%` operator on `Nat`.
|
||||
-/
|
||||
def modn : Fin n → Nat → Fin n
|
||||
| ⟨a, h⟩, m => ⟨a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h⟩
|
||||
|
||||
/--
|
||||
Bitwise and.
|
||||
-/
|
||||
def land : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.land a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise or.
|
||||
-/
|
||||
def lor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.lor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise xor (“exclusive or”).
|
||||
-/
|
||||
def xor : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(Nat.xor a b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise left shift of bounded numbers, with wraparound on overflow.
|
||||
|
||||
Examples:
|
||||
* `(1 : Fin 10) <<< (1 : Fin 10) = (2 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (3 : Fin 10) = (8 : Fin 10)`
|
||||
* `(1 : Fin 10) <<< (4 : Fin 10) = (6 : Fin 10)`
|
||||
-/
|
||||
def shiftLeft : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a <<< b) % n, mlt h⟩
|
||||
|
||||
/--
|
||||
Bitwise right shift of bounded numbers.
|
||||
|
||||
This operator corresponds to logical rather than arithmetic bit shifting. The new bits are always
|
||||
`0`.
|
||||
|
||||
Examples:
|
||||
* `(15 : Fin 16) >>> (1 : Fin 16) = (7 : Fin 16)`
|
||||
* `(15 : Fin 16) >>> (2 : Fin 16) = (3 : Fin 16)`
|
||||
* `(15 : Fin 17) >>> (2 : Fin 17) = (3 : Fin 17)`
|
||||
-/
|
||||
def shiftRight : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a >>> b) % n, mlt h⟩
|
||||
|
||||
@@ -166,39 +249,125 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
|
||||
protected theorem pos (i : Fin n) : 0 < n :=
|
||||
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
|
||||
|
||||
/-- The greatest value of `Fin (n+1)`. -/
|
||||
/--
|
||||
The greatest value of `Fin (n+1)`, namely `n`.
|
||||
|
||||
Examples:
|
||||
* `Fin.last 4 = (4 : Fin 5)`
|
||||
* `(Fin.last 0).val = (0 : Nat)`
|
||||
-/
|
||||
@[inline] def last (n : Nat) : Fin (n + 1) := ⟨n, n.lt_succ_self⟩
|
||||
|
||||
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
|
||||
/--
|
||||
Replaces the bound with another that is suitable for the value.
|
||||
|
||||
The proof embedded in `i` can be used to cast to a larger bound even if the concrete value is not
|
||||
known.
|
||||
|
||||
Examples:
|
||||
```lean example
|
||||
example : Fin 12 := (7 : Fin 10).castLT (by decide : 7 < 12)
|
||||
```
|
||||
```lean example
|
||||
example (i : Fin 10) : Fin 12 :=
|
||||
i.castLT <| by
|
||||
cases i; simp; omega
|
||||
```
|
||||
-/
|
||||
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := ⟨i.1, h⟩
|
||||
|
||||
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.castAdd` for a version that represents the larger bound with addition rather than an
|
||||
explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castLE (h : n ≤ m) (i : Fin n) : Fin m := ⟨i, Nat.lt_of_lt_of_le i.2 h⟩
|
||||
|
||||
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
|
||||
/--
|
||||
Uses a proof that two bounds are equal to allow a value bounded by one to be used with the other.
|
||||
|
||||
In other words, when `eq : n = m`, `Fin.cast eq i` converts `i : Fin n` into a `Fin m`.
|
||||
-/
|
||||
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := ⟨i, eq ▸ i.2⟩
|
||||
|
||||
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
|
||||
/--
|
||||
Coarsens a bound to one at least as large.
|
||||
|
||||
See also `Fin.natAdd` and `Fin.addNat` for addition functions that increase the bound, and
|
||||
`Fin.castLE` for a version that uses an explicit inequality proof.
|
||||
-/
|
||||
@[inline] def castAdd (m) : Fin n → Fin (n + m) :=
|
||||
castLE <| Nat.le_add_right n m
|
||||
|
||||
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
|
||||
/--
|
||||
Coarsens a bound by one.
|
||||
-/
|
||||
@[inline] def castSucc : Fin n → Fin (n + 1) := castAdd 1
|
||||
|
||||
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.natAdd` is a version of this function that takes its `Nat` parameter first.
|
||||
|
||||
Examples:
|
||||
* `Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)`
|
||||
* `Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)`
|
||||
* `Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)`
|
||||
|
||||
-/
|
||||
def addNat (i : Fin n) (m) : Fin (n + m) := ⟨i + m, Nat.add_lt_add_right i.2 _⟩
|
||||
|
||||
/-- `natAdd n i` adds `n` to `i` "on the left". -/
|
||||
/--
|
||||
Adds a natural number to a `Fin`, increasing the bound.
|
||||
|
||||
This is a generalization of `Fin.succ`.
|
||||
|
||||
`Fin.addNat` is a version of this function that takes its `Nat` parameter second.
|
||||
|
||||
Examples:
|
||||
* `Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)`
|
||||
* `Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)`
|
||||
* `Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)`
|
||||
-/
|
||||
def natAdd (n) (i : Fin m) : Fin (n + m) := ⟨n + i, Nat.add_lt_add_left i.2 _⟩
|
||||
|
||||
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
|
||||
/--
|
||||
Replaces a value with its difference from the largest value in the type.
|
||||
|
||||
Considering the values of `Fin n` as a sequence `0`, `1`, …, `n-2`, `n-1`, `Fin.rev` finds the
|
||||
corresponding element of the reversed sequence. In other words, it maps `0` to `n-1`, `1` to `n-2`,
|
||||
..., and `n-1` to `0`.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 6).rev = (0 : Fin 6)`
|
||||
* `(0 : Fin 6).rev = (5 : Fin 6)`
|
||||
* `(2 : Fin 5).rev = (2 : Fin 5)`
|
||||
-/
|
||||
@[inline] def rev (i : Fin n) : Fin n := ⟨n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)⟩
|
||||
|
||||
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
|
||||
/--
|
||||
Subtraction of a natural number from a `Fin`, with the bound narrowed.
|
||||
|
||||
This is a generalization of `Fin.pred`. It is guaranteed to not underflow or wrap around.
|
||||
|
||||
Examples:
|
||||
* `(5 : Fin 9).subNat 2 (by decide) = (3 : Fin 7)`
|
||||
* `(5 : Fin 9).subNat 0 (by decide) = (5 : Fin 9)`
|
||||
* `(3 : Fin 9).subNat 3 (by decide) = (0 : Fin 6)`
|
||||
-/
|
||||
@[inline] def subNat (m) (i : Fin (n + m)) (h : m ≤ i) : Fin n :=
|
||||
⟨i - m, Nat.sub_lt_right_of_lt_add h i.2⟩
|
||||
|
||||
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
|
||||
/--
|
||||
The predecessor of a non-zero element of `Fin (n+1)`, with the bound decreased.
|
||||
|
||||
Examples:
|
||||
* `(4 : Fin 8).pred (by decide) = (3 : Fin 7)`
|
||||
* `(1 : Fin 2).pred (by decide) = (0 : Fin 1)`
|
||||
-/
|
||||
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i ≠ 0) : Fin n :=
|
||||
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h
|
||||
|
||||
|
||||
@@ -12,4 +12,31 @@ namespace Fin
|
||||
@[simp] theorem and_val (a b : Fin n) : (a &&& b).val = a.val &&& b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_left a.isLt)
|
||||
|
||||
@[simp] theorem or_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ||| b).val = a.val ||| b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.or_lt_two_pow a.isLt b.isLt)
|
||||
|
||||
@[simp] theorem or_val_of_uInt8Size (a b : Fin UInt8.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 8) a b
|
||||
@[simp] theorem or_val_of_uInt16Size (a b : Fin UInt16.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 16) a b
|
||||
@[simp] theorem or_val_of_uInt32Size (a b : Fin UInt32.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 32) a b
|
||||
@[simp] theorem or_val_of_uInt64Size (a b : Fin UInt64.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 64) a b
|
||||
@[simp] theorem or_val_of_uSizeSize (a b : Fin USize.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow a b
|
||||
|
||||
theorem or_val (a b : Fin n) : (a ||| b).val = (a.val ||| b.val) % n := rfl
|
||||
|
||||
@[simp] theorem xor_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ^^^ b).val = a.val ^^^ b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.xor_lt_two_pow a.isLt b.isLt)
|
||||
|
||||
@[simp] theorem xor_val_of_uInt8Size (a b : Fin UInt8.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 8) a b
|
||||
@[simp] theorem xor_val_of_uInt16Size (a b : Fin UInt16.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 16) a b
|
||||
@[simp] theorem xor_val_of_uInt32Size (a b : Fin UInt32.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 32) a b
|
||||
@[simp] theorem xor_val_of_uInt64Size (a b : Fin UInt64.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 64) a b
|
||||
@[simp] theorem xor_val_of_uSizeSize (a b : Fin USize.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow a b
|
||||
|
||||
theorem xor_val (a b : Fin n) : (a ^^^ b).val = (a.val ^^^ b.val) % n := rfl
|
||||
|
||||
@[simp] theorem shiftLeft_val (a b : Fin n) : (a <<< b).val = (a.val <<< b.val) % n := rfl
|
||||
|
||||
@[simp] theorem shiftRight_val (a b : Fin n) : (a >>> b).val = a.val >>> b.val :=
|
||||
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.shiftRight_le _ _) a.isLt)
|
||||
|
||||
end Fin
|
||||
|
||||
@@ -10,14 +10,26 @@ import Init.Data.Fin.Lemmas
|
||||
|
||||
namespace Fin
|
||||
|
||||
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `0` and
|
||||
nesting to the left.
|
||||
|
||||
Example:
|
||||
* `Fin.foldl 3 (· + ·.val) (0 : Nat) = ((0 + (0 : Fin 3).val) + (1 : Fin 3).val) + (2 : Fin 3).val`
|
||||
-/
|
||||
@[inline] def foldl (n) (f : α → Fin n → α) (init : α) : α := loop init 0 where
|
||||
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
|
||||
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
|
||||
if h : i < n then loop (f x ⟨i, h⟩) (i+1) else x
|
||||
termination_by n - i
|
||||
|
||||
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
|
||||
/--
|
||||
Combine all the values that can be represented by `Fin n` with an initial value, starting at `n - 1`
|
||||
and nesting to the right.
|
||||
|
||||
Example:
|
||||
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
|
||||
-/
|
||||
@[inline] def foldr (n) (f : Fin n → α → α) (init : α) : α := loop n (Nat.le_refl n) init where
|
||||
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
|
||||
@[specialize] loop : (i : _) → i ≤ n → α → α
|
||||
@@ -26,7 +38,9 @@ namespace Fin
|
||||
termination_by structural i => i
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from left to right:
|
||||
Folds a monadic function over all the values in `Fin n` from left to right, starting with `0`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldlM n f x₀ = do
|
||||
let x₁ ← f x₀ 0
|
||||
@@ -53,7 +67,9 @@ Fin.foldlM n f x₀ = do
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
Folds a monadic function over `Fin n` from right to left:
|
||||
Folds a monadic function over `Fin n` from right to left, starting with `n-1`.
|
||||
|
||||
It is the sequence of steps:
|
||||
```
|
||||
Fin.foldrM n f xₙ = do
|
||||
let xₙ₋₁ ← f (n-1) xₙ
|
||||
@@ -125,7 +141,9 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
|
||||
| zero =>
|
||||
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
|
||||
conv => rhs; rw [←bind_pure (f 0 x)]
|
||||
congr; funext
|
||||
congr
|
||||
funext
|
||||
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
|
||||
| succ i ih =>
|
||||
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
|
||||
congr; funext; exact ih ..
|
||||
|
||||
@@ -10,10 +10,18 @@ import Init.Data.Fin.Basic
|
||||
namespace Fin
|
||||
|
||||
/--
|
||||
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
|
||||
from `P i`.
|
||||
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an
|
||||
initial accumulator `a`.
|
||||
|
||||
See `hIterate` below for more details.
|
||||
Concretely, `Fin.hIterateFrom P f i a` is equal to
|
||||
```lean
|
||||
a |> f i |> f (i + 1) |> ... |> f (n - 1)
|
||||
```
|
||||
|
||||
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
|
||||
other more specialized theorems.
|
||||
|
||||
`Fin.hIterate` is a variant that always starts at `0`.
|
||||
-/
|
||||
def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.val+1))
|
||||
(i : Nat) (ubnd : i ≤ n) (a : P i) : P n :=
|
||||
@@ -26,20 +34,18 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
/--
|
||||
`hIterate` is a heterogeneous iterative operation that applies a
|
||||
index-dependent function `f` to a value `init : P start` a total of
|
||||
`stop - start` times to produce a value of type `P stop`.
|
||||
Applies an index-dependent function to all the values less than the given bound `n`, starting at
|
||||
`0` with an accumulator.
|
||||
|
||||
Concretely, `hIterate start stop f init` is equal to
|
||||
Concretely, `Fin.hIterate P init f` is equal to
|
||||
```lean
|
||||
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
|
||||
init |> f 0 |> f 1 |> ... |> f (n-1)
|
||||
```
|
||||
|
||||
Because it is heterogeneous and must return a value of type `P stop`,
|
||||
`hIterate` requires proof that `start ≤ stop`.
|
||||
Theorems about `Fin.hIterate` can be proven using the general theorem `Fin.hIterate_elim` or other more
|
||||
specialized theorems.
|
||||
|
||||
One can prove properties of `hIterate` using the general theorem
|
||||
`hIterate_elim` or other more specialized theorems.
|
||||
`Fin.hIterateFrom` is a variant that takes a custom starting value instead of `0`.
|
||||
-/
|
||||
def hIterate (P : Nat → Sort _) {n : Nat} (init : P 0) (f : ∀(i : Fin n), P i.val → P (i.val+1)) :
|
||||
P n :=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user