Compare commits

..

467 Commits

Author SHA1 Message Date
Kim Morrison
26268136dc doc-string 2025-06-21 14:37:50 +10:00
Kim Morrison
98c220ea8d cleanup 2025-06-21 14:24:00 +10:00
Kim Morrison
b277f3a402 yay 2025-06-21 14:18:55 +10:00
Kim Morrison
7563199ccc fix merge 2025-06-21 13:26:10 +10:00
Kim Morrison
42882ce465 merge grind_no_nat_div 2025-06-21 13:17:04 +10:00
Kim Morrison
f20d0e4532 merge master 2025-06-21 13:14:25 +10:00
Leonardo de Moura
9ece4e463a refactor: NoNatZeroDivisors (#8909)
This PR refactors the `NoNatZeroDivisors` to make sure it will work with
the new `Semiring` support.
2025-06-21 03:01:05 +00:00
Sebastian Ullrich
c38c0898a3 chore: allow module in tests (#8881)
This PR adjusts the test scripts and adds a simple test-only lakefile so
that `experimental.module` is set both when editing and running tests.
2025-06-21 02:49:22 +00:00
Leonardo de Moura
070e622f05 refactor: NoNatZeroDivisors
This PR refactors the `NoNatZeroDivisors` to make sure it will work
with the new `Semiring` support.
2025-06-21 11:47:35 +09:00
Leonardo de Moura
12a8f1b5f8 chore: remove staging workarounds (#8908) 2025-06-21 02:38:09 +00:00
Lean stage0 autoupdater
7050dc6d38 chore: update stage0 2025-06-21 01:59:13 +00:00
Kim Morrison
376ae32c7c feat: fix pretty printing of grind attributes (#8892)
This PR corrects the pretty printing of `grind` modifiers. Previously
`@[grind →]` was being pretty printed as `@[grind→ ]` (Space on the
right of the symbol, rather than left.) This fixes the pretty printing
of attributes, and preserves the presence of spaces after the symbol in
the output of `grind?`.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-06-21 00:50:25 +00:00
Cameron Zwarich
0c44aab811 chore: add a test for #4716, which is fixed by the new compiler (#8907) 2025-06-20 23:43:25 +00:00
Kim Morrison
a5eeed4f2c chore: a few missing grind typeclass docstrings (#8906) 2025-06-20 23:35:58 +00:00
Joachim Breitner
be80a23281 chore: remove unused simp args (#8905)
This PR uses the linter from
https://github.com/leanprover/lean4/pull/8901 to clean up simp
arguments.
2025-06-20 22:34:30 +00:00
Sebastian Ullrich
92ac564f3c fix: make mkHCongrWithArityForConst? compatible with parallelism (#8899)
This PR ensures the helper is compatible with using `grind` in
asynchronous proofs
2025-06-20 21:55:14 +00:00
Cameron Zwarich
0fcb6495d6 chore: add a test for #6957, fixed by the new compiler (#8904) 2025-06-20 21:44:09 +00:00
Lean stage0 autoupdater
e7c8baaef5 chore: update stage0 2025-06-20 18:52:57 +00:00
Cameron Zwarich
8d8c73416a chore: add a test for #2602, which was fixed by the new compiler (#8902) 2025-06-20 17:37:19 +00:00
Sebastian Graf
cf527e05bd feat: where ... finally section to assign leftover goals (#8723)
This PR implements a `finally` section following a (potentially empty)
`where` block. `where ... finally` opens a tactic sequence block in
which the goals are the unassigned metavariables from the definition
body and its auxiliary definitions that arise from use of `let rec` and
`where`.

This can be useful for discharging multiple proof obligations in the
definition body by a single invocation of a tactic such as `all_goals`:
```lean
example (i j : Nat) (xs : Array Nat) (hi : i < xs.size) (hj: j < xs.size) :=
  match i with
  | 0 => x
  | _ => xs[i]'?_ + xs[j]'?_
where x := 13
finally all_goals assumption
```

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-20 15:51:28 +00:00
Sebastian Graf
61ee83f73b feat: Upstream MPL.SPred.* from mpl (#8745)
This PR adds a logic of stateful predicates `SPred` to `Std.Do` in order
to support reasoning about monadic programs. It comes with a dedicated
proof mode the tactics of which are accessible by importing
`Std.Tactic.Do`.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-20 15:13:40 +00:00
Cameron Zwarich
26b7e49c05 chore: update stage0 2025-06-20 17:29:10 +02:00
Cameron Zwarich
466c9b56ba chore: add new tests for noncomputable 2025-06-20 17:29:10 +02:00
Cameron Zwarich
00474e17ff chore: add an extra test case to lean/run/noncomp.lean 2025-06-20 17:29:10 +02:00
Cameron Zwarich
891a2c6590 chore: reenable subset of new-compiler tests and delete others 2025-06-20 17:29:10 +02:00
Cameron Zwarich
d489c6196c chore: update expected test outputs
This makes it easier to distinguish tests that are actually
failing while we work on the new codegen.
2025-06-20 17:29:10 +02:00
Cameron Zwarich
6703af1ea0 chore: rename closed term suffix from _closedTerm to _closed
The longer name was chosen to avoid clashes with the old compiler.
2025-06-20 17:29:10 +02:00
Cameron Zwarich
7f8ccd8425 feat: enable the new compiler 2025-06-20 17:29:10 +02:00
Joachim Breitner
a8d5982fce chore: Init: clean up some simp calls (#8897)
This PR simplifies some `simp` calls.

These are the good parts of #8896.
2025-06-20 13:26:04 +00:00
Henrik Böving
50cfe354be chore: remove old LEAN_AUTO_THREAD_FINALIZATION workaround (#8885)
This PR removes an old workaround around non-implemented C++11 features
in the thread finalization.

This `ifdef` dates back to approximately 2015 as can be seen
[here](https://github.com/leanprover/lean3/blame/master/src/util/thread.cpp#L177),
the comments mention that it was originally implemented because not all
compilers at the time were able to support the C++11 `thread_local`
keyword. 10 years later this is hopefully the case and we can remove
this workaround.

There is an additional motivation for doing this,
`lean::initialize_thread` contains the following allocation:
```cpp
    g_thread_finalizers_mgr = new thread_finalizers_manager;
```
this is supposed to be freed at some point but:
```cpp
// TODO(gabriel): race condition with thread finalizers
void delete_thread_finalizer_manager() {
    // delete g_thread_finalizers_mgr;
    // g_thread_finalizers_mgr = nullptr;
}
```
so `g_thread_finalizers_mgr` leaks upon repeated invocation of
`lean::initialize_thread`.

Note that Windows has already been using this alternative implementation
for a while so the alternative implementation has (hopefully) not rotten
away in the meantime.
2025-06-20 08:52:17 +00:00
Kim Morrison
a750da5a7f chore: convert DHashMap to a structure (#8761)
This PR changes the definition of `DHashMap` to a structure. This makes
it more consistent with the other map types, which are generally defined
as structures. It also ensures that the type `DHashMap α β` is already
in weak head normal form, making it easier for `grind` to successfully
generate patterns for `DHashMap` lemmas.
2025-06-20 08:16:46 +00:00
Leonardo de Moura
588df4612a fix: missing isEqFalse (#8893)
This PR fixes a bug in the `dvd` propagation function in cutsat.
2025-06-20 08:16:08 +00:00
Kim Morrison
4ce18249d3 Merge branch 'IntModule_refactor' of github.com:leanprover/lean4 into IntModule_refactor 2025-06-20 18:14:31 +10:00
Miyahara Kō
dd78012ddd style: replace HEq x y with x ≍ y (#8872)
Although `HEq` was abbreviated as `≍` in #8503, many instances of the
form `HEq x y` still remain.
Therefore, I searched for occurrences of `HEq x y` using the regular
expression `(?<![A-Za-z/@]|``)HEq(?![A-Za-z.])` and replaced as many as
possible with the form `x ≍ y`.
2025-06-20 07:47:33 +00:00
Kim Morrison
1e69d88d6f merge master 2025-06-20 16:36:29 +10:00
Kim Morrison
db499e96aa feat: add doc-string to grind algebra typeclasses (#8890)
This PR adds doc-strings to the `Lean.Grind` algebra typeclasses, as
these will appear in the reference manual explaining how to extend
`grind` algebra solvers to new types. Also removes some redundant
fields.
2025-06-20 04:05:47 +00:00
Kim Morrison
c5ca9aa87c merge documentation PR 2025-06-20 13:56:09 +10:00
Kim Morrison
28f89c0567 feat: add doc-string to grind algebra typeclasses 2025-06-20 13:42:29 +10:00
jrr6
f416143fbc feat: improve error behavior of end command (#8387)
This PR improves the error messages produced by `end` and prevents
invalid `end` commands from closing scopes on failure.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-20 03:05:51 +00:00
Kim Morrison
743c60224a chore: minimize grind panic (#8889) 2025-06-20 01:07:14 +00:00
Kim Morrison
e6b5c45e04 Merge remote-tracking branch 'origin/master' into IntModule_refactor 2025-06-20 09:36:45 +10:00
Kim Morrison
8af3b89203 chore: @[expose] defs that appear in grind proof terms (#8882)
This PR adds `@[expose]` annotations to terms that appear in `grind`
proof certificates, so `grind` can be used in the module system. It's
possible/likely that I haven't identified all of them yet.
2025-06-19 22:39:50 +00:00
Kim Morrison
3710e4f176 fix 2025-06-19 21:05:15 +10:00
Siddharth
da9a536ffd feat: BitVec.msb_sdiv (#8178)
This PR provides a compact formula for the MSB of the sdiv. Most of the
work in the PR involves handling the corner cases of division
overflowing (e.g. `intMin / -1 = intMin`)

---------

Co-authored-by: Luisa Cicolini <48860705+luisacicolini@users.noreply.github.com>
Co-authored-by: Tobias Grosser <github@grosser.es>
2025-06-19 09:08:04 +00:00
Kim Morrison
ec9865dbd5 fix proof 2025-06-19 18:41:44 +10:00
Kim Morrison
a2b03b3efd merge master 2025-06-19 17:23:32 +10:00
Kim Morrison
0077dd3d55 chore: remove redundant field from Lean.Grind.IntModule (#8879) 2025-06-19 06:03:14 +00:00
Kim Morrison
63cfe908c5 feat: add grind annotations for List/Array/Vector monadic functions (#8878)
This PR adds grind annotations for List/Array/Vector monadic functions.
2025-06-19 05:10:43 +00:00
Kim Morrison
c796609159 feat: grind annotations for List/Array/Vector.attach/pmap (#8877)
This PR adds grind annotations for
`List/Array/Vector.attach/attachWith/pmap`.
2025-06-19 05:00:35 +00:00
Kim Morrison
827c69e46e feat: generalize Lean.Grind.IsCharP to semirings (#8847)
This PR relaxes the assumptions for `Lean.Grind.IsCharP` from `Ring` to
`Semiring`, and provides an alternative constructor for rings.
2025-06-19 04:39:53 +00:00
Cameron Zwarich
19d9f6c450 chore: remove brittle new compiler tests that depend on internal decls (#8875) 2025-06-19 03:56:14 +00:00
Kim Morrison
42eb3bb4b5 fix 2025-06-19 09:57:30 +10:00
Kim Morrison
f3f932ae8c oops 2025-06-19 09:52:53 +10:00
Kim Morrison
6c6a058beb fix 2025-06-19 09:39:05 +10:00
Kim Morrison
04113f2be5 Merge remote-tracking branch 'origin/master' into IntModule_refactor 2025-06-19 09:35:37 +10:00
Kim Morrison
2b393a3b88 no_int_zero_divisors 2025-06-19 09:35:28 +10:00
Cameron Zwarich
bec538cc57 chore: delete disabled new-compiler tests that are no longer very useful (#8873) 2025-06-18 21:18:58 +00:00
Kyle Miller
e74d3a2f1c chore: address stage0 update TODOs (#8869)
This PR addresses a few TODOs left in comments for things to do after a
stage0 update.
2025-06-18 20:52:50 +00:00
Paul Reichert
1b5a9be785 feat: ForIn' and size for iterators (#8768)
This PR introduces a `ForIn'` instance and a `size` function for
iterators in a minimal fashion. The `ForIn'` instance is not marked as
an instance because it is unclear which `Membership` relation is
sufficiently useful. The `ForIn'` instance existing as a `def` and
inducing the `ForIn` instance, it becomes possible to provide more
specialized `ForIn'` instances, with nice `Membership` relations, for
various types of iterators. The `size` function has no lemmas yet.
2025-06-18 19:41:20 +00:00
Marc Huisinga
aea8e11d4b fix: restore code action incrementality (#8868)
This PR ensures that code actions do not have to wait for the full file
to elaborate. This regression was accidentally introduced in #7665.
2025-06-18 18:00:20 +00:00
Lean stage0 autoupdater
935aa38603 chore: update stage0 2025-06-18 18:08:31 +00:00
jrr6
e5c6fe1dac feat: add elaborators, completions, and hovers for named errors (#8730)
This PR adds support for throwing named errors with associated error
explanations. In particular, it adds elaborators for the syntax defined
in #8649, which use the error-explanation infrastructure added in #8651.
This includes completions, hovers, and jump-to-definition for error
names.

Note that another stage0 rebuild will be required to define explanations
using `register_error_explanation`.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-06-18 15:51:34 +00:00
Luisa Cicolini
62f3ee2887 feat: add leading zero counter BitVec.clz and bitblaster circuit/infrastructure (#8546)
This PR adds a new `BitVec.clz` operation and a corresponding `clz`
circuit to `bv_decide`, allowing to bitblast the count leading zeroes
operation. The AIG circuit is linear in the number of bits of the
original expression, making the bitblasting convenient wrt. rewriting.
`clz` is common in numerous compiler intrinsics (see
[here](https://clang.llvm.org/docs/LanguageExtensions.html#intrinsics-support-within-constant-expressions))
and architectures (see
[here](https://en.wikipedia.org/wiki/Find_first_set)).

Co-authored by @bollu.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-18 15:50:04 +00:00
Sebastian Ullrich
e8c82610cd refactor: make syntax covering snapshot tasks more precise on the top level (#8744) 2025-06-18 13:23:21 +00:00
Paul Reichert
86eded35db refactor: partially move iterators to Init (#8767)
This PR moves parts of the iterator library from `Std` to `Init`. The
reason is that the polymorphic range API must be in `Init` and it
depends on the iterators.
2025-06-18 10:08:04 +00:00
Lean stage0 autoupdater
f0fdab86bb chore: update stage0 2025-06-18 10:07:05 +00:00
Kim Morrison
d58e253671 chore: add missing List.eraseIdx_insertIdx deprecation (#8863) 2025-06-18 08:48:00 +00:00
Kim Morrison
e1ecc150e3 rfl 2025-06-18 18:22:49 +10:00
Kim Morrison
76fcd276c6 merge master 2025-06-18 18:20:30 +10:00
Kim Morrison
d0c1053903 chore: add test case for grind panic (#8861)
This PR adds a (failing) test case for a panic caused by grind.
2025-06-18 08:00:17 +00:00
Kim Morrison
48a0e742d8 chore: Lean.Grind.IntModule instances (#8859)
This PR shows the equivalence between `Lean.Grind.NatModule.IsOrdered`
and `Lean.Grind.IntModule.IsOrdered` over an `IntModule`.
2025-06-18 07:30:37 +00:00
Sebastian Ullrich
d131cf39c1 fix: set public aux decl prefix in init_grind_norm (#8856)
This PR ensures simp theorems generated by `init_grind_norm` are
accessible in other `module`s
2025-06-18 07:19:37 +00:00
Markus Himmel
c16204615d chore: add a failing grind test (#8858) 2025-06-18 07:14:56 +00:00
Mac Malone
e83b768140 feat: lake: reintroduce lean --setup basics (#8846)
This PR reintroduces the basics of `lean --setup` integration into Lake
without the module computation which is still undergoing performance
debugging in #8787.

Partially reverts #8736 and partially reimplements #8447.
2025-06-18 06:12:39 +00:00
Kim Morrison
705769f466 hrmm 2025-06-18 15:25:02 +10:00
Kim Morrison
cd346a360e more 2025-06-18 15:09:37 +10:00
Kyle Miller
6240cd5aa9 feat: make sure clear_value preserves local context order (#8792)
This PR makes the `clear_value` tactic preserve the order of variables
in the local context. This is done by adding
`Lean.MVarId.withRevertedFrom`, which reverts all local variables
starting from a given variable, rather than only the ones that depend on
it.

Note: an alternative implementation might convert the ldecl to a cdecl
and then reset the meta cache. This assumes that there are no other
caches that might still remember the value of the ldecl.
2025-06-18 04:40:20 +00:00
Kim Morrison
cfa38b055b chore: refactor of Lean.Grind.IntModule.IsOrdered 2025-06-18 14:23:28 +10:00
Kim Morrison
e9086533ed step1 2025-06-18 14:09:18 +10:00
Kim Morrison
cf47e5f6a7 feat: generalize grind IsCharP instance (#8848)
This PR generalizes the internal `grind` instance 
```
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0
```
to 
```
instance [Ring α] [Preorder α] [Ring.IsOrdered α] : IsCharP α 0
```
2025-06-18 02:49:26 +00:00
Kim Morrison
16e67dc738 feat: grind annotations for Nat.Bitwise (#8852)
This PR adds grind annotations for `Nat.testBit` and bitwise operations
on `Nat`.

(Also includes some in-progress tests for `BitVec`.)
2025-06-18 02:42:43 +00:00
Kim Morrison
5810f8907d feat: grind annotations relating Nat/Fin fold operations to List (#8853)
This PR adds `grind` annotations relating `Nat.fold/foldRev/any/all` and
`Fin.foldl/foldr/foldlM/foldrM` to the corresponding operations on
`List.finRange`.
2025-06-18 02:42:32 +00:00
Kim Morrison
22e8b476ba feat: grind annotations for Function.(un)curry (#8851)
This PR adds grind annotations for `Function.curry`/`uncurry`.
2025-06-18 02:41:00 +00:00
Kim Morrison
3a8258b2d5 feat: grind annotations for Prod (#8850)
This PR adds `grind` annotations for `Prod`.
2025-06-18 02:40:23 +00:00
Kim Morrison
aa9f966aee feat: grind annotations for Sum (#8849)
This PR adds `grind` annotations for `Sum`.
2025-06-18 02:21:17 +00:00
Sebastian Ullrich
e129e75e66 chore: CI: temporarily disable .olean cache 2025-06-18 11:12:56 +09:00
Lean stage0 autoupdater
04c273dbc6 chore: update stage0 2025-06-18 02:09:11 +00:00
Leonardo de Moura
2b39b453e7 feat: proof-by-reflection support for converting semiring terms into ring ones (#8845)
This PR implements the proof-by-reflection infrastructure for embedding
semiring terms as ring ones.
2025-06-17 19:24:15 +00:00
Luisa Cicolini
43aaae7348 feat: add BitVec.(toNat, toInt, toFin)_shiftLeftZeroExtend (#8811)
This PR adds theorems `BitVec.(toNat, toInt,
toFin)_shiftLeftZeroExtend`, completing the API for
`BitVec.shiftLeftZeroExtend`.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-06-17 17:43:40 +00:00
Parth Shastri
92dec7e864 feat: allow structures to have non-bracketed binders (#8671)
This PR allow structures to have non-bracketed binders, making it
consistent with `inductive`.

The change allows the following to be written instead of having to write
`S (n)`:
```lean
structure S n where
  field : Fin n
```
2025-06-17 17:40:18 +00:00
Kim Morrison
b3a53d5d01 feat: generalize embedding of CommSemiring into its CommRing envelope to the noncommutative case (#8836)
This PR generalizes #8835 to the noncommutative case, allowing us to
embed a `Lean.Grind.Semiring` into a `Lean.Grind.Ring`.
2025-06-17 09:09:05 +00:00
Sebastian Ullrich
3b2990b381 chore: CI: work around test-speedcenter breaking on ubuntu-latest 2025-06-17 18:14:12 +09:00
Parth Shastri
17b133369d refactor: remove binductionOn, use brecOn instead (#8820)
This PR removes the auto-generated `binductionOn` and `ibelow`
implementations for inductive types in favor of the improved `brecOn`
implementation from #7639.
2025-06-17 07:07:24 +00:00
Kim Morrison
259e2ec3e8 feat: define the CommRing envelope of a CommSemiring (#8835)
This PR defines the embedding of a `CommSemiring` into its `CommRing`
envelope, injective when the `CommSemiring` is cancellative. This will
be used by `grind` to prove results in `Nat`.
2025-06-17 05:39:14 +00:00
Cameron Zwarich
c9d0af1d7e chore: delete tests/lean/run/CompilerProbe.lean (#8833)
This test is essentially disabled on `master`, because it prints
nothing. With the new compiler enabled, it prints names of functions
throughout the Lean codebase satisfying certain conditions. Even just
maintaining this on the new compiler branch got old pretty quickly, so I
can't imagine we'd ever want to deal with this on `master`.
2025-06-17 05:01:12 +00:00
Cameron Zwarich
471553102c chore: delete redundant copy of unhygienicCode test (#8832)
This copy in `lean/new-compiler` is the same as the copy In `lean`, just
with different IR printing settings.
2025-06-17 04:57:07 +00:00
Cameron Zwarich
18caad9756 fix: cache scalar type info in toIR (#8831)
This PR caches the result of `lowerEnumToScalarType`, which is used
heavily in LCNF to IR conversion.
2025-06-17 04:31:33 +00:00
Kim Morrison
f557bf6024 chore: move grind algebra instances into Init.GrindInstances (#8830)
This PR rearranges files under `Init.Grind`, moving out instances for
concrete algebraic types in `Init.GrindInstances`.
2025-06-17 03:59:15 +00:00
Kim Morrison
548cc4e555 chore: reorganize BitVec files (#8829)
This PR avoids importing all of `BitVec.Lemmas` and `BitVec.BitBlast`
into `UInt.Lemmas`. (They are still imported into `SInt.Lemmas`; this
seems much harder to avoid.)
2025-06-17 03:30:35 +00:00
Kim Morrison
38fb9c5328 chore: rename BitVec.getLsb' back to BitVec.getLsb (#8827)
This PR renames `BitVec.getLsb'` to `BitVec.getLsb`, now that older
deprecated definition occupying that name has been removed. (Similarly
for `BitVec.getMsb'`.)
2025-06-17 01:28:07 +00:00
Kim Morrison
ba39fd3ca8 fix: correct Lean.Grind.NatModule (#8826)
This PR corrects the definition of `Lean.Grind.NatModule`, which wasn't
previously useful.
2025-06-17 01:00:48 +00:00
Kim Morrison
fcb3b2ec66 chore: raise internal grind limits to allow examples (#8807) 2025-06-17 01:00:17 +00:00
Cameron Zwarich
e1408d29bc fix: improve IR for inductive types represented as scalars (#8825)
This PR improves IR generation for constructors of inductive types that
are represented by scalars. Surprisingly, this isn't required for
correctness, because the boxing pass will fix it up. The extra `unbox`
operation it inserts shouldn't matter when compiling to native code,
because it's trivial for a C compiler to optimize, but it does matter
for the interpreter.
2025-06-16 23:52:50 +00:00
Cameron Zwarich
9e913a29de chore: remove redundant headBeta call (#8824) 2025-06-16 23:13:07 +00:00
Cameron Zwarich
46c3eaece9 fix: add a cache for constructor info in toIR (#8822)
This PR adds a cache for constructor info in toIR. This is called for
all constructors, projections, and cases alternatives, so it makes sense
to cache.
2025-06-16 22:56:27 +00:00
Joachim Breitner
47c294b3a9 chore: make Linux Release CI job secondary (#8818)
Follow-up to #8817.
2025-06-16 21:29:07 +00:00
Joachim Breitner
5568e06160 chore: Run “Linux release” for PRs as secondary job (#8817)
This PR reifnes #8739 to make sure we get a linux release in the PR
release.
2025-06-16 20:45:10 +00:00
Parth Shastri
ed4195778d chore: remove unused syntax (#8760)
Removes unused `tailrecursion` syntax.
2025-06-16 20:36:56 +00:00
Cameron Zwarich
997892d49a fix: constant fold Char.ofNat in LCNF simp (#8816)
This PR adds constant folding for Char.ofNat in LCNF simp. This
implicitly relies on the representation of `Char` as `UInt32` rather
than making a separate `.char` literal type, which seems reasonable as
`Char` is erased by the trivial structure optimization in `toMono`.
2025-06-16 17:48:55 +00:00
Parth Shastri
e07ed1ae5c chore: add missing instance (#8772)
Changes `ReverseImplicationOrder.instCompleteLattice` to be an
`instance`.
2025-06-16 15:44:56 +00:00
Kim Morrison
d247297214 feat: lemmas about ordered modules (#8813)
This PR adds some basic lemmas about `grind` internal notions of
modules.
2025-06-16 13:05:38 +00:00
Sebastian Ullrich
242429a262 chore: CI: provide more than 8GB RAM (#8812)
We started running into OOMs in the test suite. This is the faster
alternative to lowering test parallelism.
2025-06-16 11:58:06 +00:00
Kim Morrison
d9b2a5e9f7 feat: additional grind annotations for List/Array/Vector lemmas (#8805)
This PR continues adding `grind` annotations for `List/Array/Vector`
lemmas.
2025-06-16 11:00:51 +00:00
Leonardo de Moura
4e96a4ff45 feat: eliminate equations in grind linarith (#8810)
This PR implements equality elimination in `grind linarith`. The current
implementation supports only `IntModule` and `IntModule` +
`NoNatZeroDivisors`
2025-06-16 09:31:13 +00:00
Kim Morrison
7b67727067 feat: do not report metaprogramming declarations via exact? and rw? (#6672)
This PR filters out all declarations from `Lean.*`, `*.Tactic.*`, and
`*.Linter.*` from the results of `exact?` and `rw?`.

---------

Co-authored-by: damiano <adomani@gmail.com>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-06-16 09:20:49 +00:00
David Thrane Christiansen
8ed6824b75 chore: follow up on #8173 post-stage0 update (#8722)
This PR un-does the temporary changes made in #8173 for bootstrapping
purposes.
2025-06-16 09:08:35 +00:00
Kim Morrison
fdf6d2ea3b feat: basic theory of ordered modules over Nat (#8809)
This PR introduces the basic theory of ordered modules over Nat (i.e.
without subtraction), for `grind`. We'll solve problems here by
embedding them in the `IntModule` envelope.
2025-06-16 06:46:03 +00:00
Kim Morrison
dc531a1740 feat: missing Nat lemmas (#8808)
This PR adds the missing `le_of_add_left_le {n m k : Nat} (h : k + n ≤
m) : n ≤ m` and `le_add_left_of_le {n m k : Nat} (h : n ≤ m) : n ≤ k +
m`.
2025-06-16 06:43:37 +00:00
Kim Morrison
ddff851294 chore: cleanup of grind tests (#8806) 2025-06-16 02:47:46 +00:00
Cameron Zwarich
db414957a0 chore: fix if/else indentation (#8803) 2025-06-15 23:03:52 +00:00
Kim Morrison
114fa440f0 feat: grind annotations for List.Perm (#8765)
This PR adds grind annotations for `List.Perm`; involves a revision of
grind annotations for `List.countP/count` as well.
2025-06-15 23:01:29 +00:00
Cameron Zwarich
aa988bb892 fix: prevent floatLetIn from artificially blocking code motion (#8802)
This PR fixes a bug in `floatLetIn` where if one decl (e.g. a join
point) is floated into a case arm and it uses another decl (e.g. another
join point) that does not have any other existing uses in that arm, then
the second decl does not get floated in despite this being perfectly
legal. This was causing artificial array linearity issues in
`Lean.Elab.Tactic.BVDecide.LRAT.trim.useAnalysis`.
2025-06-15 22:19:38 +00:00
Leonardo de Moura
e2a947c2e6 feat: track occurrences in linarith (#8801)
This PR implements the infrastructure for variable elimination in the
`grind linarith` procedure.
2025-06-15 18:21:50 +00:00
Leonardo de Moura
26946ddc7f feat: Inv.lean for grind linarith (#8800) 2025-06-15 17:50:43 +00:00
Cameron Zwarich
0bfd95dd20 chore: improve readability of map/fold calls (#8799) 2025-06-15 14:15:11 +00:00
Sebastian Ullrich
957b904ef9 chore: revert "fix: add terminfo for structure fields (#8568)"
This reverts commit 021c21a273 because of a stage 2 linter failure.
2025-06-15 13:39:01 +02:00
Leonardo de Moura
1835f190c7 feat: add instance IsCharP R 0 for a linear ordered field R (#8798)
This PR adds the following instance
```
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0
```
The goal is to ensure we do not perform unnecessary case-splits in our
test suite.
2025-06-15 05:04:58 +00:00
Leonardo de Moura
f86560d134 fix: grind bogus warning and missing normalization rule (#8797)
This PR adds small fixes
2025-06-15 03:44:53 +00:00
Leonardo de Moura
cc3dafe67a fix: grind linarith internalization and HSMul support (#8796)
This PR fixes `grind linarith` term internalization and support for
`HSMul`.
2025-06-15 02:34:42 +00:00
Leonardo de Moura
5bc5d31fd9 fix: grind ring + linarith internalization (#8795)
This PR ensures that auxliary terms are not internalized by the ring and
linarith modules.
2025-06-15 01:49:37 +00:00
Leonardo de Moura
ac0c59caae feat: enable linarith even if no order is available (#8791)
This PR ensures the `grind linarith` module is activated for any type
that implements only `IntModule`. That is, the type does not need to be
a preorder anymore.
2025-06-15 01:20:49 +00:00
Mac Malone
2a8cd373ca feat: respect lean --setup module name in code generation (#8780)
This PR makes Lean code generation respect the module name provided
through `lean --setup`.

This is accomplished by porting to Lean the portion of `shell.cpp` that
spans running the frontend to exiting the process. This makes it easier
to load the module setup and control how its name is passed to the code
generation functions. This port attempts to minimize the changes made to
Lean. It marks the new Lean functions `private` and tries to preserve as
faithfully as possible the behavior of the original C++ code. Exposing
the new Lean interface publicly and/or further improving the code now
that is written in Lean is left for the future.
2025-06-15 01:11:58 +00:00
Kyle Miller
ef6386b8a9 feat: Expr.collectLooseBVars (#8794)
This PR adds a module `Lean.Util.CollectLooseBVars` with a function
`Expr.collectLooseBVars` that collects the set of loose bound variables
in an expression. That is, it computes the set of all `i` such that
`e.hasLooseBVar i` is true.
2025-06-15 00:16:43 +00:00
Kyle Miller
021c21a273 fix: add terminfo for structure fields (#8568)
This PR modifies the `structure` elaborator to add local terminfo for
structure fields and explicit parent projections, enabling "go to
definition" when there are dependent fields.

Terminfo for inherited fields is still missing.
2025-06-15 00:11:47 +00:00
Cameron Zwarich
286ddf5e28 chore: fix confusing indentation (#8793) 2025-06-15 00:07:48 +00:00
Kyle Miller
97bc609e77 feat: add have forms of let_* simp lemmas (#8790)
This PR adds `have` forms of simp lemmas that will be used in a future
`have` simplifier. This depends on #8751 and future elaboration changes,
since these are meant to elaborate using `Expr.letE (nondep := true) ..`
expressions; for now they are duplicates of the `letFun_*` lemmas.
2025-06-14 23:15:10 +00:00
Kyle Miller
cdc923167e feat: add the nondep field of Expr.letE to the C++ data model (#8751)
This PR adds the `nondep` field of `Expr.letE` to the C++ data model.
Previously this field has been unused, and in followup PRs the
elaborator will use it to encode `have` expressions (non-dependent
`let`s). The kernel does not verify that `nondep` is correctly applied
during typechecking. The `letE` delaborator now prints `have`s when
`nondep` is true, though `have` still elaborates as `letFun` for now.
Breaking change: `Expr.updateLet!` is renamed to `Expr.updateLetE!`.

This PR also fixes a bug in `Expr.letFun?` and `Expr.letFunAppArgs?`
when the body is not a lambda. In any case, these functions will be
removed once the `Expr.letE (nondep := true)` encoding of `have`
expressions is complete.
2025-06-14 23:10:27 +00:00
Leonardo de Moura
1d971c8735 feat: Rabinowitsch transformation in grind (#8789)
This PR implements the Rabinowitsch transformation for `Field`
disequalities in `grind`. For example, this transformation is necessary
for solving:
```lean
example [Field α] (a : α) : a^2 = 0 → a = 0 := by
  grind
```
2025-06-14 22:22:40 +00:00
Kyle Miller
82c2c4cd51 feat: add zetaHave/letToHave simp options (#8788)
This PR adds the `zetaHave` and `letToHave` options to `simp`.
Implementations will appear in future PRs.
2025-06-14 21:26:36 +00:00
Leonardo de Moura
019ea2a74b feat: improve support for Field in grind (#8786)
This PR improves the support for fields in `grind`. New supported
examples:
```lean
example [Field α] [IsCharP α 0] (x : α) : x ≠ 0 → (4 / x)⁻¹ * ((3 * x^3) / x)^2 * ((1 / (2 * x))⁻¹)^3 = 18 * x^8 := by grind
example [Field α] (a : α) : 2 * a ≠ 0 → 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a b : α) : 2*b - a = a + b → 1 / a + 1 / (2 * a) = 3 / b := by grind
example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] {x y z w : α} : x / y = z / w → y ≠ 0 → w ≠ 0 → x * w = z * y := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 - a := by grind
```
2025-06-14 19:29:02 +00:00
Sebastian Ullrich
ec9ff12fc6 fix: meta tag can be added async (#8783) 2025-06-14 11:19:35 +00:00
Cameron Zwarich
444595878b chore: improve clarity in a match expression (#8781) 2025-06-14 00:53:12 +00:00
Cameron Zwarich
3d3aa98c83 chore: use FVarIdHashSet for the visited set in LCNF closure computation (#8779) 2025-06-13 23:39:16 +00:00
Cameron Zwarich
27080dca35 chore: use FVarIdHashSet in LCNF collectUsed (#8778) 2025-06-13 22:55:15 +00:00
Leonardo de Moura
aef4a29148 feat: Field support in grind ring (#8777)
This PR implements basic `Field` support in the commutative ring module
in `grind`. It is just division by numerals for now. Examples:
```lean
open Lean Grind

example [Field α] [IsCharP α 0] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c  := by
  grind

example [Field α] (a b : α) : b = 0 → (a + a) / 0 = b := by
  grind

example [Field α] [IsCharP α 3] (a b : α) : a/3 = b → b = 0 := by
  grind

example [Field α] [IsCharP α 7] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c + 7 := by
  grind

example [Field R] [IsCharP R 0] (x : R) (cos : R → R) :
    (cos x ^ 2 + (2 * cos x ^ 2 - 1) ^ 2 + (4 * cos x ^ 3 - 3 * cos x) ^ 2 - 1) / 4 =
      cos x * (cos x ^ 2 - 1 / 2) * (4 * cos x ^ 3 - 3 * cos x) := by
  grind
```
2025-06-13 22:42:49 +00:00
Parth Shastri
5d50433e6a fix: allow arbitrary sorts in structural recursion over reflexive inductive types (#7639)
This PR changes the generated `below` and `brecOn` implementations for
reflexive inductive types to support motives in `Sort u` rather than
`Type u`.

Closes #7638
2025-06-13 21:51:09 +00:00
Rob23oba
812bab6910 chore: convert ExtDHashMap into a one-field structure (#8770) 2025-06-13 20:22:20 +00:00
Leonardo de Moura
ff6eb56f5c fix: natCast in grind cutsat (#8776)
This PR ensures that user provided `natCast` application are properly
internalized in the grind cutsat module.
2025-06-13 17:56:00 +00:00
Leonardo de Moura
4b7ea26d91 fix: add grind normalization theorem for Int.negSucc (#8775)
This PR adds a `grind` normalization theorem for `Int.negSucc`. Example:

```lean
example (p : Int) (n : Nat) (hmp : Int.negSucc (n + 1) + 1 = p)
    (hnm : Int.negSucc (n + 1 + 1) + 1 = Int.negSucc (n + 1)) : p = Int.negSucc n := by
  grind
```
2025-06-13 16:53:42 +00:00
Leonardo de Moura
32eedc2c22 feat: grind -cutsat (#8774)
This PR adds an option for disabling the cutsat procedure in `grind`.
The linarith module takes over linear integer/nat constraints. Example:

```lean
set_option trace.grind.cutsat.assert true in -- cutsat should **not** process the following constraints
example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) : ¬ 12*y - 4* z < 0 := by
  grind -cutsat -- `linarith` module solves it
```
2025-06-13 16:40:44 +00:00
Leonardo de Moura
95e532a536 feat: heterogeneous (k : Nat) * (a : R) support in grind linarith (#8773)
This PR implements support for the heterogeneous `(k : Nat) * (a : R)`
in ordered modules. Example:
```lean
variable (R : Type u) [IntModule R] [LinearOrder R] [IntModule.IsOrdered R]

example (x y z : R) (hx : x ≤ 3 * y) (h2 : y ≤ 2 * z) (h3 : x ≥ 6 * z) : x = 3 * y := by
  grind

example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) (h3 : x * y < 5) : ¬ 12*y - 4* z < 0 := by
  grind
```
2025-06-13 16:18:24 +00:00
plp127
cceabbbe7e fix: quoting single quote Char (''') (#8742)
This PR fixes a bug where the single-quote character `Char.ofNat 39`
would delaborate as `'''`, which causes a parse error if pasted back in
to the source code.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2025-06-13 15:29:11 +00:00
Cameron Zwarich
8019c6cc32 chore: add .dSYM files (Mac debug symbols) to tests .gitignore files (#8771) 2025-06-13 15:27:46 +00:00
Parth Shastri
5390cdbee1 fix: correctly handle explicit monotonicity proofs in mutual definitions (#8763)
This PR corrects the handling of explicit `monotonicity` proofs for
mutual `partial_fixpoint` definitions.
2025-06-13 15:04:13 +00:00
Rob23oba
e713232623 fix: resolve symbolic links through IO.FS.realPath on windows (#8534)
This PR fixes `IO.FS.realPath` on windows to take symbolic links into
account.

Closes #810
2025-06-13 13:16:13 +00:00
Justin King
0d0da768d8 perf: update free_sized declaration to be compatible with glibc (#8661)
glibc adds `__attribute__((nothrow))` to its declarations, at least for
those related to malloc. glibc has yet to introduce `free_sized`, but
when it does it would cause compilation errors. This is due to the fact
that if a function declarations has `__attribute__((nothrow))` and it is
re-declared or implemented in C++ it must also have
`__attribute__((nothrow))` or `noexcept`, otherwise the compilation will
fail.

This is a follow up to https://github.com/leanprover/lean4/pull/6598.

Signed-off-by: Justin King <jcking@google.com>
2025-06-13 13:13:00 +00:00
Sebastian Ullrich
3feb63231e chore: merge-checkout test fixed and removed from exclusions on master 2025-06-13 15:30:58 +02:00
Sebastian Ullrich
121ce56506 chore: CI: make "Linux Lake" primary PR CI job (#8739)
Comes with .olean caching and module system-powered short-circuiting
2025-06-13 11:27:49 +00:00
Luisa Cicolini
300c22a4e6 feat: associativity lemmas for BitVec.(umul, smul, uadd, sadd)Overflow (#8740)
This PR introduces associativity rules and preservation of `(umul, smul,
uadd, sadd)Overflow`flags.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-13 09:07:09 +00:00
Cameron Zwarich
f247f2bdd0 fix: run LCNF checks less often by default (#8764)
This PR changes the LCNF pass pipeline so checks are no longer run by
default after every pass, only after `init`, `saveBase`, `toMono` and
`saveMono`. This is a compile time improvement, and the utility of these
checks is decreased a bit after the decision to no longer attempt to
preserve types throughout compilation. They have not been a significant
way to discover issues during development of the new compiler.
2025-06-13 05:39:21 +00:00
Kim Morrison
db5bd5a205 chore: missing easy Int lemmas (#8762) 2025-06-13 04:20:47 +00:00
Leonardo de Moura
140a633589 feat: model based theory combination for grind mbtc (#8759)
This PR implements model-based theory combination for grind linarith.
Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (f : α → α → α) (x y z : α)
    : z ≤ x → x ≤ 1 → z = 1 → f x y = 2 → f 1 y = 2 := by
  grind
```
2025-06-13 01:20:45 +00:00
Cameron Zwarich
3aa479fd8c fix: cache TrivialStructureInfo in LCNF toMono (#8758)
This PR adds caching for the `hasTrivialStructure?` function for LCNF
types. This is one of the hottest small functions in the new compiler,
so adding a cache makes a lot of sense.
2025-06-13 01:07:38 +00:00
Kim Morrison
b280b83c98 chore: add test case with bad grind pattern (#8757) 2025-06-13 01:06:02 +00:00
Kyle Miller
84f15ac93a fix: refine how simp tracks unfolded local definitions (#8753)
This PR fixes a bug in `simp` where it was not resetting the set of
zeta-delta reduced let definitions between `simp` calls. It also fixes a
bug where `simp` would report zeta-delta reduced let definitions that
weren't given as simp arguments (these extraneous let definitions appear
due to certain processes temporarily setting `zetaDelta := true`). This
PR also modifies the metaprogramming interface for the zeta-delta
tracking functions to be re-entrant and to prevent this kind of no-reset
bug from occurring again. Closes #6655.

Re-entrance of this metaprogramming interface is not needed to fix
#6655, but it is needed for some future PRs.

The `tests/lean/run/6655.lean` file has an example of a deficiency of
`simp?`, where `simp?` still over-reports unfolded let declarations.
This is likely due to `withInferTypeConfig` setting `zetaDelta := true`
from within `isDefEq`, but I did not verify this.

This PR supersedes #7539. The difference is that this PR has
`withResetZetaDeltaFVarIds` save and restore `zetaDeltaFVarIds`, but
that PR saves and then extends `zetaDeltaFVarIds` to persist unfolded
fvars. The behavior in this PR lets metaprograms control whether they
want to persist any of the unfolded fvars in this context themselves. In
practice, metaprograms that use `withResetZetaDeltaFVarIds` are creating
many temporary fvars and are doing dependence computations. These
temporary fvars shouldn't be persisted, and also dependence shouldn't be
inferred from the fact that a dependence calculation was done. (Concrete
example: the let-to-have transformation in an upcoming PR can be run
from within simp. Just because let-to-have unfolds an fvar while
calculating dependencies of lets doesn't mean that this fvar should be
included by `simp?`.)
2025-06-13 00:57:57 +00:00
Leonardo de Moura
d4b17b9fd2 feat: counterexamples for grind linarith module (#8756)
This PR implements counterexamples for grind linarith. Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b c d : α)
    : b ≥ 0 → c > b → d > b → a ≠ b + c → a > b + c → a < b + d →  False := by
  grind
```
produces the counterexample
```
a := 7/2
b := 1
c := 2
d := 3
```

```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → b = c → a ≠ b → False := by
  grind
```
generates the counterexample
```
a := 0
b := 1
c := 1
d := -1
```
2025-06-13 00:21:35 +00:00
Cameron Zwarich
4694aaad02 chore: rewrite mkFieldParamsForCtorType in a more readable style (#8755) 2025-06-12 23:54:30 +00:00
Rob23oba
e450a02621 fix: change show tactic to work as documented (#7395)
This PR changes the `show t` tactic to match its documentation.
Previously it was a synonym for `change t`, but now it finds the first
goal that unifies with the term `t` and moves it to the front of the
goal list.
2025-06-12 23:54:09 +00:00
Cameron Zwarich
deda28e6e3 fix: enable more optimizations on inductives with computed fields in the new compiler (#8754)
This PR changes the implementation of computed fields in the new
compiler, which should enable more optimizations (and remove a
questionable hack in `toLCNF` that was only suitable for bringup). We
convert `casesOn` to `cases` like we do for other inductive types, all
constructors get replaced by their real implementations late in the base
phase, and then the `cases` expression is rewritten to use the real
constructors in `toMono`.

In the future, it might be better to move to a model where the `cases`
expression gets rewritten earlier or the constructors get replaced
later, so that both are done at the same time.
2025-06-12 23:28:09 +00:00
Cameron Zwarich
8aa003bdfc fix: move structProjCases pass before extendJoinPointContext (#8752)
This PR fixes an issue where the `extendJoinPointContext` pass can lift
join points containing projections to the top level, as siblings of
`cases` constructs matching on other projections of the same base value.
This prevents the `structProjCases` pass from projecting both at once,
extending the lifetime of the parent value and breaking linearity at
runtime.

This would theoretically be possible to fix in `structProjCases`, but it
would require some better infrastructure for handling join points. It's
also likely that the IR passes dealing with reference counting would
have similar bugs that pessimize the code. For this reason, the simplest
thing is to just perform the `structProjCases` pass earlier, which
prevents `extendJoinPointContext` from lifting these join points.
2025-06-12 21:52:02 +00:00
Kim Morrison
6a698c1c22 feat: grind annotations for List/Array/Vector.zip functions (#8750)
This PR adds grind annotations for the
`List/Array/Vector.zipWith/zipWithAll/unzip` functions.
2025-06-12 18:41:24 +00:00
Kim Morrison
b4660c96a9 feat: grind annotations for List/Array/Vector.ofFn theorems and List.Impl (#8749)
This PR adds grind annotations for `List/Array/Vector.ofFn` theorems and
additional `List.Impl` find operations.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for ofFn operations and
related functionality.

Key theorems annotated include:
- Element access theorems (`getElem_ofFn`, `getElem?_ofFn`)
- Construction and conversion theorems (`ofFn_zero`, `toList_ofFn`,
`toArray_ofFn`)
- Membership theorems (`mem_ofFn`)
- Head/tail operations (`back_ofFn`)
- Monadic operations (`ofFnM_zero`, `toList_ofFnM`, `toArray_ofFnM`,
`idRun_ofFnM`)
- List.Impl find operations (`find?_singleton`, `find?_append`,
`findSome?_singleton`, `findSome?_append`)
2025-06-12 18:09:08 +00:00
Kim Morrison
2cddf2394b feat: grind annotations for List/Array/Vector.mapIdx theorems (#8748)
This PR adds grind annotations for `Array/Vector.mapIdx` and `mapFinIdx`
theorems.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for indexed mapping
operations.

Key theorems annotated include:
- Size and element access theorems (`size_mapIdx`, `getElem_mapIdx`,
`getElem?_mapIdx`)
- Construction theorems (`mapIdx_empty`, `mapIdx_push`, `mapIdx_append`)
- Membership and equality theorems (`mem_mapIdx`, `mapIdx_mapIdx`)
- Conversion theorems (`toList_mapIdx`, `mapIdx_toArray`, etc.)
- Reverse and composition operations
- Similar annotations for `mapFinIdx` variants
2025-06-12 18:06:01 +00:00
Kim Morrison
75fe50a33e feat: grind annotations for List/Array/Vector.finRange theorems (#8747)
This PR adds grind annotations for \`List/Array/Vector.finRange\`
theorems.
2025-06-12 17:49:58 +00:00
Sebastian Ullrich
c2876a1a6a chore: update stage0 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
9f6846a343 chore: work around old compiler bug 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
64e105c121 feat: meta phase restrictions 2025-06-12 16:36:08 +02:00
Kim Morrison
d10a85539a feat: grind annotations for List/Array/Vector.find?/findSome?/idxOf?/findIdx? (#8741)
This PR adds annotations for
`List/Array/Vector.find?/findSome?/idxOf?/findIdx?`.
2025-06-12 11:06:18 +00:00
Sebastian Ullrich
f0347ee719 chore: lean --stats gives number of imported bytes (#8725)
Thanks to `mmap`, startup time is not necessarily related to this
figure, but it can be used as a rough measure for that and how much data
the module depends on, i.e. the rebuild chance.

Also adds new cumulative benchmarks for this metric as well as the
number of imported constants and env ext entries.
2025-06-12 08:29:42 +00:00
Kim Morrison
faffe86334 chore: add failing grind tests from Mathlib (#8737) 2025-06-12 05:57:32 +00:00
Mac Malone
c168d06edf chore: partially revert "feat: lake: use lean --setup" (#8736)
This PR partially reverts #8024 which introduced a significant Lake
performance regression during builds. Once the cause is discovered and
fixed, a similar PR will be made to revert this.
2025-06-12 05:53:59 +00:00
Kim Morrison
abfc49d0f7 chore: cleanup of grind tests (#8735) 2025-06-12 04:42:25 +00:00
Kim Morrison
34e98c2efc feat: add Decidable (∃ i, P i) (#8734)
This PR adds the missing instance
```
instance decidableExistsFin (P : Fin n → Prop) [DecidablePred P] : Decidable (∃ i, P i)
```
2025-06-12 02:58:37 +00:00
Leonardo de Moura
e7549b5651 feat: diseq splitting and non-chronological backtracking for linarith (#8733)
This PR implements disequality splitting and non-chronological
backtracking for the `grind` linarith procedure.
```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → d ≥ 0 → b = c → a ≠ b → False := by
  grind
```
2025-06-12 02:49:35 +00:00
Cameron Zwarich
9f65d0251a chore: remove comments about missing functionality now implemented elsewhere (#8732) 2025-06-12 00:38:42 +00:00
Cameron Zwarich
a7af9f7d5f chore: fix a typo in a doc comment (#8731) 2025-06-11 20:41:32 +00:00
Cameron Zwarich
39cbe04946 fix: use Arg in LCNF FVarSubst rather than Expr (#8729)
This PR changes LCNF's `FVarSubst` to use `Arg` rather than `Expr`. This
enforces the requirements on substitutions, which match the requirements
on `Arg`.
2025-06-11 18:08:30 +00:00
Lean stage0 autoupdater
77fd1ba6b9 chore: update stage0 2025-06-11 16:51:07 +00:00
jrr6
0002ea8a37 feat: pre-stage0 groundwork for named error messages (#8649)
This PR adds the pre-stage0-update infrastructure for named error
messages. It adds macro syntax for registering and throwing named errors
(without elaborators), mechanisms for displaying error names in the
Infoview and at the command line, and the ability to link to error
explanations in the manual (once they are added).
2025-06-11 14:52:08 +00:00
jrr6
7bd82b103a feat: pre-stage0 groundwork for error explanations (#8651)
This PR adds the pre-stage0-update infrastructure for error
explanations. It adds the environment-extension machinery for
registering and accessing explanations, and it provides a cursory parser
that validates that the high-level structure of error explanations
matches the prescribed format.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-11 14:51:44 +00:00
Sebastian Ullrich
2c9c58b1f7 fix: allow mixing modules and non-modules when root is not a module (#8724) 2025-06-11 14:39:49 +00:00
Sebastian Ullrich
54c12df950 refactor: environment extension state splitting (#8653)
Replaces the previous `export/saveEntriesFn` split with a stricly more
general function such that `exportEntriesFn` could be deprecated at a
later point. Also gives the new function access to the `Environment`
while we're at it. Also gives `getModuleEntries` access to more olean
levels in preparation for `meta import`.
2025-06-11 12:52:04 +00:00
Sebastian Ullrich
01a0524749 chore: move benchmarking script to this repo (#8718)
Corresponding to
d3f39f8343
2025-06-11 12:27:06 +00:00
Lean stage0 autoupdater
551e755d23 chore: update stage0 2025-06-11 11:06:17 +00:00
Kim Morrison
082ca94d3b feat: add grind annotations for List/Array/Vector.eraseP/erase/eraseIdx (#8719)
This PR adds grind annotations for
List/Array/Vector.eraseP/erase/eraseIdx. It also adds some missing
lemmas.
2025-06-11 09:44:47 +00:00
Rob23oba
ee5b652136 doc: add documentation for builtin attributes (#8173)
This PR adds documentation to builtin attributes like `@[refl]` or
`@[implemented_by]`.

Closes #8432

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
Co-authored-by: David Thrane Christiansen <david@lean-fro.org>
2025-06-11 09:04:37 +00:00
Marc Huisinga
91b5e19833 feat: server-side for module hierarchy (#8654)
This PR adds server-side support for a new module hierarchy component in
VS Code that can be used to navigate both the import tree of a module
and the imported-by tree of a module. Specifically, it implements new
requests `$/lean/prepareModuleHierarchy`,
`$/lean/moduleHierarchy/imports` and
`$/lean/moduleHierarchy/importedBy`. These requests are not supported by
standard LSP. Companion PR at
[leanprover/vscode-lean4#620](https://github.com/leanprover/vscode-lean4/pull/620).


![Imports](https://github.com/user-attachments/assets/5ef650e7-3b0e-4a33-9ecb-f442bff88006)
![Imported
by](https://github.com/user-attachments/assets/d98e7a2c-3c4f-4509-afdf-08134a97aa78)

### Breaking changes
This PR augments the .ilean format with the direct imports of a file in
order to implement the `$/lean/moduleHierarchy/importedBy` request and
bumps the .ilean format version.
2025-06-11 08:02:18 +00:00
Paul Reichert
cf8315ed96 fix: restrict the IteratorLoop instance on DropWhile, which was accidentally more general (#8703)
This PR corrects the `IteratorLoop` instance in `DropWhile`, which
previously triggered for arbitrary iterator types.
2025-06-11 07:35:46 +00:00
Eric Wieser
44e36dec6f feat: strengthen finIdxOf? lemmas (#8678)
This PR makes the LHS of `isSome_finIdxOf?` and `isNone_finIdxOf?` more
general.
2025-06-11 07:32:01 +00:00
Cameron Zwarich
a92890ec84 fix: use the fvar subst for erased code in LCNF simp (#8717)
This PR uses the fvar substitution mechanism to replace erased code.
This isn't entirely satisfactory, since LCNF's `.return` doesn't support
a general `Arg` (which has a `.erased` constructor), it only supports an
`FVarId`. This is in contrast to the IR `.ret`, which does support a
general `Arg`.
2025-06-11 05:46:39 +00:00
Kim Morrison
eccc472e8d chore: remove set_option grind.warning false (#8714)
This PR removes the now unnecessary `set_option grind.warning false`
statements, now that the warning is disabled by default.
2025-06-11 05:09:19 +00:00
Cameron Zwarich
d8c54fb93d fix: consider any type application of an erased term to be erased (#8716)
This PR makes any type application of an erased term to be erased. This
comes up a bit more than one would expect in the implementation of Lean
itself.
2025-06-11 04:58:21 +00:00
Leonardo de Moura
aab65f595d feat: infrastructure for disequality constraints in grind linarith (#8715)
This PR implements the basic infrastructure for processing disequalities
in the `grind linarith` module. We still have to implement backtracking.
2025-06-11 04:04:41 +00:00
Lean stage0 autoupdater
0a9c246497 chore: update stage0 2025-06-11 02:42:58 +00:00
Leonardo de Moura
2a63b392dd fix: ring module in grind (#8713)
This PR fixes a bug in the commutative ring module used in `grind`. It
was missing simplification opportunities.
2025-06-11 01:20:50 +00:00
Cameron Zwarich
0b2884bfa3 fix: erase code of an erased type in LCNF simp (#8712)
This PR optimizes let decls of an erased type to an erased value.
Specialization can create local functions that produce a Prop, and
there's no point in keeping them around.
2025-06-11 00:58:55 +00:00
Johan Commelin
c53ab2835c fix: pin version of softprops/action-gh-release (#8710)
This PR pins the precise hash of softprops/action-gh-release to

    softprops/action-gh-release@da05d55257

because the latest version is broken.
See https://github.com/softprops/action-gh-release/issues/628 for more
details.
2025-06-11 00:08:18 +00:00
Anne Baanen
54dd7aae8c chore: improvements to release checklist and scripts (#8586)
This PR improves the release checklist and scripts:

* Check that the release's commit hash is not all-numeric starting with
0 (this can break SemVer, which [required us to release
v4.21.0-rc2](https://github.com/leanprover/lean4/releases/tag/v4.21.0-rc2)).
* Check that projects being bumped to a release tag do not reference
`nightly-testing` anymore.
* Clarify how to create subsequent release candidates if an `-rc1`
already exists.
* Fix typos in the release checklist documentation.
2025-06-10 22:56:06 +00:00
euprunin
52e0742108 chore: fix spelling mistakes (#8711)
Co-authored-by: euprunin <euprunin@users.noreply.github.com>
2025-06-10 20:24:28 +00:00
Sebastian Ullrich
614e6122f7 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8705)
It would accidentally fall back to stage 1 otherwise
2025-06-10 17:11:23 +00:00
Cameron Zwarich
1a9de502f2 fix: handle constants with erased types in toMonoType (#8709)
This PR handles constants with erased types in `toMonoType`. It is much
harder to write a test case for this than you would think, because most
references to such types get replaced with `lcErased` earlier.
2025-06-10 16:27:33 +00:00
Leonardo de Moura
085c4ed3f9 fix: internalization issue in the interface between linarith and ring (#8708)
This PR fixes an internalization bug in the interface between linarith
and ring modules in `grind`. The `CommRing` module may create new terms
during normalization.
2025-06-10 16:06:47 +00:00
Rob23oba
be4ebb8ac3 feat: equivalence of tree maps (#8210)
This PR adds an equivalence relation to tree maps akin to the existing
one for hash maps. In order to get many congruence lemmas to eventually
use for defining functions on extensional tree maps, almost all of the
remaining tree map functions have also been given lemmas to relate them
to list functions, although these aren't currently used to prove lemmas
other than congruence lemmas.
2025-06-10 14:49:52 +00:00
Kim Morrison
2344e3f254 chore: minor fixes to grind_indexmap test case (#8706) 2025-06-10 11:35:48 +00:00
Anne Baanen
48f394b1d4 chore: begin development cycle for v4.22.0 (#8642)
This PR bumps the version number of the Lean project to 4.22.0, since
v4.21.0 is now in the release candidate stage.
2025-06-10 11:29:41 +00:00
Sebastian Ullrich
2629921c01 fix: import completion after meta import (#8704)
The details of `identWithPartialTrailingDot` prevent a robust approach
using quotations.
2025-06-10 09:06:58 +00:00
Marc Huisinga
e123b327a5 feat: enable auto-implicits in lake math template (#8656)
This PR enables auto-implicits in the Lake math template. This resolves
an issue where new users sometimes set up a new project for math
formalization and then quickly realize that none of the code samples in
our official books and docs that use auto-implicits work in their
projects. With the introduction of [inlay hints for
auto-implicits](https://github.com/leanprover/lean4/pull/6768), we
consider the auto-implicit UX to be sufficiently usable that they can be
enabled by default in the math template.
Notably, this change does not affect Mathlib itself, which will proceed
to disable auto-implicits.

This change was previously discussed with and agreed to by the Mathlib
maintainer team.
2025-06-10 08:08:21 +00:00
Kim Morrison
e904314742 feat: add SHA-suffixed PR release tags (#8702)
This PR enhances the PR release workflow to create both short format and
SHA-suffixed release tags. Creates both pr-release-{PR_NUMBER} and
pr-release-{PR_NUMBER}-{SHORT_SHA} tags, generates separate releases for
both formats, adds separate GitHub status checks, and updates
Batteries/Mathlib testing branches to use SHA-suffixed tags for exact
commit traceability.

This removes the need for downstream repositories to deal with the
toolchain changing without the toolchain name changing.
2025-06-10 07:09:08 +00:00
Mac Malone
0ebd320940 fix: lake: export LeanOption in Lean from Lake (#8701)
This PR exports `LeanOption` in the `Lean` namespace from the `Lake`
namespace. `LeanOption` was moved from `Lean` to `Lake` in #8447, which
can cause unnecessary breakage without this.
2025-06-10 04:09:40 +00:00
Kim Morrison
b1980ef871 chore: cleanup notes about grind in LRAT (#8623)
This PR cleans up some notes about `grind` failures in the LRAT checker,
now that some `grind` bugs have been fixed.
2025-06-10 03:47:28 +00:00
Kim Morrison
8fce30e7cb chore: change grind.warning default to false (#8698)
This PR turns off the default warning when using `grind`, in preparation
for v4.22. I'll removing all the `set_option grind.warning false` in our
codebase in a second PR, after an update-stage0.
2025-06-10 03:40:45 +00:00
Kim Morrison
308a383079 chore: fix grind annotation on DHashMap.contains_iff_mem (#8700)
The original annotations produced patterns that matched too often.
2025-06-10 03:26:54 +00:00
Leonardo de Moura
2d67524e42 feat: equality in grind linarith (#8697)
This PR implements support for inequalities in the `grind` linear
arithmetic procedure and simplifies its design. Some examples that can
already be solved:
```lean
open Lean.Grind
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c d : α)
    : a + d < c → b = a + (2:Int)*d → b - d > c → False := by
  grind

example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b : α)
    : a = 0 → b = 1 → a + b ≤ 2 := by
  grind

example [CommRing α] [Preorder α] [Ring.IsOrdered α] (a b c d e : α) :
    2*a + b ≥ 1 → b ≥ 0 → c ≥ 0 → d ≥ 0 → e ≥ 0
    → a ≥ 3*c → c ≥ 6*e → d - e*5 ≥ 0
    → a + b + 3*c + d + 2*e < 0 → False := by
  grind
```
2025-06-09 23:39:24 +00:00
Leonardo de Moura
41c41e455a feat: One.one support in linarith (#8694)
This PR implements special support for `One.one` in linarith when the
structure is a ordered ring. It also fixes bugs during initialization.
2025-06-09 20:17:48 +00:00
Cameron Zwarich
f61a412801 fix: make unsafeBaseIO noinline (#8669)
This PR makes `unsafeBaseIO` `noinline`. The new compiler is better at
optimizing `Result`-like types, which can cause the final operation in
an `unsafeBaseIO` block to be dropped, since `unsafeBaseIO` is
discarding the state.
2025-06-09 14:48:37 +00:00
Leonardo de Moura
00f6b1e70a fix: denotation functions for interfacing CommRing and linarith (#8693)
This PR fixes the denotation functions used to interface the ring and
linarith modules in grind.
2025-06-09 14:43:13 +00:00
Sebastian Ullrich
8422d936cf chore: revert "fix LEAN_PATH for building stage2+ Leanc.lean" (#8692)
Reverts leanprover/lean4#8685 pending Windows fix
2025-06-09 08:50:34 +00:00
Leonardo de Moura
dd1d3e6a3a feat: model search procedure for grind linarith (#8690)
This PR implements the main framework of the model search procedure for
the linarith component in grind. It currently handles only inequalities.
It can already solve simple goals such as
```lean
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c → c < a → False := by
  grind

example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c + d → a - d < c := by
  grind
```
2025-06-09 04:31:28 +00:00
Leonardo de Moura
e38b8a0a7a feat: proof terms generation for CommRing and linarith interface (#8689)
This PR implements proof term generation for the `CommRing` and
`linarith` interface. It also fixes the `CommRing` helper theorems.
2025-06-08 23:38:03 +00:00
Leonardo de Moura
3e0168df58 feat: proof term construction infrastructure for linarith in grind (#8687)
This PR implements the infrastructure for constructing proof terms in
the linarith procedure in `grind`. It also adds the `ToExpr` instances
for the reified objects.
2025-06-08 19:58:48 +00:00
Mac Malone
fcaae1dc58 feat: lake: use lean --setup (#8447)
This PR makes use of `lean --setup` in Lake builds of Lean modules and
adds Lake support for the new `.olean` artifacts produced by the module
system.

Lake now computes the entire transitive import graph of a module for
Lean, allowing it eagerly provide the artifacts managed by Lake to Lean
via the `modules` field of `lean --setup`.

`lake setup-file` no longer respects the imports passed to it and
instead just parses the file's header for imports. This is necessary
because import statements are now more complex than a simple module
name.
2025-06-08 17:42:45 +00:00
Sebastian Ullrich
8cc6a4a028 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8685)
It would accidentally fall back to stage 1 otherwise
2025-06-08 16:17:05 +00:00
Cameron Zwarich
4ec5dad05f fix: only mark single-alt cases discriminant as used if any param is used (#8683)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a single-alt cases is only marked as used if any param
is used.
2025-06-08 06:20:38 +00:00
Leonardo de Moura
7e1d0cc125 feat: use CommRing to normalize linarith expressions (#8682)
This PR uses the `CommRing` module to normalize linarith inequalities.
2025-06-08 05:41:00 +00:00
Cameron Zwarich
2ae066fdc0 fix: only mark a cases discriminant used if it has non-default alt (#8681)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a `cases` construct will only be mark used if it has a
non-default alternative.
2025-06-08 05:07:02 +00:00
Leonardo de Moura
c9c794ee8a feat: reification and denotation for linarith module in grind (#8680)
This PR adds the `reify?` and `denoteExpr` for the new linarith module
in `grind`.
2025-06-08 02:53:28 +00:00
Leonardo de Moura
106708ee78 feat: grind linarith module infrastructure (#8677)
This PR adds the basic infrastructure for the linarith module in
`grind`.
2025-06-08 00:19:52 +00:00
Cameron Zwarich
666fb5c571 fix: update maxHeartbeats in tests/lean/run/match_expr_perf.lean (#8676)
This PR updates `maxHeartbeats` in the match_expr_perf.lean test, since
with the new compiler this also includes the allocations made by the
compiler.
2025-06-07 23:27:16 +00:00
Cameron Zwarich
8d8fd0715f fix: increase precision of new compiler's noncomputable check (#8675)
This PR increases the precision of the new compiler's non computable
check, particularly around irrelevant uses of `noncomputable` defs in
applications.

There are no tests included because they don't pass with the old
compiler. They are on the new compiler's branch and they will be merged
when it is enabled.
2025-06-07 22:20:55 +00:00
Leonardo de Moura
4abc4430dc refactor: ENodeKey => ExprPtr (#8674) 2025-06-07 19:30:02 +00:00
Lean stage0 autoupdater
d46188de54 chore: update stage0 2025-06-07 14:27:00 +00:00
Sebastian Ullrich
de57b77feb chore: support meta in ParseImportsFast (#8672) 2025-06-07 13:08:20 +00:00
Lean stage0 autoupdater
f0eae3b879 chore: update stage0 2025-06-07 11:04:28 +00:00
Sebastian Ullrich
1abf6fe1f5 chore: do not interpret meta as noncomputable (#8668)
To be replaced by actual handling of `meta`
2025-06-07 09:45:04 +00:00
Mac Malone
f917951745 fix: lake: ensure Lake versions are SemVer compatible (#8613)
This PR changes the Lake version syntax (to `5.0.0-src+<commit>`) to
ensure it is a well-formed SemVer,
2025-06-07 07:17:06 +00:00
Mac Malone
8904e5c070 feat: lake: builtin facet memoize toggle (#7738)
This PR makes memoization of built-in facets toggleable through a
`memoize` option on the facet configuration. Built-in facets which are
essentially aliases (e.g., `default`, `o`) have had memoization
disabled.
2025-06-07 06:00:05 +00:00
Leonardo de Moura
ef9094d7f8 feat: CommRing interface for grind linarith (#8670)
This PR adds helper theorems that will be used to interface the
`CommRing` module with the linarith procedure in `grind`.
2025-06-07 00:35:14 +00:00
Lean stage0 autoupdater
d50292d31b chore: update stage0 2025-06-06 20:02:08 +00:00
Joachim Breitner
24cb133eb2 feat: explicit defeq attribute (#8419)
This PR introduces an explicit `defeq` attribute to mark theorems that
can be used by `dsimp`. The benefit of an explicit attribute over the
prior logic of looking at the proof body is that we can reliably omit
theorem bodies across module boundaries. It also helps with intra-file
parallelism.

If a theorem is syntactically defined by `:= rfl`, then the attribute is
assumed and need not given explicitly. This is a purely syntactic check
and can be fooled, e.g. if in the current namespace, `rfl` is not
actually “the” `rfl` of `Eq`. In that case, some other syntax has be
used, such as `:= (rfl)`. This is also the way to go if a theorem can be
proved by `defeq`, but one does not actually want `dsimp` to use this
fact.

The `defeq` attribute will look at the *type* of the declaration, not
the body, to check if it really holds definitionally. Because of
different reduction settings, this can sometimes go wrong. Then one
should also write `:= (rfl)`, if one does not want this to be a defeq
theorem. (If one does then this is currently not possible, but it’s
probably a bad idea anyways).

The `set_option debug.tactic.simp.checkDefEqAttr true`, `dsimp` will
warn if could not apply a lemma due to a missing `defeq` attribute.

With `set_option backward.dsimp.useDefEqAttr.get false` one can revert
to the old behavior of inferring rfl-ness based on the theorem body.

Both options will go away eventually (too bad we can’t mark them as
deprecated right away, see #7969)

Meta programs that generate theorems (e.g. equational theorems) can use
`inferDefEqAttr` to set the attribute based on the theorem body of the
just created declaration.

This builds on #8501 to update Init to `@[expose]` a fair amount of
definitions that, if not exposed, would prevent some existing `:= rfl`
theorems from being `defeq` theorems. In the interest of starting
backwards compatible, I exposed these function. Hopefully many can be
un-exposed later again.

A mathlib adaption branch exists that includes both the meta programming
fixes and changes to the theorems (e.g. changing `:= by rfl` to `:=
rfl`).

With the module system there is now no special handling for `defeq`
theorem bodies, because we don’t look at the body anymore. The previous
hack is removed. The `defeq`-ness of the theorem needs to be checked in
the context of the theorem’s *type*; the error message contains a hint
if the defeq check fails because of the exported context.
2025-06-06 18:40:06 +00:00
Henrik Böving
eddbe08118 refactor: AIG doesn't need to be modified for constants (#8663) 2025-06-06 15:32:38 +00:00
Paul Reichert
d16c4052c2 feat: introduce empty iterator (#8615)
This PR provides a special empty iterator type. Although its behavior
can be emulated with a list iterator (for example), having a special
type has the advantage of being easier to optimize for the compiler.
2025-06-06 14:26:52 +00:00
tonneaus
febad6a380 doc: typo in IO.lean (#8657) 2025-06-06 13:12:12 +00:00
Marc Huisinga
257cd15a00 fix: wrong signature help after map/filter/etc (#8655)
This PR fixes a bug in the signature help where it would be displayed
for higher-order-functions that are the last argument of another
function.
2025-06-06 13:07:01 +00:00
Paul Reichert
5963bc8b8a fix: remove IteratorLoop instances without associated LawfulIteratorLoop instances (#8629)
This PR replaces special, more optimized `IteratorLoop` instances, for
which no lawfulness proof has been made, with the verified default
implementation. The specialization of the loop/collect implementations
is low priority, but having lawfulness instances for all iterators is
important for verification.
2025-06-06 08:06:59 +00:00
Paul Reichert
ec9b00996f feat: equivalence of iterators (#8545)
This PR provides the means to reason about "equivalent" iterators.
Simply speaking, two iterators are equivalent if they behave the same as
long as consumers do not introspect their states.
2025-06-06 08:06:39 +00:00
Kim Morrison
50474fef78 chore: cleanup after renaming get_elem_tactic_trivial 2025-06-06 13:10:18 +10:00
Kim Morrison
a5567618ac chore: update stage0 2025-06-06 13:10:18 +10:00
Kim Morrison
a3caf60f6a feat: rename get_elem_tactic_trivial to get_elem_tactic_extensible 2025-06-06 13:10:17 +10:00
Leonardo de Moura
c3d31cf24b feat: helper theorems for equality detection and coefficent normalization (#8650)
This PR adds helper theorems for coefficient normalization and equality
detection. This theorems are for the linear arithmetic procedure in
`grind`.
2025-06-06 02:42:57 +00:00
Leonardo de Moura
f7ecf06234 feat: normalization and ordered IntModule helper theorems (#8645)
This PR adds many helper theorems for the future `IntModule` linear
arithmetic procedure in `grind`.
It also adds helper theorems for normalizing input atoms and support for
disequality in the new linear arithmetic procedure in `grind`.
2025-06-05 23:39:10 +00:00
Cameron Zwarich
b97d35d879 fix: improve precision of the new compiler's noncomputable check for proj (#8647)
This PR improves the precision of the new compiler's `noncomputable`
check for projections. There is no test included because while this was
reduced from Mathlib, the old compiler does not correctly handle the
reduced test case. It's not entirely clear to me if the check is passing
with the old compiler for correct reasons. A test will be added to the
new compiler's branch.
2025-06-05 22:44:02 +00:00
Kim Morrison
ebf5fbd294 feat: complete grind's ToInt framework (#8639)
This PR completes the `ToInt` family of typeclasses which `grind` will
use to embed types into the integers for `cutsat`. It contains instances
for the usual concrete data types (`Fin`, `UIntX`, `IntX`, `BitVec`),
and is extensible (e.g. for Mathlib's `PNat`).
2025-06-05 11:25:04 +00:00
Luisa Cicolini
74d8746356 feat: add BitVec.setWidth'_eq to bv_normalize (#8640)
This PR adds `BitVec.setWidth'_eq` to `bv_normalize` such that
`bv_decide` can reduce it and solve lemmas involving `setWidth'_eq`
2025-06-05 09:42:47 +00:00
Joachim Breitner
1d9dd33bec feat: #print sig (#8641)
This PR adds the `#print sig $ident` variant of the `#print` command,
which omits the body. This is useful for testing meta-code, in the
```
#guard_msgs (drop trace, all) in #print sig foo
```
idiom. The benefit over `#check` is that it shows the declaration kind,
reducibility attributes (and in the future more built-in attributes,
like `@[defeq]` in #8419). (One downside is that `#check` shows unused
function parameter names, e.g. in induction principles; this could
probably be refined.)
2025-06-05 09:02:19 +00:00
Siddharth
9b9dd8546a feat: simplify T-division into E-division when numerator is positive (#8205)
This PR adds a simp lemma that simplifies T-division where the numerator
is a `Nat` into an E-division:


```lean
@[simp] theorem ofNat_tdiv_eq_ediv {a : Nat} {b : Int} : (a : Int).tdiv b = a / b :=
   tdiv_eq_ediv_of_nonneg (by simp)
```

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-06-05 06:20:49 +00:00
Siddharth
de7d43865e feat: bitvector trichotomy lemmas (#8203)
This PR adds trichotomy lemmas for unsigned and signed comparisons,
stating that only one of three cases may happen: either `x < y`, `x =
y`, or `x > y` (for both signed and unsigned comparsions). We use
explicit arguments so that users can write `rcases slt_trichotomy x y
with hlt | heq | hgt`.
2025-06-05 05:28:44 +00:00
Leonardo de Moura
3ce7dd318d feat: sort equivalence classes in grind diagnostics (#8638)
This PR improves the diagnostic information produced by `grind`. It now
sorts the equivalence classes by generation and then `Expr. lt`.
2025-06-05 04:35:59 +00:00
Leonardo de Moura
b1709d1fc1 feat: background theorems for IntModule (#8637)
This PR adds background theorems for normalizing `IntModule` expressions
using reflection.
2025-06-05 02:32:53 +00:00
Cameron Zwarich
6ebf39d0fc chore: fix formatting (#8635) 2025-06-04 22:43:45 +00:00
Cameron Zwarich
a6e2df6250 fix: don't treat types with erased constructor types as having trivial structure (#8634)
This PR makes `hasTrivialStructure?` return false for types whose
constructors have types that are erased, e.g. if they construct a
`Prop`.
2025-06-04 22:33:44 +00:00
Leonardo de Moura
e08b2a1f62 feat: track case-split source in grind (#8633)
This PR implements case-split tracking in `grind`. The information is
displayed when `grind` fails or diagnostic information is requested.
Examples:

- Failure

![image](https://github.com/user-attachments/assets/b10516c3-d205-4e08-80a4-daca195c1d8a)

- Success with `set_option diagnostics true`

![image](https://github.com/user-attachments/assets/15ee31e0-27d8-473f-a469-12b424ce6d24)
2025-06-04 16:59:36 +00:00
Sebastian Ullrich
2f4e56b5d2 chore: fixes after rebootstrap 2025-06-04 18:26:05 +02:00
Sebastian Ullrich
a487bb8d63 chore: update stage0 2025-06-04 18:26:05 +02:00
Sebastian Ullrich
8457342d33 feat: meta syntax 2025-06-04 18:26:05 +02:00
Siddharth
596e65d7df feat: AIG.relabel(Nat)_unsat_iff for AIGs with empty variable types (#8631)
This PR generalizes `Std.Sat.AIG. relabel(Nat)_unsat_iff` to allow the
AIG type to be empty. We generalize the proof, by showing that in the
case when `α` is empty, the environment doesn't matter, since all
environments `α → Bool` are isomorphic.

This showed up when reusing the AIG primitives for building a
k-induction based model checker to prove arbitrary width bitvector
identities.
2025-06-04 15:10:48 +00:00
Kim Morrison
7c76dbf6be feat: typeclasses for grind to extensibly embed types into Int (#8543)
This PR adds typeclasses for `grind` to embed types into `Int`, for
cutsat. This allows, for example, treating `Fin n`, or Mathlib's `ℕ+` in
a uniform and extensible way.

There is a primary typeclass that carries the `toInt` function, and a
description of the interval the type embeds in. There are then
individual typeclasses describing how arithmetic/order operations
interact with the embedding.
2025-06-04 13:04:19 +00:00
Lean stage0 autoupdater
6b102c91e3 chore: update stage0 2025-06-04 13:21:17 +00:00
Joachim Breitner
b9243e19be feat: make equational theorems of non-exposed defs private (#8519)
This PR makes the equational theorems of non-exposed defs private. If
the author of a module chose not to expose the body of their function,
then they likely don't want that implementation to leak through
equational theorems. Helps with #8419.

There is some amount of incidential complexity due to how `private`
works in lean, by mangling the name: lots of code paths that need now do
the right thing™ about private and non-private names, including the
whole reserved name machinery.

So this includes a number of refactorings:

* The logic for calculating an equational theorem name (or similar) is
now done by a single function, `mkEqLikeNameFor`, rather than all over
the place.

* Since the name of the equational theorem now depends on the current
context (in particular whether it’s a proper module, or a non-module
file), the forward map from declaration to equational theorem doesn’t
quite work anymore. This map is deleted; the list of equational theorems
are now always found by looking for declaration of the expected names
(`alreadyGenerated). If users define such theorems themselves (and make
it past the “do not allow reserved names to be declared”) they get to
keep both pieces.

* Because this map was deleted, mathlib’s `eqns` command can no longer
easily warn if equational lemmas have already been generated too early
(adaption branch exists). But in general I think lean could provide a
more principled way of supporting custom unfold lemmas, and ideally the
whole equational theorem machinery is just using that.

* The ReservedNamePredicate is used by `resolveExact`, so we need to
make sure that it returns the right name, including privateness. It is
not ok to just reserve both the private and non-private name but then
later in the ReservedNameAction produce just one of the two.
 
* We create `foo.def_eq` eagerly for well-founded recursion. This is
needed because we need feed in the proof of the rewriting done by
`wf_preprocess`. But if `foo.def_eq` is private in a module, then a
non-module importing it will still expect a non-private `foo.def_eq` to
exist. To patch that, we install a `copyPrivateUnfoldTheorem :
GetUnfoldEqnFn` that declares a theorem aliasing the private one. Seems
to work.
2025-06-04 11:52:08 +00:00
Kim Morrison
d6478e15c7 chore: remove slow and unnecessary @[grind] annotations (#8630) 2025-06-04 10:57:25 +00:00
Leonardo de Moura
1629440cb8 feat: improve grind diagnostics for successful case (#8625)
This PR improves the diagnostic information produced by `grind` when it
succeeds. We now include the list of case-splits performed, and the
number of application per function symbol. Example:


![image](https://github.com/user-attachments/assets/109f3f80-85a1-4368-8958-fdf56707ea7d)
2025-06-04 09:34:48 +00:00
Kim Morrison
4500a7f02b fix: remove global NatCast (Fin n) instance (#8620)
This PR removes the `NatCast (Fin n)` global instance (both the direct
instance, and the indirect one via `Lean.Grind.Semiring`), as that
instance causes causes `x < n` (for `x : Fin k`, `n : Nat`) to be
elaborated as `x < ↑n` rather than `↑x < n`, which is undesirable. Note
however that in Mathlib this happens anyway!
2025-06-04 06:58:39 +00:00
Leonardo de Moura
c12159b519 refactor: move read-only data to Grind.Context (#8624) 2025-06-04 02:50:43 +00:00
Kim Morrison
1260059a59 feat: add grind use case example IndexMap (#8622)
This PR adds a test case / use case example for `grind`, setting up the
very basics of `IndexMap`, modelled on Rust's
[`indexmap`](https://docs.rs/indexmap/latest/indexmap/). It is not
intended as a complete implementation: just enough to exercise `grind`.

(Thanks to @arthurpaulino for suggesting this as a test case.)
2025-06-04 01:33:56 +00:00
Leonardo de Moura
8165ecc1db fix: bug in the equality resolution procedure in grind (#8621)
This PR fixes a bug in the equality-resolution procedure used by
`grind`.
The procedure now performs a topological sort so that every simplified
theorem declaration is emitted **before** any place where it is
referenced.
Previously, applying equality resolution to
```lean
h : ∀ x, p x a → ∀ y, p y b → x ≠ y
```
in the example
```lean
example
  (p : Nat → Nat → Prop)
  (a b c : Nat)
  (h  : ∀ x, p x a → ∀ y, p y b → x ≠ y)
  (h₁ : p c a)
  (h₂ : p c b) :
  False := by
  grind
```
caused `grind` to produce the incorrect term
```lean
p ?y a → ∀ y, p y b → False
```
The patch eliminates this error, and the following correct simplified
theorem is generated
```lean
∀ y, p y a → p y b → False
```
2025-06-04 00:34:47 +00:00
Leonardo de Moura
344b52f999 fix: term internalization issue in grind (#8619)
This PR fixes an internalization (aka preprocessing) issue in `grind`
when applying injectivity theorems.
2025-06-04 00:13:51 +00:00
Kyle Miller
5e952598dc fix: let private names be unresolved in the pretty printer, fix shadowing bug when pp.universes is true (#8617)
This PR fixes (1) an issue where private names are not unresolved when
they are pretty printed, (2) an issue where in `pp.universes` mode names
were allowed to shadow local names, (3) an issue where in `match`
patterns constants shadowing locals wouldn't use `_root_`, and (4) an
issue where tactics might have an incorrect "try this" when
`pp.fullNames` is set. Adds more delaboration tests for name
unresolution.

It also cleans up the `delabConst` delaborator so that it uses
`unresolveNameGlobalAvoidingLocals`, rather than doing any local context
analysis itself. The `inPattern` logic has been removed; it was a
heuristic added back in #575, but it now leads to incorrect results (and
in `match` patterns, local names shadow constants in name resolution).
2025-06-03 23:37:35 +00:00
Cameron Zwarich
b9aefb4a50 feat: LCNF constant folding for Nat.nextPowerOfTwo (#8618)
This PR implements LCNF constant folding for `Nat.nextPowerOfTwo`.
2025-06-03 21:13:58 +00:00
Cameron Zwarich
9afe5ccae3 feat: LCNF constant folding for Nat.pow (#8616)
This PR adds constant folding for `Nat.pow` to the new compiler,
following the same limits as the old compiler.
2025-06-03 19:10:38 +00:00
Marc Huisinga
cb0284f98e feat: signature help (#8511)
This PR implements signature help support. When typing a function
application, editors with support for signature help will now display a
popup that designates the current (remaining) function type. This
removes the need to remember the function signature while typing the
function application, or having to constantly cycle between hovering
over the function identifier and typing the application. In VS Code, the
signature help can be triggered manually using `Ctrl+Shift+Space`.


![Demo](https://github.com/user-attachments/assets/d1f6ed79-bb16-4593-8d28-68b1cce5d5dc)

### Other changes

- In order to support signature help for the partial syntax `f a <|` or
`f a $`, these notations now elaborate as `f a`, not `f a .missing`.
- The logic in `delabConstWithSignature` that delaborates parameters is
factored out into a function `delabForallParamsWithSignature` so that it
can be used for arbitrary `forall`s, not just constants.
- The `InfoTree` formatter is adjusted to produce output where it is
easier to identify the kind of `Info` in the `InfoTree`.
- A bug in `InfoTree.smallestInfo?` is fixed so that it doesn't panic
anymore when its predicate `p` does not ensure that both `pos?` and
`tailPos?` of the `Info` are present.
2025-06-03 17:26:33 +00:00
Cameron Zwarich
35e83066e6 feat: implement LCNF constant folding for toNat (#8614)
This PR implements constant folding for `toNat` in the new compiler,
which improves parity with the old compiler.
2025-06-03 17:12:15 +00:00
Sebastian Ullrich
ba847d41f1 chore: revise environment constant addition details (#8610)
* Move constant registration with elab env from `Lean.addDecl` to
`Lean.Environment.addDeclCore` for compatibility
* Make module system behavior independent of `Elab.async` value
2025-06-03 15:16:45 +00:00
Cameron Zwarich
f5e72d0962 feat: make guard_msgs.diff=true the default (#8596)
This PR makes `guard_msgs.diff=true` the default. The main usage of
`#guard_msgs` is for writing tests, and this makes staring at altered
test outputs considerably less tiring.
2025-06-03 15:13:15 +00:00
Sebastian Ullrich
536c87d73c chore: make test more robust 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
c95e058e3c chore: fix tests after rebootstrap 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
4746e38414 chore: update stage0 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
f718f26200 feat: create private aux decls in private contexts 2025-06-03 15:53:05 +02:00
Marc Huisinga
184dbae130 feat: reusable rpc refs (#8105)
This PR adds support for server-sided `RpcRef` reuse and fixes a bug
where trace nodes in the InfoView would close while the file was still
being processed.

The core of the trace node issue is that the server always serves new
RPC references in every single response to the client, which means that
the client is forced to reset its UI state.

In a previous attempt at fixing this (#8056), the server would memorize
the RPC-encoded JSON of interactive diagnostics (which includes RPC
references) and serve it for as long as it could reuse the snapshot
containing the diagnostics, so that RPC references are reused. The
problem with this was that the client then had multiple finalizers
registered for the same RPC reference (one for every reused RPC
reference that was served), and once the first reference was
garbage-collected, all other reused references would point into the
void.

This PR takes a different approach to resolve the issue: The meaning of
`$/lean/rpc/release` is relaxed from "Free the object pointed to by this
RPC reference" to "Decrement the RPC reference count of the object
pointed to by this RPC reference", and the server now maintains a
reference count to track how often a given `RpcRef` was served. Only
when every single served instance of the `RpcRef` has been released, the
object is freed. Additionally, the reuse mechanism is generalized from
being only supported for interactive diagnostics, to being supported for
any object using `WithRpcRef`. In order to make use of reusable RPC
references, downstream users still need to memorize the `WithRpcRef`
instances accordingly.

Closes #8053.

### Breaking changes

Since `WithRpcRef` is now capable of tracking its identity to decide
which `WithRpcRef` usage constitutes a reuse, the constructor of
`WithRpcRef` has been made `private` to discourage downstream users from
creating `WithRpcRef` instances with manually-set `id`s. Instead,
`WithRpcRef.mk` (which lives in `BaseIO`) is now the preferred way to
create `WithRpcRef` instances.
2025-06-03 12:35:12 +00:00
Kim Morrison
bc47aa180b feat: use grind to shorten some proofs in the LRAT checker (#8609)
This PR uses `grind` to shorten some proofs in the LRAT checker. The
intention is not particularly to improve the quality or maintainability
of these proofs (although hopefully this is a side effect), but just to
give `grind` a work out.

There are a number of remaining notes, either about places where `grind`
fails with an internal error (for which #8608 is hopefully
representative, and we can fix after that), or `omega` works but `grind`
doesn't (to be investigated later).

Only in some of the files have I thoroughly used grind. In many files
I've just replaced leaves or branches of proofs with `grind` where it
worked easily, without setting up the internal annotations in the LRAT
library required to optimize the use of `grind`. It's diminishing
returns to do this in a proof library that is not high priority, so I've
simply drawn a line.
2025-06-03 08:38:57 +00:00
Kim Morrison
f7b6e155d4 chore: add failing grind test (#8608) 2025-06-03 07:45:38 +00:00
Kim Morrison
f4e86e310c chore: add failing grind test (unknown metavariable) (#8607) 2025-06-03 07:00:56 +00:00
Kim Morrison
5f0bdfcada chore: initial @[grind] annotations for Array/Vector.range (#8606) 2025-06-03 06:44:01 +00:00
Kim Morrison
0f4459b42c chore: add @[grind] annotations to Fin.getElem_fin (#8605) 2025-06-03 06:37:35 +00:00
Paul Reichert
55b89aaf38 feat: introduce drop iterator combinator (#8420)
This PR provides the iterator combinator `drop` that transforms any
iterator into one that drops the first `n` elements.

Additionally, the PR removes the specialized `IteratorLoop` instance on
`Take`. It currently does not have a `LawfulIteratorLoop` instance,
which needs to exist for the loop consumer lemmas to work. Having the
specialized instance is low priority.
2025-06-03 06:37:09 +00:00
Kim Morrison
9fc8713946 chore: grind annotations for getElem?_pos and variants (#8590)
This PR adds `@[grind]` to `getElem?_pos` and variants.

I'd initially thought these would result in too much case splitting, but
it seems to be only minor, and in use cases the payoff is good.
2025-06-03 06:17:05 +00:00
Cameron Zwarich
106411420b fix: support compiler.extract_closed option in the new compiler (#8604)
This PR adds support for the `compiler.extract_closed` option to the new
compiler, since this is used by the definition of `unsafeBaseIO`. We'll
revisit this once we switch to the new compiler and rethink its
relationship with IO.
2025-06-03 05:58:32 +00:00
Kim Morrison
921be93535 chore: add @[grind] to List/Array/Vector.mem_map (#8603) 2025-06-03 05:07:11 +00:00
Cameron Zwarich
63d123f4be fix: support Eq.recOn in the new compiler (#8602)
This PR adds support to the new compiler for `Eq.recOn` (which is
supported by the old compiler but missing a test).
2025-06-03 04:45:20 +00:00
Kim Morrison
7adea80123 chore: missing [@grind] annotations for List/Array.modify` (#8601) 2025-06-03 04:13:01 +00:00
Kim Morrison
310a123901 chore: grind annotations for List/Array/Vector.any/all (#8600) 2025-06-03 03:52:54 +00:00
Kim Morrison
6c17ad8954 chore: add failing grind test (#8599)
`@[grind local]` currently doesn't work as expected on theorems in
namespaces.
2025-06-03 01:49:36 +00:00
Jakob von Raumer
3452a8a2e5 feat: improve BitVec.extractLsb' lemma on appended vectors (#8585)
This PR makes the lemma `BitVec.extractLsb'_append_eq_ite` more usable
by using the "simple case" more often, and uses this simplification to
make `BitVec.extractLsb'_append_eq_of_add_lt` stronger, renaming it to
`BitVec.extractLsb'_append_eq_of_add_le`.
2025-06-02 20:11:59 +00:00
Luisa Cicolini
fcc97fe49f feat: add toInt_smod and auxilliary theorems (#8253)
This PR adds `toInt_smod` and auxilliary lemmas necessary for its proof
(`msb_intMin_umod_neg_of_msb_true`,
`msb_neg_umod_neg_of_msb_true_of_msb_true`, `toInt_dvd_toInt_iff`,
`toInt_dvd_toInt_iff_of_msb_true_msb_false`,
`toInt_dvd_toInt_iff_of_msb_false_msb_true`,
`neg_toInt_neg_umod_eq_of_msb_true_msb_true`, `toNat_pos_of_ne_zero`,
`toInt_umod_neg_add`, `toInt_sub_neg_umod` and
`BitVec.[lt_of_msb_false_of_msb_true, msb_umod_of_msb_false_of_ne_zero`,
`neg_toInt_neg]`)

co-authored with @tobiasgrosser

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: kuhnsa <151550049+salinhkuhn@users.noreply.github.com>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-02 20:09:00 +00:00
Cameron Zwarich
af365238a1 fix: wrap the new compiler in withoutExporting (#8595)
This PR wraps the invocation of the new compiler in `withoutExporting`.
This is not necessary for the old compiler because it uses more direct
access to the kernel environment.
2025-06-02 16:57:10 +00:00
Cameron Zwarich
3ccc9ca7ac fix: remove incorrect strictOr/strictAnd optimizations (#8594)
This PR removes incorrect optimizations for strictOr/strictAnd from the
old compiler, along with deleting an incorrect test. In order to do
these optimizations correctly, nontermination analysis is required.
Arguably, the correct way to express these optimizations is by exposing
the implementation of strictOr/strictAnd to a nontermination-aware phase
of the compiler, and then having them follow from more general
transformations.
2025-06-02 16:14:56 +00:00
Cameron Zwarich
b73a67a635 chore: use HashMap in ToMonoM.State.noncomputableVars (#8592) 2025-06-02 15:08:51 +00:00
Kim Morrison
9a3228ef88 chore: adjustments to grind lemmas for List.Pairwise (#8588) 2025-06-02 13:19:21 +00:00
Kim Morrison
b0963938d4 chore: initial grind annotations for List.erase (#8589) 2025-06-02 12:56:09 +00:00
Kim Morrison
47b353f155 chore: adjust HashMap grind lemmas (#8587)
This PR adjusts the grind annotation on
`Std.HashMap.map_fst_toList_eq_keys` and variants, so `grind` can reason
bidirectionally between `m.keys` and `m.toList`.
2025-06-02 12:50:21 +00:00
Sebastian Ullrich
add3e1ae12 fix: IO.FS.removeDirAll should not follow symlinks (#8573)
This PR avoids the likely unexpected behavior of `removeDirAll` to
delete through symlinks and adds the new function
`IO.FS.symlinkMetadata`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-06-02 08:44:17 +00:00
Sebastian Ullrich
569e46033b feat: do not export private declarations (#8337)
This PR adjusts the experimental module system to not export any private
declarations from modules.

Fixes #5002
2025-06-02 08:01:08 +00:00
Sebastian Ullrich
5023b40576 chore: CI: fix cache (#8579)
* include .olean variants
* include SHA in key on push as well
2025-06-02 08:00:42 +00:00
Sebastien Gouezel
3516143aed doc: use notMem instead of not_mem in recommended_spelling (#8496)
This PR changes the recommended spelling from `not_mem` to `notMem`, to
reflect the decision that has been made in mathlib.

It does *not* change the name of any core lemma.

See Zulip discussion at [#mathlib4 > Naming: nmem vs not_mem @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Naming.3A.20nmem.20vs.20not_mem/near/520315224)
2025-06-02 06:46:36 +00:00
Cameron Zwarich
0339cd2836 fix: don't drop state during update in Param.toMono (#8582)
This PR fixes an accidental dropping of state in Param.toMono. When this
code was originally written, there was no other state besides
`typeParams`.
2025-06-02 05:28:27 +00:00
Cameron Zwarich
bae336da87 chore: make ToMonoM.State.typeParams an FVarIdHashSet rather than an FVarIdSet (#8581) 2025-06-02 05:07:57 +00:00
dependabot[bot]
e7b24479ed chore: CI: bump dawidd6/action-download-artifact from 9 to 10 (#8578)
Bumps
[dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
from 9 to 10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dawidd6/action-download-artifact/releases">dawidd6/action-download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v10</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix the download-commit test to actually look for a commit by <a
href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/330">dawidd6/action-download-artifact#330</a></li>
<li>Add the option &quot;ref&quot;, specifying either a commit or a
branch by <a
href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/329">dawidd6/action-download-artifact#329</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a>
made their first contribution in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/330">dawidd6/action-download-artifact#330</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dawidd6/action-download-artifact/compare/v9...v10">https://github.com/dawidd6/action-download-artifact/compare/v9...v10</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4c1e823582"><code>4c1e823</code></a>
Add the option &quot;ref&quot;, specifying either a commit or a branch
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/329">#329</a>)</li>
<li><a
href="a708c3c648"><code>a708c3c</code></a>
Fix the download-commit test to actually look for a commit (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/330">#330</a>)</li>
<li><a
href="19f6be5f04"><code>19f6be5</code></a>
Update README.md</li>
<li>See full diff in <a
href="https://github.com/dawidd6/action-download-artifact/compare/v9...v10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dawidd6/action-download-artifact&package-manager=github_actions&previous-version=9&new-version=10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 21:37:06 +00:00
Leonardo de Moura
193f59aefe feat: grind +ring by default (#8576)
This PR sets `ring := true` by default in `grind`. It also fixes a bug
in the reification procedure, and improves the term internalization in
the ring and cutsat modules.
2025-06-01 17:46:49 +00:00
Cameron Zwarich
c681cccf1d fix: make simpAppApp? actually bail out on trivial aliases as intended (#8575)
This PR makes LCNF's simpAppApp? bail out on trivial aliases as
intended. It seems that there was a typo in the original logic, and this
PR also extends it to include aliases of global constants rather than
just local vars.
2025-06-01 16:03:17 +00:00
user202729
c6cad5fcff doc: fix a typo in ULift's documentation (#8571)
Just a typo. From my understanding (and the specification otherwise) the
resulting level is the maximum of `r` and `s` instead of the minimum.

No issue opened yet (thus the draft).
2025-06-01 06:25:52 +00:00
Leonardo de Moura
bb6d1e000b feat: generalized Option theorems for grind (#8572)
This PR adds some generalized `Option` theorems for `grind` . The avoid
`casts` operations during E-matching.
2025-06-01 06:25:37 +00:00
Lean stage0 autoupdater
abcfa708f2 chore: update stage0 2025-06-01 05:51:10 +00:00
Mac Malone
ed705306ae fix: invalid field notation error for mvar (#8259)
This PR clarifies the invalid field notation error when projected value
type is a metavariable.

Co-authored-by @sgraf812.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-01 03:02:04 +00:00
Mac Malone
e618a0a4f5 fix: invalid dotted identifier notation error for sort (#8260)
This PR clarifies the invalid dotted identifier notation error when the
type is a sort.

Co-authored-by @sgraf812.

---------

Co-authored-by: Joseph Rotella <7482866+jrr6@users.noreply.github.com>
2025-06-01 03:00:46 +00:00
Leonardo de Moura
db353ab964 fix: ematch generalized patterns (#8570)
This PR fixes some issues in the E-matching generalized pattern support
after the update stage0.
2025-06-01 02:38:29 +00:00
Leonardo de Moura
157ca5a4f3 feat: ematch generalized patterns (#8569)
This PR adds support for generalized E-match patterns to arbitrary
theorems.
2025-05-31 19:08:33 -07:00
jrr6
43aec5b254 fix: improve error-message hint rendering and API (#8486)
This PR improves the rendering of hints in error messages by
consistently indenting diffs and splitting large diffs less granularly;
it also improves the ergonomics of `Lean.MessageData.hint`. Note that
the changes to the signature of `Lean.MessageData.hint` are breaking.

This PR depends on #8457.
2025-06-01 01:22:09 +00:00
Leonardo de Moura
f6c83f3dce chore: adjust test (#8567)
It is working now
2025-06-01 00:21:23 +00:00
Kyle Miller
502380e1f0 fix: record fvar alias info for generalized variables in induction/cases (#8002)
This PR fixes an issue where "go to definition" for variables
generalized by the `induction` and `cases` tactic did not work. Closes
#2873.
2025-05-31 22:27:44 +00:00
Cameron Zwarich
936eb3d62e fix: don't convert Nat multiplication by 2^n to a left shift (#8566)
This PR changes the LCNF constant folding pass to not convert Nat
multiplication to a left shift by a power of 2. The fast path test for
this is sufficiently complex that it's simpler to just use the fast path
for multiplication.
2025-05-31 21:36:55 +00:00
Cameron Zwarich
0c43efc2c9 fix: only treat type/instance params as ground vars in specialization (#8565)
This PR makes the LCNF specialization pass only treat type/instance
params as ground vars. The current policy was too liberal and would
result on computations being floated into specialized loops.
2025-05-31 21:18:24 +00:00
Leonardo de Moura
2c8ee4f29c fix: simplify interface between grind core and cutsat (#8564)
This PR simplifies the interface between the `grind` core and the cutsat
procedure. Before this PR, core would try to minimize the number of
numeric literals that have to be internalized in cutsat. This
optimization was buggy (see `grind_cutsat_zero.lean` test), and produced
counterintuitive counterexamples.
2025-05-31 16:28:31 +00:00
Leonardo de Moura
0988db9ab2 refactor: simplify inferface between core and offset module (#8562)
`processNewEqLit` optimization is not worth the extra complexity.
2025-05-31 15:16:29 +00:00
Cameron Zwarich
adc7b1ed87 fix: increase maxHeartbeats in isDefEqProjIssue test for the new compiler (#8561)
This PR increases maxHeartbeats in the isDefEqProjIssue test, because
when running under the new compiler the `run_meta` call includes the
allocations of the compiler itself. With the old compiler, many of the
corresponding allocations were internal to C++ code and would not
increase the heartbeat count.
2025-05-31 04:56:29 +00:00
Leonardo de Moura
837193b5ec fix: block potential adversarial exploit of non-aborting assert! (#8560)
This PR is similar to #8559 but for `Expr.mkData`. This vulnerability
has not been exploited yet, but adversarial users may find a way.
2025-05-31 03:14:01 +00:00
Leonardo de Moura
6940d2c4ff fix: block adversarial exploit of non-aborting assert! (#8559)
This PR fixes an adversarial soundness attack described in #8554. The
attack exploits the fact that `assert!` no longer aborts execution, and
that users can redirect error messages.
Another PR will implement the same fix for `Expr.Data`.
2025-05-31 00:08:30 +00:00
Paul Reichert
ed4252f8c9 feat: array iterators, repeat/unfold, ForM for iterators (#8552)
This PR provides array iterators (`Array.iter(M)`,
`Array.iterFromIdx(M)`), infinite iterators produced by a step function
(`Iter.repeat`), and a `ForM` instance for finite iterators that is
implemented in terms of `ForIn`.
2025-05-30 18:17:53 +00:00
Leonardo de Moura
8883ca0965 chore: move test (#8550)
It is working now.
2025-05-30 17:13:38 +00:00
Leonardo de Moura
999fcd2d95 fix: hash function for congruence closure in grind (#8549)
This PR fixes the hash function used to implement congruence closure in
`grind`. The hash of an `Expr` must not depend on whether the expression
has been internalized or not.
2025-05-30 17:07:26 +00:00
Paul Reichert
a8ab3f230c feat: introduce iterator combinators takeWhile and dropWhile (#8493)
This PR provides the iterator combinators `takeWhile` (forwarding all
emitted values of another iterator until a predicate becomes false)
`dropWhile` (dropping values until some predicate on these values
becomes false, then forwarding all the others).
2025-05-30 16:35:40 +00:00
Paul Reichert
4f77e05225 feat: introduce zip iterator combinator (#8484)
This PR provides the iterator combinator `zip` in a pure and monadic
version.
2025-05-30 15:20:28 +00:00
Paul Reichert
90462e2551 feat: introduce iterator combinators filterMap, filter and map (#8451)
This PR provides the iterator combinator `filterMap` in a pure and
monadic version and specializations `map` and `filter`. This new
combinator allows to apply a function to the emitted values of a stream
while filtering out certain elements.

`map` should have an optimized `IteratorCollect` implementation but it
turns out that this is not possible without a major refactor of
`IteratorCollect`: `toArrayMapped` requires a proof that the iterator is
finite. If `it.mapM f` is `Finite` but `it` is not, then such a proof
does not exist. `IteratorCollect` needs to take a proof that the loop
will terminate for the given monadic function `f` instead. This will not
be done in this PR.
2025-05-30 13:43:41 +00:00
Paul Reichert
a12f89aefa feat: introduce take iterator combinator (#8418)
This PR provides the `take` iterator combinator that transforms any
iterator into an iterator that stops after a given number of steps. The
change contains the implementation and lemmas.

`take` has a special implementation of `IteratorLoop` that relies on a
potentially more efficient `forIn` implementation of the inner iterator.

The mysterious `@[specialize]` on a test has been removed because it is
not necessary anymore according to a manual inspection of the IR. Either
I erroneously concluded from experiments that it was necessary of
something has changed in the meantime that makes it unnecessary.
2025-05-30 10:34:12 +00:00
Paul Reichert
2d5e8ca311 feat: upstream LawfulMonadLift(T) from Batteries (#8435)
This PR upstreams the `LawfulMonadLift(T)` classes, lemmas and instances
from Batteries into Core because the iterator library needs them in
order to prove lemmas about the `mapM` operator, which relies on
`MonadLiftT`.
2025-05-30 09:14:01 +00:00
Paul Reichert
d60cb88e62 feat: ForIn, fold(M), drain lemmas for iterators (#8405)
This PR provides lemmas about the loop constructs `ForIn`, `fold`,
`foldM` and `drain` and their relation to each other in the context of
iterators.
2025-05-30 09:10:31 +00:00
Leonardo de Moura
d2e01bbd09 feat: overapplied ite and dite applications in grind (#8544)
This PR implements support for over-applied `ite` and `dite`
applications in the `grind` tactic. It adds support for propagation and
case-split.
2025-05-30 06:34:04 +00:00
Leonardo de Moura
069fb4351c fix: inappropriate whnfD uses in grind (#8542)
This PR fixes two inappropriate uses of `whnfD` in `grind`. They were
potential performance foot guns, and were producing unexpected errors
since `whnfD` is not consistently used (and it should not be) in all
modules.
2025-05-30 04:35:29 +00:00
Leonardo de Moura
f54a65f72f feat: nested proof propagation in grind (#8541)
This PR ensures that for any nested proof `h : p` in a goal, we
propagate that `p` is true in the `grind` tactic.
2025-05-30 03:25:14 +00:00
Mac Malone
3817dd57bd fix: lake: precompile imports of non-workspace files by library (#8529)
This PR changes `lake lean` and `lake setup-file` to precompile the
imports of non-workspace files using the the import's whole library.
This ensures that additional link objects are linked and available
during elaboration.

Closes #8448.
2025-05-30 02:28:28 +00:00
Mac Malone
e68c6a38fb feat: lake: relative paths for Lean build messages (#8539)
This PR changes Lake to use relative path for the Lean messages produced
by a module build. This makes the message portable across different
machines, which is useful for Mathlib's cache.
2025-05-30 02:02:35 +00:00
Cameron Zwarich
b7ec369863 fix: allow ground variables to depend on fun decls in LCNF specialize pass (#8540)
This PR changes the LCNF specialize pass to allow ground variables to
depend on local fun decls (with no non-ground free variables). This
enables specialization of Monad instances that depend on local lambdas.
2025-05-30 00:45:00 +00:00
Mac Malone
3fdaf24b49 fix: lake: ensure valid use of (sync := true) (#8531)
This PR fixes some places in Lake where `(sync := true)` was incorrectly
used on code that could block, and more generally improves `(sync :;=
true)` usage.
2025-05-30 00:19:25 +00:00
Kim Morrison
77e16407e4 chore: add test case where grind causes a PANIC (#8538)
Minimized from #8518, thanks @wkrozowski!
2025-05-30 00:12:37 +00:00
Kim Morrison
efd8d149ea chore: add missing lemma for List.range 1 (#8537) 2025-05-30 00:09:51 +00:00
Leonardo de Moura
4316629119 fix: BEq support in grind (#8536)
This PR fixes the support for `LawfulBEq` and `BEq` in `grind`.
2025-05-29 23:47:40 +00:00
jrr6
020da5bffb fix: behavior of hard line breaks in Format strings (#8457)
This PR fixes an issue when including a hard line break in a `Format`
that caused subsequent (ordinary) line breaks to be erroneously
flattened to spaces.

This issue is especially important for displaying notes and hints in
error messages, as these components could appear garbled due to improper
line-break rendering.
2025-05-29 22:10:27 +00:00
Sebastian Ullrich
bc8189b61d perf: avoid Environment.find? block in addDecl (#8533) 2025-05-29 21:32:37 +00:00
Cameron Zwarich
e30303e33c fix: extract more Nats in extractClosed (#8535)
This PR extracts more Nats (and their downstream users) in extractClosed
by fixing a silly oversight in the logic.
2025-05-29 21:11:21 +00:00
Sebastian Ullrich
1879a2bafc fix: SnapshotTree.waitAll (#8532) 2025-05-29 20:12:23 +00:00
Mac Malone
3b72c7d193 fix: lake: better library plugin heuristic (#8528)
This PR fixes the heuristic Lake uses to determine whether a `lean_lib`
can be loaded via `lean --plugin` rather than `lean --load-dynlib`.
Previously, a mismatch between the single root's name and the library's
name would not be caught and cause loading to fail.
2025-05-29 17:48:05 +00:00
Kim Morrison
22d4c1d803 chore: failing grind tests (subset of #8518) (#8526)
This is a subset of tests from #8518 that are fully minimized. I'll
merge this first.

---------

Co-authored-by: Wojciech Rozowski <wojciech@lean-fro.org>
2025-05-29 11:48:19 +00:00
Kim Morrison
0fe23b7fd6 feat: initial @[grind] annotations for List.count (#8527)
This PR adds `grind` annotations for theorems about `List.countP` and
`List.count`.
2025-05-29 11:46:44 +00:00
Kim Morrison
72141b05fd chore: add failing grind test (#8524) 2025-05-29 05:59:58 +00:00
Leonardo de Moura
1fd7206f00 feat: match-expressions with congruence equation theorems (#8506)
This PR implements `match`-expressions in `grind` using `match`
congruence equations. The goal is to minimize the number of `cast`
operations that need to be inserted, and avoid `cast` over functions.
The new approach support `match`-expressions of the form `match h : ...
with ...`.
2025-05-29 02:23:26 +00:00
Cameron Zwarich
a6e76b424c fix: move the new compiler's noncomputable check into toMono (#8523)
This PR moves the new compiler's noncomputable check into toMono,
matching the recent change in the old compiler. This is mildly more
complicated because we can't throw an error at the mere use of a
constant, we need to check for a later relevant use. This is still a bit
more conservative than it could theoretically be around join points and
local functions, but it's hard to imagine that mattering in practice
(and we can easily enable it if it does).
2025-05-29 00:40:25 +00:00
Kyle Miller
4dd8648a25 feat: different syntax for new clear_value tactic (#8516)
This PR is a followup to #8449 to refine the syntax of `clear_value`.
The syntax for adding equality hypotheses before clearing values is now
`clear_value (h : x = _)`. Any expression definitionally equal to `x`
can be used in place of the underscore.

This syntax was developed in a [Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/.60clear_value.60.20syntax.20request.20for.20comments/near/520704290).
2025-05-28 22:33:35 +00:00
Cameron Zwarich
5814c1e757 fix: recursively process jmp args in LCNF.toMono (#8521)
This PR makes LCNF.toMono recursively process jmp args.
2025-05-28 20:56:03 +00:00
Kyle Miller
c3a010a938 feat: use dot notation for class parent projections (#8504)
This PR modifies the pretty printer so that dot notation is used for
class parent projections. Previously, dot notation was never used for
classes.

We still need to modify dot notation to take the method resolution order
into account when collapsing parent projections.
2025-05-28 20:34:40 +00:00
Kim Morrison
bd14e7079b fix: make Array.size not reducible (#8513)
This PR removes the `@[reducible]` annotation on `Array.size`. This is
probably best gone anyway in order to keep separation between the `List`
and `Array` APIs, but it also helps avoid uselessly instantiating
`Array` theorems when `grind` is working on `List` problems.
2025-05-28 12:37:24 +00:00
Sebastian Ullrich
f214708636 chore: update stage0 2025-05-28 14:27:31 +02:00
Sebastian Ullrich
5d7e09ddad feat: [no_expose] attribute 2025-05-28 14:26:22 +02:00
Kim Morrison
c6194e05b8 chore: remove prime from Fin.ofNat' (#8515)
This PR removes the prime from `Fin.ofNat'`: the old `Fin.ofNat` has
completed its 6 month deprecation cycle and is being removed.
2025-05-28 11:51:00 +00:00
Kim Morrison
1087ec9225 chore: remove >6 month old deprecations (#8514) 2025-05-28 11:28:03 +00:00
Kyle Miller
c5bea23a54 feat: value_of% elaborator (#8512)
This PR adds a `value_of% ident` term that elaborates to the value of
the local or global constant `ident`. This is useful for creating
definition hypotheses:
```lean
let x := ... complicated expression ...
have hx : x = value_of% x := rfl
```
2025-05-28 11:12:11 +00:00
Kim Morrison
ede085ae74 chore: add failing grind test (#8509) 2025-05-28 08:56:23 +00:00
Sebastian Ullrich
067fa83b1a chore: update stage0 2025-05-28 10:18:04 +02:00
Sebastian Ullrich
af1d8dd070 feat: := private instance syntax 2025-05-28 10:18:04 +02:00
Joachim Breitner
803dc3e687 refactor: Init: expose lots of functions (#8501)
This PR adds the `@[expose]` attribute to many functions (and changes
some theorems to be by `:= (rfl)`) in preparation for the `@[defeq]`
attribute change in #8419.
2025-05-28 07:37:54 +00:00
Kyle Miller
921ce7682e feat: use omission dots for hidden let values in Infoview (#8041)
This PR changes the behavior of `pp.showLetValues` to use a hoverable
`⋯` to hide let values. This is now false by default, and there is a new
option `pp.showLetValues.threshold` for allowing small expressions to be
shown anyway. For tactic metavariables, there is an additional option
`pp.showLetValues.tactic.threshold`, which by default is set to the
maximal value, since in tactic states local values are usually
significant.
2025-05-27 23:09:11 +00:00
Leonardo de Moura
5187cb37a9 chore: notation for HEq (#8503) 2025-05-27 19:22:57 +00:00
Cameron Zwarich
632d078a70 fix: use kernel environment to find definitions in the new compiler (#8502)
This PR changes the new compiler to use the kernel environment to find
definitions, which causes compilation to be skipped when the decl had a
kernel error (e.g. due to an unresolved metavariable). This matches the
behavior of the old compiler.

This will need to be revisited in the future when we want to make
compilation more asynchronous.
2025-05-27 16:56:00 +00:00
Luisa Cicolini
5fda4c1023 feat: BitVec.[toNat|toInt] non-overflow simp lemmas (#8492)
This PR adds `simp` lemmas for `toInt_*` and `toNat_*` with arithmetic
operation given the hypothesis of no-overflow
(`toNat_add_of_not_uaddOverflow`, `toInt_add_of_not_saddOverflow`,
`toNat_sub_of_not_usubOverflow`, `toInt_sub_of_not_ssubOverflow`,
`toInt_neg_of_not_negOverflow`, `toNat_mul_of_not_umulOverflow`,
`toInt_mul_of_not_smulOverflow`). In particular, these are `simp` since
(1) the `rhs` is strictly simpler than the `lhs` and (2) this version is
also simpler than the standard operation when the hypothesis is
available.
 
co-authored by @tobiasgrosser

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-05-27 15:13:43 +00:00
Kim Morrison
a4fb2eef47 feat: make Array.ofFn.go use fuel (#8499)
This PR changes the definition of `Array.ofFn.go` to use recursion on
`Nat` (rather than well-founded recursion). This resolves a problem
reported on [zulip]([#lean4 > Memory issues with &#96;Vector.ofFn&#96;.
@
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Memory.20issues.20with.20.60Vector.2EofFn.60.2E/near/520622564)).
2025-05-27 13:44:28 +00:00
Kim Morrison
87152a3fae feat: grind annotations for List.Sublist/IsInfix/IsPrefix/IsSuffix (#8497)
This PR adds preliminary grind annotations for
`List.Sublist`/`IsInfix`/`IsPrefix`/`IsSuffix`, along with test cases.
2025-05-27 12:56:43 +00:00
Tobias Grosser
ed6c78048e chore: skip OS X aarch64 CI only in merge groups (#8334)
This PR enables the build of all artifacts for custom releases, e.g.,
releases outside the main lean4 repository.

This resolves https://github.com/leanprover/lean4/issues/8333.
2025-05-27 11:51:59 +00:00
Kim Morrison
3ab60c59fe chore: missing @[grind] annotations for Array (#8495) 2025-05-27 09:56:10 +00:00
Kim Morrison
eaa1bc14ed chore: more simp lemmas for LawfulGetElem (#8470)
This PR adds `@[simp]` to `getElem_pos/neg` (similarly for `getElem!`).
These are often already simp lemmas for concrete types.
2025-05-27 09:41:22 +00:00
Rob23oba
a912652b7d fix: simp_all? and simp_all?! (#8491)
This PR fixes the behavior of `simp_all?` and `simp_all?!`, aligning
them with `simp_all` and `simp_all!` respectively.

Closes #8490
2025-05-27 07:07:12 +00:00
Kyle Miller
3af9ab64ed feat: subst tactic can substitute let values (#8450)
This PR adds a feature to the `subst` tactic so that when `x : X := v`
is a local definition, `subst x` substitutes `v` for `x` in the goal and
removes `x`. Previously the tactic would throw an error.
2025-05-27 06:06:35 +00:00
Kyle Miller
a6dd6a4656 feat: clear_value tactic (#8449)
This PR upstreams and extends the Mathlib `clear_value` tactic. Given a
local definition `x : T := v`, the tactic `clear_value x` replaces it
with a hypothesis `x : T`, or throws an error if the goal does not
depend on the value `v`. The syntax `clear_value x with h` creates a
hypothesis `h : x = v` before clearing the value of `x`. Furthermore,
`clear_value *` clears all values that can be cleared, or throws an
error if none can be cleared.
2025-05-27 01:52:08 +00:00
Kim Morrison
1e752b0a01 chore: cleanup simp lemmas, following the simpNF linter (#8481) 2025-05-26 04:13:17 +00:00
Leonardo de Moura
11f7d6da39 feat: reuse simp cache in grind (#8483)
This PR ensures `grind` reuses the `simp` cache between different calls.
Recall that `grind` uses `simp` to normalize terms during
internalization.
2025-05-26 04:10:58 +00:00
Kim Morrison
e2fc9ba92e feat: grind annotations for List.Pairwise/Nodup (#8482)
This PR adds preliminary `@[grind]` annotations for `List.Pairwise` and
`List.Nodup`.
2025-05-26 03:13:18 +00:00
Kim Morrison
c1866a7b7e chore: fix awaiting-mathlib.yml (#8480)
This PR hopefully fixes a problem from #8471, which even the most
cursory testing (by me!) should have detected.
2025-05-26 02:13:00 +00:00
Leonardo de Moura
03e905d994 feat: hash consing with alpha equivalence in grind (#8479)
This PR implements hash-consing for `grind` that takes alpha equivalence
into account.
2025-05-26 00:51:18 +00:00
Kim Morrison
383f68f806 chore: add grind_trig test case (#8476) 2025-05-26 00:03:53 +00:00
Kim Morrison
41c2ae12f3 chore: update syntax in grind_ite example (#8475) 2025-05-25 23:21:11 +00:00
Sebastian Ullrich
9982bab93e perf: Environment.find? should not block on privacy mismatch (#8472)
This PR avoids name resolution blocking on the elaboration of a
theorem's proof when looking up the theorem name.
2025-05-25 16:18:57 +00:00
Cameron Zwarich
be513656b0 fix: use a custom environment extension for LCNF decls (#8468)
This PR switches the LCNF baseExt/monoExt environment extensions to use
a custom environment extension that uses a PersistentHashMap. The
optimizer relies upon the ability to update a decl multiple times, which
does not work with SimplePersistentEnvExtension.
2025-05-25 15:11:54 +00:00
Kim Morrison
bdbb659765 chore: while awaiting-mathlib, show yellow status not red (#8471)
This PR changes the CI check when the `awaiting-mathlib` label is
present. If `breaks-mathlib` is present, it shows a red cross, but if
neither `breaks-mathlib` nor `builds-mathlib` is present it shows a
yellow circle.
2025-05-25 12:38:56 +00:00
Leonardo de Moura
2a1354b3cc chore: add seal to workaround performance issue (#8469)
This PR adds `seal` commands at `grind_ite.lean` to workaround expensive
definitionally equality tests in the canonicalizer. The new module
system will automatically hide definitions such as `HashMap.insert` and
`TreeMap.insert` which are being unfolded by the canonicalizer in this
test.
This PR also adds a `profileItM` for tracking the time spent in the
`grind` canonicalizer.
2025-05-25 00:54:30 +00:00
Leonardo de Moura
a54872f5f6 fix: preprocessLight at ensureInternalized (#8466)
This PR fixes another instance of the `grind` issue "unexpected kernel
projection term during internalization".
2025-05-24 17:13:20 +00:00
Kim Morrison
2b0b1e013f feat: further generic GetElem lemmas (#8465)
This PR adds further lemmas about `LawfulGetElem`, including marking
some with `@[grind]`.
2025-05-24 12:58:29 +00:00
Mario Carneiro
1f000feb80 chore: remove unnecessary partial in Lean.Expr (#8464)
The termination prover has gotten stronger since these definitions were
written, and now they can be proved terminating automatically. (One
definition had to be changed slightly because it wasn't actually
terminating before.)
2025-05-24 07:00:37 +00:00
Cameron Zwarich
d5060e9e66 feat: add extractClosed pass to LCNF pass list (#8462)
This PR enables the LCNF extractClosed pass by default.
2025-05-24 05:20:10 +00:00
Kim Morrison
38ca310fb7 feat: @[grind] annotations for TreeMap (#8446)
This PR adds basic `@[grind]` annotations for `TreeMap` and its
variants. Likely more annotations will be added after we've explored
some examples.
2025-05-24 04:49:54 +00:00
Kim Morrison
3dd12f85f0 feat: further @[grind] annotations for Option (#8460)
This PR adds further `@[grind]` annotations for `Option`, as follow-up
to the recent additions to the `Option` API in #8379 and #8298.

**However**, I am concurrently investigating adding `attribute [grind
cases] Option`, which will result in many (most?) of the annotations for
`Option` being removed again. In any case, I'm going to merge this
first, as if that is viable I would like to test that most/all the
lemmas now marked with `@[grind]` are still provable by `grind`.
2025-05-24 04:25:00 +00:00
Kim Morrison
0f8618f842 chore: remove @[grind] from Array.size_eq_zero_iff` (#8461) 2025-05-24 04:20:52 +00:00
Kim Morrison
acdef6e04b feat: verification of qsort via grind (#7995)
This PR adds a verification of `Array.qsort` properties, trying to use
`grind` and `fun_induction` where possible.
Currently this is in the `tests/` folder, but once `grind` is ready for
production use we will move it out into the library.

Note that the current `qsort` algorithm has quadratic behaviour on
constant lists, and needs to be adjusted. We'll only move the
verification out into the library once this has been fixed (and the
proofs adapted). These verification theorems may be commented out in the
meantime if it's urgent to fix `qsort`.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-24 04:01:55 +00:00
Cameron Zwarich
7b80cd24a9 feat: closed term extraction in the new compiler (#8458)
This PR adds closed term extraction to the new compiler, closely
following the approach in the old compiler. In the future, we will
explore some ideas to improve upon this approach.
2025-05-24 02:40:37 +00:00
Leonardo de Moura
21846ebdf8 feat: non-chronological backtracking for grind (WIP) (#8440)
This PR implements non-chronological backtracking for the `grind`
tactic. This feature ensures that `grind` does not need to process
irrelevant branches after performing a case-split that is not relevant.
It is not just about performance, but also the size of the final proof
term. The new test demonstrates this feature in practice.
```lean
-- In the following test, the first 8 case-splits are irrelevant,
-- and non-choronological backtracking is used to avoid searching
-- (2^8 - 1) irrelevant branches
/--
trace: 
[grind.split] p8 ∨ q8, generation: 0
[grind.split] p7 ∨ q7, generation: 0
[grind.split] p6 ∨ q6, generation: 0
[grind.split] p5 ∨ q5, generation: 0
[grind.split] p4 ∨ q4, generation: 0
[grind.split] p3 ∨ q3, generation: 0
[grind.split] p2 ∨ q2, generation: 0
[grind.split] p1 ∨ q1, generation: 0
[grind.split] ¬p ∨ ¬q, generation: 0
-/
#guard_msgs (trace) in
set_option trace.grind.split true in
theorem ex
    : p ∨ q →
      ¬ p ∨ q →
      p ∨ ¬ q →
      ¬ p ∨ ¬ q →
      p1 ∨ q1 →
      p2 ∨ q2 →
      p3 ∨ q3 →
      p4 ∨ q4 →
      p5 ∨ q5 →
      p6 ∨ q6 →
      p7 ∨ q7 →
      p8 ∨ q8 →
      False := by
  grind (splits := 10)
```
2025-05-23 19:33:54 +00:00
Cameron Zwarich
9ea4946560 feat: add support for USize literals in LCNF (#8456)
This PR adds support for primitive USize literals in LCNF.
2025-05-23 17:22:31 +00:00
Cameron Zwarich
3b205505ef chore: clean up structProjCases pass (#8455) 2025-05-23 15:46:21 +00:00
Lean stage0 autoupdater
6afa8208ec chore: update stage0 2025-05-23 15:21:08 +00:00
Rob23oba
65a5d0cb9d feat: improve Ord proof api (#8378)
This PR improves and extends the api around `Ord` and `Ordering`. These
changes are split off from #8210.
2025-05-23 14:00:20 +00:00
Joachim Breitner
fc3c82b1c7 chore: denixify stage0-updater workflow (#8452)
This PR lets the stage0 autoupdater build lean using the `cmake`
infrastructure, not the deprecated nix infrastructure.
2025-05-23 13:12:50 +00:00
Sebastian Graf
8fc94c5c90 fix: Make split work with metavariables in the target (#8437)
This PR fixes `split` in the presence of metavariables in the target.

The fix consists of replacing an internal use of `apply` for
instantiating match splitters by a new, simpler variant `applyN`. This
new `applyN` is not prone to #8436, which is the ultimate cause for
`split` failing on targets containing metavariables.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-23 12:46:27 +00:00
Paul Reichert
96b81f3cc1 feat: lemmas about list iterators (#8384)
This PR provides lemmas about the behavior of `step`, `toArray`,
`toList` and `toListRev` on list iterators created with `List.iter` and
`List.iterM`.
2025-05-23 09:29:59 +00:00
Kim Morrison
44ff70020d feat: add simp lemma writing Vector.tail in terms of Vector.extract (#8445)
This PR adds a `@[simp]` lemma, and comments explaining that there is
intentionally no verification API for `Vector.take`, `Vector.drop`, or
`Vector.tail`, which should all be rewritten in terms of
`Vector.extract`.
2025-05-22 23:22:54 +00:00
Eric Wieser
ae1ab94992 fix: replace bad simp lemmas for Id (#7352)
This PR reworks the `simp` set around the `Id` monad, to not elide or
unfold `pure` and `Id.run`

In particular, it stops encoding the "defeq abuse" of `Id X = X` in the
statements of theorems, instead using `Id.run` and `pure` to pass back
and forth between these two spellings. Often when writing these with
`pure`, they generalize to other lawful monads; though such changes were
split off to other PRs.

This fixes the problem with the current simp set where `Id.run (pure x)`
is simplified to `Id.run x`, instead of the desirable `x`.
This is particularly bad because the` x` is sometimes inferred with type
`Id X` instead of `X`, which prevents other `simp` lemmas about `X` from
firing.

Making `Id` reducible instead is not an option, as then the `Monad`
instances would have nothing to key on.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-05-22 22:45:35 +00:00
Joachim Breitner
5e40f4af52 feat: linear-size noConfusionType construction (#8037)
This PR introduces a `noConfusionType` construction that’s sub-quadratic
in size, and reduces faster.

The previous `noConfusion` construction with two nested `match`
statements is quadratic in size and reduction behavior. Using some
helper definitions, a linear size construction is possible.

With this, processing the RISC-V-AST definition from
https://github.com/opencompl/sail-riscv-lean takes 6s instead of 60s.

The previous construction is still used when processing the early
prelude, and can be enabled elsewhere using `set_option
backwards.linearNoConfusionType false`.
2025-05-22 14:54:05 +00:00
Rob23oba
2594a8edad fix: namespace completion to only use the short name (#8350)
This PR changes namespace completion to use the same algorithm as
declaration identifier completion, which makes it use the short name
(last name component) for completions instead of the full name, avoiding
namespace duplications.

Closes #5654
2025-05-22 11:58:47 +00:00
Kim Morrison
b24e232a7a feat: lemmas about ordered rings and fields for grind (#8443)
This PR adds the lemmas about ordered rings and ordered fields which
will be needed by the new algebraic normalization components of `grind`.
2025-05-22 11:41:51 +00:00
Jakob von Raumer
9ad3974314 feat: add List.drop_cons (#8434)
This PR adds the equivalent of `List.take_cons` about `List.drop`.
2025-05-22 11:29:42 +00:00
Lean stage0 autoupdater
b31bf4e645 chore: update stage0 2025-05-22 11:24:54 +00:00
Marc Huisinga
c8d245a08f fix: unknown identifier ranges (#8362)
This PR fixes a bug where the unknown identifier code actions wouldn't
work correctly for some unknown identifier error spans and adjusts
several unknown identifier spans to actually end on the identifier in
question.

The following additional adjustments are made:
- The fallback mechanism of the unknown identifier code actions is
removed, since it could produce severely incorrect suggestions for
unknown identifier errors on fields.
- A performance bug when using the code action to import all unknown
identifiers is fixed.
- A bug that occurs when the elaborator produces multiple overlapping
completion infos is fixed.
- A bug in the snapshot selection that could cause it to wait for
snapshots in snapshots with non-canonical syntax is fixed.
- Some invariants of the snapshot tree are documented.
- The snapshot tree formatting is adjusted to display the final info
tree again.
2025-05-22 10:05:31 +00:00
Leonardo de Moura
4eccb5b479 fix: grind diagnostics at maxHeartbeats (#8438)
This PR ensures that `grind` diagnostics are obtained even when
`maxHeartbeats` is reached.
This PR also removes some dead code.
2025-05-21 22:14:59 +00:00
Paul Reichert
0a43c138ac feat: lemmas about iterator collectors (#8380)
This PR provides simple lemmas about `toArray`, `toList` and `toListRev`
for the iterator library.

It also changes the definition of `Iter` and `IterM` so that they aren't
equal anymore and in particular not definitionally equal. While it was
very convenient to have them be definitionally equal when working with
dependent code, it was also confusing and annoying that one would
sometimes end up with something like `it.toList = IterM.toList it`,
where `it : Iter β`.
2025-05-21 21:11:26 +00:00
Arthur Adjedj
1138062a70 fix: normalize imax 1 u to u (#7631)
This PR fixes `Lean.Level.mkIMaxAux` (`mk_imax` in the kernel) such that
`imax 1 u` reduces to `u`.

Closes #7096
2025-05-21 20:27:53 +00:00
grunweg
ebf455a137 doc: clarify that .now returns a date(time) in the local time zone (#8331)
This PR improves the docstring for `PlainDateTime.now` and its variants.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-05-21 08:04:36 +00:00
Kim Morrison
87cc330489 feat: ordered ring typeclass for grind (#8429)
This PR adds `Lean.Grind.Ring.IsOrdered`, and cleans up the ring/module
grind API. These typeclasses are at present unused, but will support
future algorithmic improvements in `grind`.
2025-05-21 07:05:01 +00:00
Kim Morrison
47a1355fc4 chore: cleanup grind palindrome test (#8428) 2025-05-21 03:31:56 +00:00
Kim Morrison
79254d039c chore: restore @[simp] to List.ofFn_succ (#8427) 2025-05-21 03:12:26 +00:00
Leonardo de Moura
c28b052576 feat: [grind?] attribute (#8426)
This PR adds the attribute `[grind?]`. It is like `[grind]` but displays
inferred E-matching patterns. It is a more convinient than writing.
Thanks @kim-em for suggesting this feature.
```lean
set_option trace.grind.ematch.pattern true
```
This PR also improves some tests, and adds helper function
`ENode.isRoot`.
2025-05-21 00:32:49 +00:00
Kim Morrison
a541b8e75e chore: fix name of new Fin.foldlM_eq_finRange_foldlM lemmas (#8425) 2025-05-21 00:30:33 +00:00
Li Xuanji
a9a069a0ef doc: Fix doc bug in Resolve.lean (#8411)
This PR fixes a doc bug in the Resolve.lean; in reverse order, B comes
before A
2025-05-20 17:16:18 +00:00
Leonardo de Moura
8753239226 chore: remove Grind.Config.failures options (#8423)
Option is not very useful.
2025-05-20 15:40:51 +00:00
Paul Reichert
f4ee72b18c feat: minimal iterator library (#8358)
This PR introduces a very minimal version of the new iterator library.
It comes with list iterators and various consumers, namely `toArray`,
`toList`, `toListRev`, `ForIn`, `fold`, `foldM` and `drain`. All
consumers also come in a partial variant that can be used without any
proofs. This limited version of the iterator library generates decent
code, even with the old code generator.
2025-05-20 14:53:57 +00:00
Leonardo de Moura
8535a2268b fix: simplify isCasesAttrCandidate? in grind (#8415)
The behavior was counterintuitive.
2025-05-20 14:29:07 +00:00
Kim Morrison
d8e7ca2355 feat: draft typeclasses/tests for grind handling fields (#8417)
This PR introduces `Lean.Grind.Field`, proves that a `IsCharP 0` field
satisfies `NoNatZeroDivisors`, and sets up some basic (currently
failing) tests for `grind`.
2025-05-20 13:44:11 +00:00
Henrik Böving
8e0870beec feat: LT for Timestamp and Duration (#8422)
This PR adds `LT` and `Decidable` `LT` instances for
`Std.Time.Timestamp` and `Std.Time.Duration`.
2025-05-20 11:33:49 +00:00
Kim Morrison
3790f8c78e chore: deduplicate Grind.RatModule and Grind.NoNatZeroDivisors (#8416)
Also adds instances from e.g. `Semiring` to `NatModule` and `Ring` to
`IntModule`.
2025-05-20 07:49:42 +00:00
Kim Morrison
3bf95e9b58 feat: add List/Array/Vector.ofFnM (#8389)
This PR adds the `List/Array/Vector.ofFnM`, the monadic analogues of
`ofFn`, along with basic theory.

At the same time we pave some potholes in nearby API.

---------

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2025-05-20 05:28:29 +00:00
Kim Morrison
bc21b57396 chore: use HMul in Lean.Grind.Module (#8414) 2025-05-20 04:22:06 +00:00
Kim Morrison
6395d69140 feat: add HashMap.get*_filter* lemmas specialized for LawfulBEq (#8399)
This PR adds variants of `HashMap.getElem?_filter` that assume
`LawfulBEq` and have a simpler right-hand-side. `simp` can already
achieve these, via rewriting with `getKey_eq` under the lambda. However
`grind` can not, and these lemmas help `grind` work with `HashMap`
goals. There are variants for all variants of `HashMap`,
`getElem?/getElem/getElem!/getD`, and for `filter` and `filterMap`.
2025-05-20 03:04:32 +00:00
Leonardo de Moura
4ba72aeef7 feat: missing normalization rules in grind (#8413)
This PR implements normalization rules that pull universal quantifiers
across disjunctions. This is a common normalization step performed by
first-order theorem provers.
2025-05-20 02:38:29 +00:00
Leonardo de Moura
e984473886 fix: markNestedProofs preprocessor in grind (#8412)
This PR fixes the `markNestedProofs` preprocessor used in `grind`. There
was a missing case (e.g., `Expr.mdata`)
2025-05-20 01:46:23 +00:00
Leonardo de Moura
88f6439955 fix: case-splitting in grind (#8410)
This PR fixes a case-splitting heuristic in `grind` and simplifies the
proof for test `grind_palindrome2.lean`.
2025-05-20 00:51:47 +00:00
Cameron Zwarich
fc8f290347 feat: support native literals of size unsigned integer types (#8409)
This PR adds support to LCNF for native UInt8/UInt16/UInt32/UInt64
literals.
2025-05-20 00:38:38 +00:00
Cameron Zwarich
423b31755d chore: remove dependency of pretty-printing LCNF.LitValue on toExpr (#8408) 2025-05-19 22:55:21 +00:00
jrr6
d1ec806834 feat: improve error messages in invalid match alternatives (#8368)
This PR improves the error messages produced by invalid pattern-match
alternatives and improves parity in error placement between
pattern-matching tactics and elaborators.

Closes #7170
2025-05-19 17:40:41 +00:00
jrr6
b93231f97e feat: improve inductive type parameter error messages (#8338)
This PR improves the error messages displayed in `inductive`
declarations when type parameters are invalid or absent.

Closes #2195 by improving the relevant error message.
2025-05-19 17:03:49 +00:00
Kim Morrison
f40d72ea47 feat: typeclasses for grind to work with ordered modules (#8347)
This PR adds draft typeclasses for `grind` to process facts about
ordered modules. These interfaces will evolve as the implementation
develops.
2025-05-19 13:55:38 +00:00
Kim Morrison
10fdfc54cb chore: upstream HSMul notation typeclass (#8401)
Upstreaming the `HSMul` notation typeclass, to enable `grind` to process
goals using it.
2025-05-19 12:37:08 +00:00
David Thrane Christiansen
943a9c6a43 chore: revert mistaken deletion (#8404)
This PR reverts the deletion of files that should not have been removed
with the old documentation site.
2025-05-19 12:14:09 +00:00
Wojciech Rozowski
a8a6f71abb fix: add monotonicity lemmas for universal quantifiers (#8403)
This PR adds missing monotonicity lemmas for universal quantifiers, that
are used in defining (co)inductive predicates.
2025-05-19 11:27:46 +00:00
Markus Himmel
9ad4414642 feat: Option lemmas (#8379)
This PR adds missing `Option` lemmas.

Also:

- generalize `bindM` from `Monad` to `Pure`
- change the `simp` normal form of both `<|>` and `Option.orElse` to
`Option.or`
2025-05-19 08:59:31 +00:00
Kim Morrison
efe2ab4c04 chore: remove duplicate instances (#8397)
This PR cleans up many duplicate instances (or, in some cases,
needlessly duplicated `def X := ...; instance Y := X`).
2025-05-19 04:36:06 +00:00
Cameron Zwarich
831026bcf4 chore: remove redundant ToFormat/ToString debug printing instances (#8400) 2025-05-19 03:31:22 +00:00
2803 changed files with 53780 additions and 14031 deletions

View File

@@ -10,11 +10,29 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check awaiting-mathlib label
id: 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');
const { labels, number: prNumber } = context.payload.pull_request;
const hasAwaiting = labels.some(label => label.name == "awaiting-mathlib");
const hasBreaks = labels.some(label => label.name == "breaks-mathlib");
const hasBuilds = labels.some(label => label.name == "builds-mathlib");
if (hasAwaiting && hasBreaks) {
core.setFailed('PR has both "awaiting-mathlib" and "breaks-mathlib" labels.');
} else if (hasAwaiting && !hasBreaks && !hasBuilds) {
core.info('PR is marked "awaiting-mathlib" but neither "breaks-mathlib" nor "builds-mathlib" labels are present.');
core.setOutput('awaiting', 'true');
}
- name: Wait for mathlib compatibility
if: github.event_name == 'pull_request' && steps.check-awaiting-mathlib-label.outputs.awaiting == 'true'
run: |
echo "::notice title=Awaiting mathlib::PR is marked 'awaiting-mathlib' but neither 'breaks-mathlib' nor 'builds-mathlib' labels are present."
echo "This check will remain in progress until the PR is updated with appropriate mathlib compatibility labels."
# Keep the job running indefinitely to show "in progress" status
while true; do
sleep 3600 # Sleep for 1 hour at a time
done

View File

@@ -82,7 +82,7 @@ jobs:
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-*
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-* tests/lean/run/importStructure.lean
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
@@ -104,12 +104,12 @@ jobs:
# NOTE: must be in sync with `save` below
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean
${{ matrix.name == 'Linux Lake' && false && '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 }}
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
@@ -127,9 +127,12 @@ jobs:
[ -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)
OPTIONS=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.release }}' ]]; then
# this also enables githash embedding into stage 1 library, which prohibits reusing
# `.olean`s across commits, so we don't do it in the fast non-release CI
OPTIONS+=(-DCHECK_OLEAN_VERSION=ON)
fi
if [[ -n '${{ matrix.cross_target }}' ]]; then
# used by `prepare-llvm`
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
@@ -193,7 +196,7 @@ jobs:
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 || matrix.name == 'Linux release')
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2
with:
@@ -210,7 +213,7 @@ jobs:
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
if: matrix.check-stage3
- name: Test Speedcenter Benchmarks
run: |
# Necessary for some timing metrics but does not work on Namespace runners
@@ -224,7 +227,7 @@ jobs:
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
if: matrix.check-rebootstrap
- name: CCache stats
if: always()
run: ccache -s
@@ -242,8 +245,8 @@ jobs:
# NOTE: must be in sync with `restore` above
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean
${{ matrix.name == 'Linux Lake' && false && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}

View File

@@ -103,6 +103,13 @@ jobs:
echo "Tag ${TAG_NAME} did not match SemVer regex."
fi
- name: Check for custom releases (e.g., not in the main lean repository)
if: startsWith(github.ref, 'refs/tags/') && github.repository != 'leanprover/lean4'
id: set-release-custom
run: |
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -111,7 +118,7 @@ jobs:
run: |
check_level=0
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" ]]; then
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
@@ -157,9 +164,15 @@ jobs:
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large && level < 2 ? "nscloud-ubuntu-22.04-amd64-4x16" : "ubuntu-latest",
"release": true,
"check-level": 0,
// Special handling for release jobs. We want:
// 1. To run it in PRs so developrs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the
// Linux lake build and adds little value in the merge queue
// 3. To run it in release (obviously)
"check-level": isPr ? 0 : 2,
"secondary": isPr,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
@@ -169,21 +182,14 @@ jobs:
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16" : "ubuntu-latest",
"check-level": 0,
// just a secondary build job for now until false positives can be excluded
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// TODO: importStructure is not compatible with .olean caching
// TODO: why does scopedMacros fail?
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
},
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"test": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
"test-speedcenter": level >= 2,
"check-level": 1,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
},
{
"name": "Linux Reldebug",
@@ -216,7 +222,8 @@ jobs:
},
{
"name": "macOS aarch64",
"os": "macos-14",
// standard GH runner only comes with 7GB so use large runner if possible
"os": large ? "nscloud-macos-sonoma-arm64-6x14" : "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
@@ -224,11 +231,7 @@ jobs:
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"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)
// See above for release job levels
"check-level": isPr ? 0 : 2,
"secondary": isPr,
},
@@ -247,7 +250,7 @@ jobs:
},
{
"name": "Linux aarch64",
"os": "nscloud-ubuntu-22.04-arm64-4x8",
"os": "nscloud-ubuntu-22.04-arm64-4x16",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
@@ -357,7 +360,7 @@ jobs:
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -401,7 +404,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
body_path: diff.md
prerelease: true

View File

@@ -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@v9 # https://github.com/marketplace/actions/download-workflow-artifact
uses: dawidd6/action-download-artifact@v10 # https://github.com/marketplace/actions/download-workflow-artifact
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
@@ -48,19 +48,30 @@ jobs:
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
git -C lean4.git fetch -n origin master
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
# Create both the original tag and the SHA-suffixed tag
SHORT_SHA="${{ steps.workflow-info.outputs.sourceHeadSha }}"
SHORT_SHA="${SHORT_SHA:0:7}"
# Export the short SHA for use in subsequent steps
echo "SHORT_SHA=${SHORT_SHA}" >> "$GITHUB_ENV"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}" "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}"
- name: Delete existing release if present
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
# Try to delete any existing release for the current PR.
# Try to delete any existing release for the current PR (just the version without the SHA suffix).
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -73,7 +84,22 @@ jobs:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
files: artifacts/*/*
fail_on_unmatched_files: true
draft: false
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}
repository: ${{ github.repository_owner }}/lean4-pr-releases
env:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
@@ -87,6 +113,20 @@ jobs:
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}",
});
- name: Report release status (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
state: "success",
context: "PR toolchain (SHA-suffixed)",
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}",
});
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
@@ -282,16 +322,18 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, pushing an empty commit."
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Batteries `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -346,21 +388,23 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean
lake update batteries
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, merging $BASE and bumping Batteries."
echo "Branch already exists, updating lean-toolchain and bumping Batteries."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
lake update batteries
git add lake-manifest.json
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes

View File

@@ -40,34 +40,24 @@ jobs:
run: |
git config --global user.name "Lean stage0 autoupdater"
git config --global user.email "<>"
# Would be nice, but does not work yet:
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
# This action does not run that often and building runs in a few minutes, so ok for now
#- if: env.should_update_stage0 == 'yes'
# uses: DeterminateSystems/magic-nix-cache-action@v2
- if: env.should_update_stage0 == 'yes'
name: Restore Build Cache
uses: actions/cache/restore@v4
with:
path: nix-store-cache
key: Nix Linux-nix-store-cache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
Nix Linux-nix-store-cache
- if: env.should_update_stage0 == 'yes'
name: Further Set Up Nix Cache
shell: bash -euxo pipefail {0}
run: |
# Nix seems to mutate the cache, so make a copy
cp -r nix-store-cache nix-store-cache-copy || true
- if: env.should_update_stage0 == 'yes'
name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
extra-conf: |
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Open Nix shell once
if: env.should_update_stage0 == 'yes'
run: true
shell: 'nix develop -c bash -euxo pipefail {0}'
- name: Set up NPROC
if: env.should_update_stage0 == 'yes'
run: |
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: nix run .#update-stage0-commit
run: cmake --preset release
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: make -j$NPROC -C build/release update-stage0-commit
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: git show --stat
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'

1
.gitignore vendored
View File

@@ -6,7 +6,6 @@
lake-manifest.json
/build
/src/lakefile.toml
/tests/lakefile.toml
/lakefile.toml
GPATH
GRTAGS

View File

@@ -50,7 +50,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- 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.
- `doc-gen4` has additional 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.
@@ -94,6 +94,8 @@ We'll use `v4.6.0` as the intended release version as a running example.
This checklist walks you through creating the first release candidate for a version of Lean.
For subsequent release candidates, the process is essentially the same, but we start out with the `releases/v4.7.0` branch already created.
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.
@@ -112,7 +114,7 @@ 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
git push --set-upstream origin releases/v4.7.0
```
- 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.

9
doc/std/README.md Normal file
View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

260
doc/std/naming.md Normal file
View 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:
![](naming-tree.svg)
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
View 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
View 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 dont 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).

9
script/bench.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
# We benchmark against stage 2 to test new optimizations.
timeout -s KILL 1h time bash -c 'mkdir -p build/release; cd build/release; cmake ../.. && make -j$(nproc) stage2' 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2

View File

@@ -50,5 +50,4 @@ echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -Wl,-Bstatic
# when not using the above flags, link GMP dynamically/as usual. Always link ICU dynamically.
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp $(pkg-config --libs libuv) -lucrtbase'"
# do not set `LEAN_CC` for tests
echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF"
echo -n " -DLEAN_TEST_VARS=''"

View File

@@ -53,6 +53,23 @@ def tag_exists(repo_url, tag_name, github_token):
matching_tags = response.json()
return any(tag["ref"] == f"refs/tags/{tag_name}" for tag in matching_tags)
def commit_hash_for_tag(repo_url, tag_name, github_token):
# 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)
if response.status_code != 200:
return False
# Check if any of the returned refs exactly match our tag
matching_tags = response.json()
matching_commits = [tag["object"]["sha"] for tag in matching_tags if tag["ref"] == f"refs/tags/{tag_name}"]
if len(matching_commits) != 1:
return None
else:
return matching_commits[0]
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}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -286,6 +303,14 @@ def main():
lean4_success = False
else:
print(f" ✅ Tag {toolchain} exists")
commit_hash = commit_hash_for_tag(lean_repo_url, toolchain, github_token)
SHORT_HASH_LENGTH = 7 # Lake abbreviates the Lean commit to 7 characters.
if commit_hash is None:
print(f" ❌ Could not resolve tag {toolchain} to a commit.")
lean4_success = False
elif commit_hash[0] == '0' and commit_hash[:SHORT_HASH_LENGTH].isnumeric():
print(f" ❌ Short commit hash {commit_hash[:SHORT_HASH_LENGTH]} is numeric and starts with 0, causing issues for version parsing. Try regenerating the last commit to get a new hash.")
lean4_success = False
if not release_page_exists(lean_repo_url, toolchain, github_token):
print(f" ❌ Release page for {toolchain} does not exist")

View File

@@ -94,6 +94,7 @@ def generate_script(repo, version, config):
"echo 'This repo has nightly-testing infrastructure'",
f"git merge origin/bump/{version.split('-rc')[0]}",
"echo 'Please resolve any conflicts.'",
"grep nightly-testing lakefile.* && echo 'Please ensure the lakefile does not include nightly-testing versions.'",
""
])
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 21)
set(LEAN_VERSION_MINOR 22)
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'")
@@ -58,9 +58,6 @@ option(USE_GITHASH "GIT_HASH" ON)
option(INSTALL_LICENSE "INSTALL_LICENSE" ON)
# When ON we install a copy of cadical
option(INSTALL_CADICAL "Install a copy of cadical" ON)
# When ON thread storage is automatically finalized, it assumes platform support pthreads.
# This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell).
option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON)
# FLAGS for disabling optimizations and debugging
option(FREE_VAR_RANGE_OPT "FREE_VAR_RANGE_OPT" ON)
@@ -182,10 +179,6 @@ else()
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_MULTI_THREAD")
endif()
if(AUTO_THREAD_FINALIZATION AND NOT MSVC)
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_AUTO_THREAD_FINALIZATION")
endif()
# Set Module Path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")

View File

@@ -37,6 +37,7 @@ import Init.Ext
import Init.Omega
import Init.MacroTrace
import Init.Grind
import Init.GrindInstances
import Init.While
import Init.Syntax
import Init.Internal

View File

@@ -45,7 +45,7 @@ theorem em (p : Prop) : p ¬p :=
| Or.inr h, _ => Or.inr h
| _, Or.inr h => Or.inr h
| Or.inl hut, Or.inl hvf =>
have hne : u v := by simp [hvf, hut, true_ne_false]
have hne : u v := by simp [hvf, hut]
Or.inl hne
have p_implies_uv : p u = v :=
fun hp =>
@@ -107,8 +107,8 @@ noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α → Prop) : α
theorem epsilon_spec_aux {α : Sort u} (h : Nonempty α) (p : α Prop) : ( y, p y) p (@epsilon α h p) :=
(strongIndefiniteDescription p h).property
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α (nonempty_of_exists hex) p) :=
epsilon_spec_aux (nonempty_of_exists hex) p hex
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α hex.nonempty p) :=
epsilon_spec_aux hex.nonempty p hex
theorem epsilon_singleton {α : Sort u} (x : α) : @epsilon α x (fun y => y = x) = x :=
@epsilon_spec α (fun y => y = x) x, rfl

View File

@@ -7,6 +7,7 @@ module
prelude
import Init.Prelude
meta import Init.Prelude
set_option linter.missingDocs true -- keep it documented
/-!

View File

@@ -49,7 +49,7 @@ 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 α) : α :=
@[expose] def ForInStep.value (x : ForInStep α) : α :=
match x with
| ForInStep.done b => b
| ForInStep.yield b => b

View File

@@ -136,7 +136,7 @@ may throw the corresponding exception.
This is the inverse of `ExceptT.run`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
/--
@@ -144,7 +144,7 @@ Use a monadic action that may throw an exception as an action that may return an
This is the inverse of `ExceptT.mk`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
namespace ExceptT
@@ -154,14 +154,14 @@ 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]
@[always_inline, inline, expose]
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]
@[always_inline, inline, expose]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a
| Except.error e => pure (Except.error e)
@@ -170,14 +170,14 @@ protected def bindCont {α β : Type u} (f : α → ExceptT ε m β) : Except ε
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
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]
@[always_inline, inline, expose]
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)
@@ -186,7 +186,7 @@ protected def map {α β : Type u} (f : α → β) (x : ExceptT ε m α) : Excep
/--
Runs a computation from an underlying monad in the transformed monad with exceptions.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t
@@ -197,7 +197,7 @@ instance : MonadLift m (ExceptT ε m) := ⟨ExceptT.lift⟩
/--
Handles exceptions produced in the `ExceptT ε` transformer.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with
| Except.ok a => pure (Except.ok a)

View File

@@ -25,7 +25,7 @@ namespace ExceptCpsT
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
@@ -43,7 +43,7 @@ Returns the value of a computation, forgetting whether it was an exception or a
This corresponds to early return.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure
@@ -63,7 +63,7 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
/--
Run an action from the transformed monad in the exception monad.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k

View File

@@ -9,3 +9,4 @@ prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.Instances
import Init.Control.Lawful.Lemmas
import Init.Control.Lawful.MonadLift

View File

@@ -6,6 +6,7 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
module
prelude
import Init.Ext
import Init.SimpLemmas
import Init.Meta
@@ -49,7 +50,7 @@ attribute [simp] id_map
(comp_map _ _ _).symm
theorem Functor.map_unit [Functor f] [LawfulFunctor f] {a : f PUnit} : (fun _ => PUnit.unit) <$> a = a := by
simp [map]
simp
/--
An applicative functor satisfies the laws of an applicative functor.
@@ -147,7 +148,7 @@ attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x
change x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map]
/--
@@ -241,13 +242,23 @@ theorem LawfulMonad.mk' (m : Type u → Type v) [Monad m]
namespace Id
@[simp] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[simp] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
@[ext] theorem ext {x y : Id α} (h : x.run = y.run) : x = y := h
instance : LawfulMonad Id := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
@[simp] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl
-- These lemmas are bad as they abuse the defeq of `Id α` and `α`
@[deprecated run_map (since := "2025-03-05")] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[deprecated run_bind (since := "2025-03-05")] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[deprecated run_pure (since := "2025-03-05")] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
end Id
/-! # Option -/

View File

@@ -58,7 +58,7 @@ protected theorem bind_pure_comp [Monad m] (f : α → β) (x : ExceptT ε m α)
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
show (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
change (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -67,10 +67,10 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
| Except.error _ => simp
| Except.ok _ =>
simp [bind_pure_comp]; apply bind_congr; intro b;
cases b <;> simp [comp, Except.map, const]
cases b <;> simp [Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
show (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
change (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -206,15 +206,15 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
(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
show (f >>= fun g => g <$> x).run s = _
change (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
change (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
change (x >>= fun a => y >>= fun _ => pure a).run s = _
simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by

View File

@@ -0,0 +1,11 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Control.Lawful.MonadLift.Basic
import Init.Control.Lawful.MonadLift.Lemmas
import Init.Control.Lawful.MonadLift.Instances

View File

@@ -0,0 +1,52 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao
-/
module
prelude
import Init.Control.Basic
/-!
# LawfulMonadLift and LawfulMonadLiftT
This module provides classes asserting that `MonadLift` and `MonadLiftT` are lawful, which means
that `monadLift` is compatible with `pure` and `bind`.
-/
section MonadLift
/-- The `MonadLift` typeclass only contains the lifting operation. `LawfulMonadLift` further
asserts that lifting commutes with `pure` and `bind`:
```
monadLift (pure a) = pure a
monadLift (ma >>= f) = monadLift ma >>= monadLift ∘ f
```
-/
class LawfulMonadLift (m : semiOutParam (Type u Type v)) (n : Type u Type w)
[Monad m] [Monad n] [inst : MonadLift m n] : Prop where
/-- Lifting preserves `pure` -/
monadLift_pure {α : Type u} (a : α) : inst.monadLift (pure a) = pure a
/-- Lifting preserves `bind` -/
monadLift_bind {α β : Type u} (ma : m α) (f : α m β) :
inst.monadLift (ma >>= f) = inst.monadLift ma >>= (fun x => inst.monadLift (f x))
/-- The `MonadLiftT` typeclass only contains the transitive lifting operation.
`LawfulMonadLiftT` further asserts that lifting commutes with `pure` and `bind`:
```
monadLift (pure a) = pure a
monadLift (ma >>= f) = monadLift ma >>= monadLift ∘ f
```
-/
class LawfulMonadLiftT (m : Type u Type v) (n : Type u Type w) [Monad m] [Monad n]
[inst : MonadLiftT m n] : Prop where
/-- Lifting preserves `pure` -/
monadLift_pure {α : Type u} (a : α) : inst.monadLift (pure a) = pure a
/-- Lifting preserves `bind` -/
monadLift_bind {α β : Type u} (ma : m α) (f : α m β) :
inst.monadLift (ma >>= f) = monadLift ma >>= (fun x => monadLift (f x))
export LawfulMonadLiftT (monadLift_pure monadLift_bind)
end MonadLift

View File

@@ -0,0 +1,137 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao, Paul Reichert
-/
module
prelude
import all Init.Control.Option
import all Init.Control.Except
import all Init.Control.ExceptCps
import all Init.Control.StateRef
import all Init.Control.StateCps
import Init.Control.Lawful.MonadLift.Lemmas
import Init.Control.Lawful.Instances
universe u v w x
variable {m : Type u Type v} {n : Type u Type w} {o : Type u Type x}
variable (m n o) in
instance [Monad m] [Monad n] [Monad o] [MonadLift n o] [MonadLiftT m n]
[LawfulMonadLift n o] [LawfulMonadLiftT m n] : LawfulMonadLiftT m o where
monadLift_pure := fun a => by
simp only [monadLift, LawfulMonadLift.monadLift_pure, liftM_pure]
monadLift_bind := fun ma f => by
simp only [monadLift, LawfulMonadLift.monadLift_bind, liftM_bind]
variable (m) in
instance [Monad m] : LawfulMonadLiftT m m where
monadLift_pure _ := rfl
monadLift_bind _ _ := rfl
namespace StateT
variable [Monad m] [LawfulMonad m]
instance {σ : Type u} : LawfulMonadLift m (StateT σ m) where
monadLift_pure _ := by ext; simp [MonadLift.monadLift]
monadLift_bind _ _ := by ext; simp [MonadLift.monadLift]
end StateT
namespace ReaderT
variable [Monad m]
instance {ρ : Type u} : LawfulMonadLift m (ReaderT ρ m) where
monadLift_pure _ := rfl
monadLift_bind _ _ := rfl
end ReaderT
namespace OptionT
variable [Monad m] [LawfulMonad m]
@[simp]
theorem lift_pure {α : Type u} (a : α) : OptionT.lift (pure a : m α) = pure a := by
simp only [OptionT.lift, OptionT.mk, bind_pure_comp, map_pure, pure, OptionT.pure]
@[simp]
theorem lift_bind {α β : Type u} (ma : m α) (f : α m β) :
OptionT.lift (ma >>= f) = OptionT.lift ma >>= (fun a => OptionT.lift (f a)) := by
simp only [instMonad, OptionT.bind, OptionT.mk, OptionT.lift, bind_pure_comp, bind_map_left,
map_bind]
instance : LawfulMonadLift m (OptionT m) where
monadLift_pure := lift_pure
monadLift_bind := lift_bind
end OptionT
namespace ExceptT
variable [Monad m] [LawfulMonad m]
@[simp]
theorem lift_bind {α β ε : Type u} (ma : m α) (f : α m β) :
ExceptT.lift (ε := ε) (ma >>= f) = ExceptT.lift ma >>= (fun a => ExceptT.lift (f a)) := by
simp only [instMonad, ExceptT.bind, mk, ExceptT.lift, bind_map_left, ExceptT.bindCont, map_bind]
instance : LawfulMonadLift m (ExceptT ε m) where
monadLift_pure := lift_pure
monadLift_bind := lift_bind
instance : LawfulMonadLift (Except ε) (ExceptT ε m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, mk, pure, Except.pure, ExceptT.pure]
monadLift_bind ma _ := by
simp only [instMonad, ExceptT.bind, mk, MonadLift.monadLift, pure_bind, ExceptT.bindCont,
Except.instMonad, Except.bind]
rcases ma with _ | _ <;> simp
end ExceptT
namespace StateRefT'
instance {ω σ : Type} {m : Type Type} [Monad m] : LawfulMonadLift m (StateRefT' ω σ m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold StateRefT'.lift ReaderT.pure
simp only
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold StateRefT'.lift ReaderT.bind
simp only
end StateRefT'
namespace StateCpsT
instance {σ : Type u} [Monad m] [LawfulMonad m] : LawfulMonadLift m (StateCpsT σ m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold StateCpsT.lift
simp only [pure_bind]
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold StateCpsT.lift
simp only [bind_assoc]
end StateCpsT
namespace ExceptCpsT
instance {ε : Type u} [Monad m] [LawfulMonad m] : LawfulMonadLift m (ExceptCpsT ε m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold ExceptCpsT.lift
simp only [pure_bind]
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold ExceptCpsT.lift
simp only [bind_assoc]
end ExceptCpsT

View File

@@ -0,0 +1,63 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.MonadLift.Basic
universe u v w
variable {m : Type u Type v} {n : Type u Type w} [Monad m] [Monad n] [MonadLiftT m n]
[LawfulMonadLiftT m n] {α β : Type u}
theorem monadLift_map [LawfulMonad m] [LawfulMonad n] (f : α β) (ma : m α) :
monadLift (f <$> ma) = f <$> (monadLift ma : n α) := by
rw [ bind_pure_comp, bind_pure_comp, monadLift_bind]
simp only [bind_pure_comp, monadLift_pure]
theorem monadLift_seq [LawfulMonad m] [LawfulMonad n] (mf : m (α β)) (ma : m α) :
monadLift (mf <*> ma) = monadLift mf <*> (monadLift ma : n α) := by
simp only [seq_eq_bind, monadLift_map, monadLift_bind]
theorem monadLift_seqLeft [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
monadLift (x <* y) = (monadLift x : n α) <* (monadLift y : n β) := by
simp only [seqLeft_eq, monadLift_map, monadLift_seq]
theorem monadLift_seqRight [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
monadLift (x *> y) = (monadLift x : n α) *> (monadLift y : n β) := by
simp only [seqRight_eq, monadLift_map, monadLift_seq]
/-! We duplicate the theorems for `monadLift` to `liftM` since `rw` matches on syntax only. -/
@[simp]
theorem liftM_pure (a : α) : liftM (pure a : m α) = pure (f := n) a :=
monadLift_pure _
@[simp]
theorem liftM_bind (ma : m α) (f : α m β) :
liftM (n := n) (ma >>= f) = liftM ma >>= (fun a => liftM (f a)) :=
monadLift_bind _ _
@[simp]
theorem liftM_map [LawfulMonad m] [LawfulMonad n] (f : α β) (ma : m α) :
liftM (f <$> ma) = f <$> (liftM ma : n α) :=
monadLift_map _ _
@[simp]
theorem liftM_seq [LawfulMonad m] [LawfulMonad n] (mf : m (α β)) (ma : m α) :
liftM (mf <*> ma) = liftM mf <*> (liftM ma : n α) :=
monadLift_seq _ _
@[simp]
theorem liftM_seqLeft [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
liftM (x <* y) = (liftM x : n α) <* (liftM y : n β) :=
monadLift_seqLeft _ _
@[simp]
theorem liftM_seqRight [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
liftM (x *> y) = (liftM x : n α) *> (liftM y : n β) :=
monadLift_seqRight _ _

View File

@@ -29,7 +29,7 @@ of a value and a state.
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]
@[always_inline, inline, expose]
def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
x s
@@ -37,7 +37,7 @@ def StateT.run {σ : Type u} {m : Type u → Type v} {α : Type u} (x : StateT
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]
@[always_inline, inline, expose]
def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
(·.1) <$> x s
@@ -66,21 +66,21 @@ variable [Monad m] {α β : Type u}
/--
Returns the given value without modifying the state. Typically used via `Pure.pure`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def pure (a : α) : StateT σ m α :=
fun s => pure (a, s)
/--
Sequences two actions. Typically used via the `>>=` operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
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]
@[always_inline, inline, expose]
protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
fun s => do let (a, s) x s; pure (f a, s)
@@ -114,14 +114,14 @@ 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]
@[always_inline, inline, expose]
protected def get : StateT σ m σ :=
fun s => pure (s, s)
/--
Replaces the mutable state with a new value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def set : σ StateT σ m PUnit :=
fun s' _ => pure (, s')
@@ -133,7 +133,7 @@ It is equivalent to `do let (a, s) := f (← StateT.get); StateT.set s; pure a`.
`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]
@[always_inline, inline, expose]
protected def modifyGet (f : σ α × σ) : StateT σ m α :=
fun s => pure (f s)
@@ -143,7 +143,7 @@ Runs an action from the underlying monad in the monad with state. The state is n
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
fun s => do let a t; pure (a, s)

View File

@@ -28,7 +28,7 @@ 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]
@[always_inline, inline, expose]
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
@@ -39,7 +39,7 @@ 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]
@[always_inline, inline, expose]
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
@@ -47,7 +47,7 @@ def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
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]
@[always_inline, inline, expose]
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@@ -72,7 +72,7 @@ Runs an action from the underlying monad in the monad with state. The state is n
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
fun _ s k => x >>= (k . s)

View File

@@ -9,7 +9,7 @@ module
prelude
import Init.Tactics
import Init.Meta
meta import Init.Meta
namespace Lean.Parser.Tactic.Conv

View File

@@ -8,7 +8,7 @@ notation, basic datatypes and type classes
module
prelude
import Init.Prelude
meta import Init.Prelude
import Init.SizeOf
set_option linter.missingDocs true -- keep it documented
@@ -43,14 +43,14 @@ and `flip (·<·)` is the greater-than relation.
theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl
@[simp] theorem Function.const_comp {f : α β} {c : γ} :
(Function.const β c f) = Function.const α c := by
(Function.const β c f) = Function.const α c :=
rfl
@[simp] theorem Function.comp_const {f : β γ} {b : β} :
(f Function.const α b) = Function.const α (f b) := by
(f Function.const α b) = Function.const α (f b) :=
rfl
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true := by
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true :=
rfl
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false :=
rfl
@[simp] theorem Function.comp_id (f : α β) : f id = f := rfl
@@ -95,7 +95,8 @@ structure Thunk (α : Type u) : Type u where
-/
mk ::
/-- Extract the getter function out of a thunk. Use `Thunk.get` instead. -/
private fn : Unit α
-- The field is public so as to allow computation through it.
fn : Unit α
attribute [extern "lean_mk_thunk"] Thunk.mk
@@ -117,6 +118,10 @@ Computed values are cached, so the value is not recomputed.
@[extern "lean_thunk_get_own"] protected def Thunk.get (x : @& Thunk α) : α :=
x.fn ()
-- Ensure `Thunk.fn` is still computable even if it shouldn't be accessed directly.
@[inline] private def Thunk.fnImpl (x : Thunk α) : Unit α := fun _ => x.get
@[csimp] private theorem Thunk.fn_eq_fnImpl : @Thunk.fn = @Thunk.fnImpl := rfl
/--
Constructs a new thunk that forces `x` and then applies `x` to the result. Upon forcing, the result
of `f` is cached and the reference to the thunk `x` is dropped.
@@ -897,43 +902,43 @@ section
variable {α β φ : Sort u} {a a' : α} {b b' : β} {c : φ}
/-- Non-dependent recursor for `HEq` -/
noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : HEq a b) : motive b :=
noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : a b) : motive b :=
h.rec m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : HEq a b) (m : motive a) : motive b :=
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b :=
h.rec m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : HEq a b) (h₂ : p a) : p b :=
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b :=
eq_of_heq h₁ h₂
/-- Substitution with heterogeneous equality. -/
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂
/-- Heterogeneous equality is symmetric. -/
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
@[symm] theorem HEq.symm (h : a b) : b a :=
h.rec (HEq.refl a)
/-- Propositionally equal terms are also heterogeneously equal. -/
theorem heq_of_eq (h : a = a') : HEq a a' :=
theorem heq_of_eq (h : a = a') : a a' :=
Eq.subst h (HEq.refl a)
/-- Heterogeneous equality is transitive. -/
theorem HEq.trans (h₁ : HEq a b) (h₂ : HEq b c) : HEq a c :=
theorem HEq.trans (h₁ : a b) (h₂ : b c) : a c :=
HEq.subst h₂ h₁
/-- Heterogeneous equality precomposes with propositional equality. -/
theorem heq_of_heq_of_eq (h₁ : HEq a b) (h₂ : b = b') : HEq a b' :=
theorem heq_of_heq_of_eq (h₁ : a b) (h₂ : b = b') : a b' :=
HEq.trans h₁ (heq_of_eq h₂)
/-- Heterogeneous equality postcomposes with propositional equality. -/
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : HEq a' b) : HEq a b :=
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : a' b) : a b :=
HEq.trans (heq_of_eq h₁) h₂
/-- If two terms are heterogeneously equal then their types are propositionally equal. -/
theorem type_eq_of_heq (h : HEq a b) : α = β :=
theorem type_eq_of_heq (h : a b) : α = β :=
h.rec (Eq.refl α)
end
@@ -942,7 +947,7 @@ end
Rewriting inside `φ` using `Eq.recOn` yields a term that's heterogeneously equal to the original
term.
-/
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) Eq.recOn (motive := fun x _ => φ x) h p p
| rfl, p => HEq.refl p
/--
@@ -950,8 +955,8 @@ Heterogeneous equality with an `Eq.rec` application on the left is equivalent to
equality on the original term.
-/
theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq (@Eq.rec α a motive refl b h) c HEq refl c :=
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h}
: @Eq.rec α a motive refl b h c refl c :=
h.rec (fun _ => id, id) c
/--
@@ -960,7 +965,7 @@ equality on the original term.
-/
theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq c (@Eq.rec α a motive refl b h) HEq c refl :=
c @Eq.rec α a motive refl b h c refl :=
h.rec (fun _ => id, id) c
/--
@@ -977,7 +982,7 @@ theorem apply_eqRec {α : Sort u} {a : α} (motive : (b : α) → a = b → Sort
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
terms are heterogeneously equal.
-/
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : HEq a b := by
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : a b := by
subst h₁
apply heq_of_eq
exact h₂
@@ -985,7 +990,7 @@ theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h
/--
The result of casting a term with `cast` is heterogeneously equal to the original term.
-/
theorem cast_heq {α β : Sort u} : (h : α = β) (a : α) HEq (cast h a) a
theorem cast_heq {α β : Sort u} : (h : α = β) (a : α) cast h a a
| rfl, a => HEq.refl a
variable {a b c d : Prop}
@@ -1014,8 +1019,8 @@ instance : Trans Iff Iff Iff where
theorem Eq.comm {a b : α} : a = b b = a := Iff.intro Eq.symm Eq.symm
theorem eq_comm {a b : α} : a = b b = a := Eq.comm
theorem HEq.comm {a : α} {b : β} : HEq a b HEq b a := Iff.intro HEq.symm HEq.symm
theorem heq_comm {a : α} {b : β} : HEq a b HEq b a := HEq.comm
theorem HEq.comm {a : α} {b : β} : a b b a := Iff.intro HEq.symm HEq.symm
theorem heq_comm {a : α} {b : β} : a b b a := HEq.comm
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm : (a b) (b a) := Iff.intro Iff.symm Iff.symm
@@ -1048,11 +1053,6 @@ theorem Exists.elim {α : Sort u} {p : α → Prop} {b : Prop}
| isFalse _ => rfl
| isTrue h => False.elim h
set_option linter.missingDocs false in
@[deprecated decide_true (since := "2024-11-05")] abbrev decide_true_eq_true := decide_true
set_option linter.missingDocs false in
@[deprecated decide_false (since := "2024-11-05")] abbrev decide_false_eq_false := decide_false
/-- Similar to `decide`, but uses an explicit instance -/
@[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool :=
decide (h := d)
@@ -1212,10 +1212,7 @@ abbrev noConfusionEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f :
instance : Inhabited Prop where
default := True
deriving instance Inhabited for NonScalar, PNonScalar, True, ForInStep
theorem nonempty_of_exists {α : Sort u} {p : α Prop} : Exists (fun x => p x) Nonempty α
| w, _ => w
deriving instance Inhabited for NonScalar, PNonScalar, True
/-! # Subsingleton -/
@@ -1242,7 +1239,7 @@ protected theorem Subsingleton.elim {α : Sort u} [h : Subsingleton α] : (a b :
If two types are equal and one of them is a subsingleton, then all of their elements are
[heterogeneously equal](lean-manual://section/HEq).
-/
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : HEq a b := by
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : a b := by
subst h₂
apply heq_of_eq
apply Subsingleton.elim
@@ -1389,16 +1386,7 @@ instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
Nonempty.elim h (fun b => Sum.inr b)
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
match a, b with
| Sum.inl a, Sum.inl b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr a, Sum.inr b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr _, Sum.inl _ => isFalse fun h => Sum.noConfusion h
| Sum.inl _, Sum.inr _ => isFalse fun h => Sum.noConfusion h
deriving instance DecidableEq for Sum
end
@@ -1702,7 +1690,7 @@ theorem true_iff_false : (True ↔ False) ↔ False := iff_false_intro (·.mp T
theorem false_iff_true : (False True) False := iff_false_intro (·.mpr True.intro)
theorem iff_not_self : ¬(a ¬a) | H => let f h := H.1 h h; f (H.2 f)
theorem heq_self_iff_true (a : α) : HEq a a True := iff_true_intro HEq.rfl
theorem heq_self_iff_true (a : α) : a a True := iff_true_intro HEq.rfl
/-! ## implies -/
@@ -1902,7 +1890,7 @@ a structure.
protected abbrev hrecOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(c : (a b : α) (p : r a b) HEq (f a) (f b))
(c : (a b : α) (p : r a b) f a f b)
: motive q :=
Quot.recOn q f fun a b p => eq_of_heq (eqRec_heq_iff.mpr (c a b p))
@@ -2100,7 +2088,7 @@ a structure.
protected abbrev hrecOn
(q : Quotient s)
(f : (a : α) motive (Quotient.mk s a))
(c : (a b : α) (p : a b) HEq (f a) (f b))
(c : (a b : α) (p : a b) f a f b)
: motive q :=
Quot.hrecOn q f c
end
@@ -2264,7 +2252,7 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
Quot.liftOn f
(fun (f : (x : α), β x) => f x)
(fun _ _ h => h x)
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
change extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h)
/--

View File

@@ -46,3 +46,4 @@ import Init.Data.NeZero
import Init.Data.Function
import Init.Data.RArray
import Init.Data.Vector
import Init.Data.Iterators

View File

@@ -209,7 +209,7 @@ theorem Context.evalList_sort_congr
induction c generalizing a b with
| nil => simp [sort.loop, h₂]
| cons c _ ih =>
simp [sort.loop]; apply ih; simp [evalList_insert ctx h, evalList]
simp [sort.loop]; apply ih; simp [evalList_insert ctx h]
cases a with
| nil => apply absurd h₃; simp
| cons a as =>
@@ -282,7 +282,7 @@ theorem Context.toList_nonEmpty (e : Expr) : e.toList ≠ [] := by
simp [Expr.toList]
cases h : l.toList with
| nil => contradiction
| cons => simp [List.append]
| cons => simp
theorem Context.unwrap_isNeutral
{ctx : Context α}
@@ -328,13 +328,13 @@ theorem Context.eval_toList (ctx : Context α) (e : Expr) : evalList α ctx e.to
induction e with
| var x => rfl
| op l r ih₁ ih₂ =>
simp [evalList, Expr.toList, eval, ih₁, ih₂]
simp [Expr.toList, eval, ih₁, ih₂]
apply evalList_append <;> apply toList_nonEmpty
theorem Context.eval_norm (ctx : Context α) (e : Expr) : evalList α ctx (norm ctx e) = eval α ctx e := by
simp [norm]
cases h₁ : ContextInformation.isIdem ctx <;> cases h₂ : ContextInformation.isComm ctx <;>
simp_all [evalList_removeNeutrals, eval_toList, toList_nonEmpty, evalList_mergeIdem, evalList_sort]
simp_all [evalList_removeNeutrals, eval_toList, evalList_mergeIdem, evalList_sort]
theorem Context.eq_of_norm (ctx : Context α) (a b : Expr) (h : norm ctx a == norm ctx b) : eval α ctx a = eval α ctx b := by
have h := congrArg (evalList α ctx) (eq_of_beq h)

View File

@@ -22,7 +22,7 @@ an array `xs : Array α`, given a proof that every element of `xs` in fact satis
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
-/
@[expose]
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
@@ -39,7 +39,7 @@ of elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith
@[implemented_by attachWithImpl, expose] 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)
@@ -54,7 +54,7 @@ recursion](lean-manual://section/well-founded-recursion) that use higher-order f
`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
@[inline, expose] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[simp, grind =] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
@@ -68,15 +68,15 @@ well-founded recursion mechanism to prove that the function terminates.
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
simp [pmap]
@[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, grind =] 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_iff] using H) := by
simp [attachWith]
@[simp] theorem toList_attach {xs : Array α} :
xs.attach.toList = xs.toList.attachWith (· xs) (by simp [mem_toList]) := by
@[simp, grind =] theorem toList_attach {xs : Array α} :
xs.attach.toList = xs.toList.attachWith (· xs) (by simp [mem_toList_iff]) := by
simp [attach]
@[simp] theorem toList_pmap {xs : Array α} {P : α Prop} {f : a, P a β} {H : a xs, P a} :
@[simp, grind =] 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]
@@ -92,16 +92,16 @@ well-founded recursion mechanism to prove that the function terminates.
intro a m h₁ h₂
congr
@[simp] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp, grind =] 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 : α) (xs : Array α) (h : b xs.push a, P b) :
@[simp, grind =] 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
@[simp, grind =] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp, grind =] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
l.attachWith (fun x => x l.toArray) (fun x h => by simpa using h) =
@@ -122,11 +122,13 @@ theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a,
simp only [List.pmap_toArray, mk.injEq]
rw [List.pmap_congr_left _ h]
@[grind =]
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]
@[grind =]
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
@@ -142,18 +144,18 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
subst w
simp
@[simp] theorem attach_push {a : α} {xs : Array α} :
@[simp, grind =] 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 : α} {xs : Array α} {P : α Prop} {H : x xs.push a, P x} :
@[simp, grind =] 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 _ _)]
simp
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
@@ -189,38 +191,39 @@ theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a
(xs.attachWith p H).map Subtype.val = xs := by
cases xs; simp
@[simp]
@[simp, grind]
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]
@[simp, grind]
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]
@[simp, grind =]
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]
@[grind]
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]
@[simp, grind =]
theorem size_pmap {p : α Prop} {f : a, p a β} {xs H} : (pmap f xs H).size = xs.size := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem size_attach {xs : Array α} : xs.attach.size = xs.size := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem size_attachWith {p : α Prop} {xs : Array α} {H} : (xs.attachWith p H).size = xs.size := by
cases xs; simp
@@ -252,13 +255,13 @@ theorem attachWith_ne_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈
xs.attachWith P H #[] xs #[] := by
cases xs; simp
@[simp]
@[simp, grind =]
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]
@[simp, grind =]
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] =
@@ -266,57 +269,59 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {xs : Array α} (
(h _ (getElem_mem (@size_pmap _ _ p f xs h hi))) := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem getElem?_attachWith {xs : Array α} {i : Nat} {P : α Prop} {H : a xs, P a} :
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
getElem?_pmap ..
@[simp]
@[simp, grind =]
theorem getElem?_attach {xs : Array α} {i : Nat} :
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => mem_of_getElem? a) :=
getElem?_attachWith
@[simp]
@[simp, grind =]
theorem getElem_attachWith {xs : Array α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).size) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap _ _ h
@[simp]
@[simp, grind =]
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
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x xs} Prop} {f : a, p a β} (H) :
@[simp, grind =] 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₂) :
@[simp, grind =] 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
@[grind =]
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]
@[grind =]
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
@[simp, grind =] 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
@[simp, grind =] 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
@@ -337,7 +342,7 @@ 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]
List.foldl_toArray', mem_toArray, List.foldl_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@@ -356,23 +361,25 @@ 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]
List.foldr_toArray', mem_toArray, List.foldr_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@[grind =]
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
@[grind =]
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 xs
simp [List.attachWith_map]
@[simp] theorem map_attachWith {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
@[simp, grind =] 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
@@ -393,6 +400,7 @@ theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
@[grind =]
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
@@ -400,6 +408,7 @@ theorem attach_filterMap {xs : Array α} {f : α → Option β} :
rw [attach_congr List.filterMap_toArray]
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
@[grind =]
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
@@ -409,7 +418,7 @@ theorem attach_filter {xs : Array α} (p : α → Bool) :
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
@[simp]
@[simp, grind =]
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
@@ -417,7 +426,7 @@ theorem filterMap_attachWith {q : α → Prop} {xs : Array α} {f : {x // q x}
cases xs
simp [Function.comp_def]
@[simp]
@[simp, grind =]
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 =
@@ -426,6 +435,7 @@ theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} →
cases xs
simp [Function.comp_def, List.filter_map]
@[grind =]
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
@@ -433,7 +443,7 @@ theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f
cases xs
simp [List.pmap_pmap, List.pmap_map]
@[simp] theorem pmap_append {p : ι Prop} {f : a : ι, p a α} {xs ys : Array ι}
@[simp, grind =] 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)) ++
@@ -448,7 +458,7 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
xs.pmap f h₁ ++ ys.pmap f h₂ :=
pmap_append _
@[simp] theorem attach_append {xs ys : Array α} :
@[simp, grind =] 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
@@ -456,59 +466,62 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
rw [attach_congr (List.append_toArray _ _)]
simp [List.attach_append, Function.comp_def]
@[simp] theorem attachWith_append {P : α Prop} {xs ys : Array α}
@[simp, grind =] theorem attachWith_append {P : α Prop} {xs ys : Array α}
{H : (a : α), a xs ++ ys P a} :
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
simp [attachWith, attach_append, map_pmap, pmap_append]
simp [attachWith]
@[simp] theorem pmap_reverse {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp, grind =] 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
@[grind =]
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]
@[simp] theorem attachWith_reverse {P : α Prop} {xs : Array α}
@[simp, grind =] theorem attachWith_reverse {P : α Prop} {xs : Array α}
{H : (a : α), a xs.reverse P a} :
xs.reverse.attachWith P H =
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
cases xs
simp
@[grind =]
theorem reverse_attachWith {P : α Prop} {xs : Array α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
cases xs
simp
@[simp] theorem attach_reverse {xs : Array α} :
@[simp, grind =] 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]
simp
@[grind =]
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, grind =] 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
simp
@[simp] theorem back?_attachWith {P : α Prop} {xs : Array α}
@[simp, grind =] 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? h)) := by
cases xs
simp
@[simp]
@[simp, grind =]
theorem back?_attach {xs : Array α} :
xs.attach.back? = xs.back?.pbind fun a h => some a, mem_of_back? h := by
cases xs
@@ -526,7 +539,7 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H
cases xs
simp
@[simp]
@[simp, grind =]
theorem count_attach [BEq α] {xs : Array α} {a : {x // x xs}} :
xs.attach.count a = xs.count a := by
rcases xs with xs
@@ -535,13 +548,13 @@ theorem count_attach [BEq α] {xs : Array α} {a : {x // x ∈ xs}} :
simp only [Subtype.beq_iff]
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@[simp]
@[simp, grind =]
theorem count_attachWith [BEq α] {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₁) :
@[simp, grind =] 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]
@@ -574,9 +587,12 @@ state, the right approach is usually the tactic `simp [Array.unattach, -Array.ma
-/
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 = #[] := by
@[simp] theorem unattach_empty {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := by
simp [unattach]
@[deprecated unattach_empty (since := "2025-05-26")]
abbrev unattach_nil := @unattach_empty
@[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]
@@ -687,7 +703,7 @@ and simplifies these to the function directly taking the value.
{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,
simp only [List.flatMap_toArray, List.unattach_toArray,
mk.injEq]
rw [List.flatMap_subtype]
simp [hf]

View File

@@ -91,7 +91,8 @@ theorem ext' {xs ys : Array α} (h : xs.toList = ys.toList) : xs = ys := by
@[simp, grind =] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
@[simp, grind =] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
simp [getElem?_def]
simp only [getElem?_def, getElem_toList]
simp only [Array.size]
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
@@ -112,6 +113,10 @@ theorem mem_def {a : α} {as : Array α} : a ∈ as ↔ a ∈ as.toList :=
rw [Array.mem_def, getElem_toList]
apply List.getElem_mem
@[simp, grind =] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
@[simp] theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
end Array
namespace List
@@ -163,7 +168,7 @@ Low-level indexing operator which is as fast as a C array read.
This avoids overhead due to unboxing a `Nat` used as an index.
-/
@[extern "lean_array_uget", simp]
@[extern "lean_array_uget", simp, expose]
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
a[i.toNat]
@@ -186,7 +191,7 @@ Examples:
* `#["orange", "yellow"].pop = #["orange"]`
* `(#[] : Array String).pop = #[]`
-/
@[extern "lean_array_pop"]
@[extern "lean_array_pop", expose]
def pop (xs : Array α) : Array α where
toList := xs.toList.dropLast
@@ -205,7 +210,7 @@ Examples:
* `Array.replicate 3 () = #[(), (), ()]`
* `Array.replicate 0 "anything" = #[]`
-/
@[extern "lean_mk_array"]
@[extern "lean_mk_array", expose]
def replicate {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
@@ -233,7 +238,7 @@ Examples:
* `#["red", "green", "blue", "brown"].swap 1 2 = #["red", "blue", "green", "brown"]`
* `#["red", "green", "blue", "brown"].swap 3 0 = #["brown", "green", "blue", "red"]`
-/
@[extern "lean_array_fswap"]
@[extern "lean_array_fswap", expose]
def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic) (hj : j < xs.size := by get_elem_tactic) : Array α :=
let v₁ := xs[i]
let v₂ := xs[j]
@@ -241,7 +246,7 @@ def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic)
xs'.set j v₁ (Nat.lt_of_lt_of_eq hj (size_set _).symm)
@[simp] theorem size_swap {xs : Array α} {i j : Nat} {hi hj} : (xs.swap i j hi hj).size = xs.size := by
show ((xs.set i xs[j]).set j xs[i]
change ((xs.set i xs[j]).set j xs[i]
(Nat.lt_of_lt_of_eq hj (size_set _).symm)).size = xs.size
rw [size_set, size_set]
@@ -263,8 +268,6 @@ def swapIfInBounds (xs : Array α) (i j : @& Nat) : Array α :=
else xs
else xs
@[deprecated swapIfInBounds (since := "2024-11-24")] abbrev swap! := @swapIfInBounds
/-! ### GetElem instance for `USize`, backed by `uget` -/
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
@@ -286,6 +289,7 @@ Examples:
* `#[1, 2].isEmpty = false`
* `#[()].isEmpty = false`
-/
@[expose]
def isEmpty (xs : Array α) : Bool :=
xs.size = 0
@@ -327,12 +331,16 @@ Examples:
* `Array.ofFn (n := 3) toString = #["0", "1", "2"]`
* `Array.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = #["red", "green", "blue"]`
-/
def ofFn {n} (f : Fin n α) : Array α := go 0 (emptyWithCapacity n) where
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (acc : Array α) : Array α :=
if h : i < n then go (i+1) (acc.push (f i, h)) else acc
decreasing_by simp_wf; decreasing_trivial_pre_omega
def ofFn {n} (f : Fin n α) : Array α := go (emptyWithCapacity n) n (Nat.le_refl n) where
/-- Auxiliary for `ofFn`. `ofFn.go f acc i h = acc ++ #[f (n - i), ..., f(n - 1)]` -/
go (acc : Array α) : (i : Nat) i n Array α
| i + 1, h =>
have w : n - i - 1 < n :=
Nat.lt_of_lt_of_le (Nat.sub_one_lt (Nat.sub_ne_zero_iff_lt.mpr h)) (Nat.sub_le n i)
go (acc.push (f n - i - 1, w)) i (Nat.le_of_succ_le h)
| 0, _ => acc
-- See also `Array.ofFnM` defined in `Init.Data.Array.OfFn`.
/--
Constructs an array that contains all the numbers from `0` to `n`, exclusive.
@@ -367,7 +375,7 @@ Examples:
* `Array.singleton 5 = #[5]`
* `Array.singleton "one" = #["one"]`
-/
@[inline] protected def singleton (v : α) : Array α := #[v]
@[inline, expose] protected def singleton (v : α) : Array α := #[v]
/--
Returns the last element of an array, or panics if the array is empty.
@@ -396,7 +404,7 @@ that requires a proof the array is non-empty.
def back? (xs : Array α) : Option α :=
xs[xs.size - 1]?
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), expose]
def get? (xs : Array α) (i : Nat) : Option α :=
if h : i < xs.size then some xs[i] else none
@@ -410,7 +418,7 @@ Examples:
* `#["spinach", "broccoli", "carrot"].swapAt 1 "pepper" = ("broccoli", #["spinach", "pepper", "carrot"])`
* `#["spinach", "broccoli", "carrot"].swapAt 2 "pepper" = ("carrot", #["spinach", "broccoli", "pepper"])`
-/
@[inline] def swapAt (xs : Array α) (i : Nat) (v : α) (hi : i < xs.size := by get_elem_tactic) : α × Array α :=
@[inline, expose] def swapAt (xs : Array α) (i : Nat) (v : α) (hi : i < xs.size := by get_elem_tactic) : α × Array α :=
let e := xs[i]
let xs' := xs.set i v
(e, xs')
@@ -425,7 +433,7 @@ Examples:
* `#["spinach", "broccoli", "carrot"].swapAt! 1 "pepper" = (#["spinach", "pepper", "carrot"], "broccoli")`
* `#["spinach", "broccoli", "carrot"].swapAt! 2 "pepper" = (#["spinach", "broccoli", "pepper"], "carrot")`
-/
@[inline]
@[inline, expose]
def swapAt! (xs : Array α) (i : Nat) (v : α) : α × Array α :=
if h : i < xs.size then
swapAt xs i v
@@ -538,7 +546,7 @@ Examples:
-/
@[inline]
def modify (xs : Array α) (i : Nat) (f : α α) : Array α :=
Id.run <| modifyM xs i f
Id.run <| modifyM xs i (pure <| f ·)
set_option linter.indexVariables false in -- Changing `idx` causes bootstrapping issues, haven't investigated.
/--
@@ -571,7 +579,7 @@ def modifyOp (xs : Array α) (idx : Nat) (f : αα) : Array α :=
loop 0 b
/-- Reference implementation for `forIn'` -/
@[implemented_by Array.forIn'Unsafe]
@[implemented_by Array.forIn'Unsafe, expose]
protected def forIn' {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) a as β m (ForInStep β)) : m β :=
let rec loop (i : Nat) (h : i as.size) (b : β) : m β := do
match i, h with
@@ -638,7 +646,7 @@ example [Monad m] (f : α → β → m α) :
```
-/
-- Reference implementation for `foldlM`
@[implemented_by foldlMUnsafe]
@[implemented_by foldlMUnsafe, expose]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β :=
let fold (stop : Nat) (h : stop as.size) :=
let rec loop (i : Nat) (j : Nat) (b : β) : m β := do
@@ -703,7 +711,7 @@ example [Monad m] (f : α → β → m β) :
```
-/
-- Reference implementation for `foldrM`
@[implemented_by foldrMUnsafe]
@[implemented_by foldrMUnsafe, expose]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β :=
let rec fold (i : Nat) (h : i as.size) (b : β) : m β := do
if i == stop then
@@ -758,13 +766,11 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
decreasing_by simp_wf; decreasing_trivial_pre_omega
map 0 (emptyWithCapacity as.size)
@[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM
/--
Applies the monadic action `f` to every element in the array, along with the element's index and a
proof that the index is in bounds, from left to right. Returns the array of results.
-/
@[inline]
@[inline, expose]
def mapFinIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m]
(as : Array α) (f : (i : Nat) α (h : i < as.size) m β) : m (Array β) :=
let rec @[specialize] map (i : Nat) (j : Nat) (inv : i + j = as.size) (bs : Array β) : m (Array β) := do
@@ -782,7 +788,7 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
Applies the monadic action `f` to every element in the array, along with the element's index, from
left to right. Returns the array of results.
-/
@[inline]
@[inline, expose]
def mapIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : Nat α m β) (as : Array α) : m (Array β) :=
as.mapFinIdxM fun i a _ => f i a
@@ -828,7 +834,7 @@ Almost! 5
some 10
```
-/
@[inline]
@[inline, expose]
def findSomeM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m (Option β)) (as : Array α) : m (Option β) := do
for a in as do
match ( f a) with
@@ -909,7 +915,7 @@ The optional parameters `start` and `stop` control the region of the array to be
elements with indices from `start` (inclusive) to `stop` (exclusive) are checked. By default, the
entire array is checked.
-/
@[implemented_by anyMUnsafe]
@[implemented_by anyMUnsafe, expose]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
let any (stop : Nat) (h : stop as.size) :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
@@ -1051,9 +1057,9 @@ Examples:
* `#[1, 2, 3].foldl (· ++ toString ·) "" = "123"`
* `#[1, 2, 3].foldl (s!"({·} {·})") "" = "((( 1) 2) 3)"`
-/
@[inline]
@[inline, expose]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Array α) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM f init start stop
Id.run <| as.foldlM (pure <| f · ·) init start stop
/--
Folds a function over an array from the right, accumulating a value starting with `init`. The
@@ -1068,9 +1074,9 @@ Examples:
* `#[1, 2, 3].foldr (toString · ++ ·) "" = "123"`
* `#[1, 2, 3].foldr (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[inline]
@[inline, expose]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Array α) (start := as.size) (stop := 0) : β :=
Id.run <| as.foldrM f init start stop
Id.run <| as.foldrM (pure <| f · ·) init start stop
/--
Computes the sum of the elements of an array.
@@ -1079,7 +1085,7 @@ Examples:
* `#[a, b, c].sum = a + (b + (c + 0))`
* `#[1, 2, 5].sum = 8`
-/
@[inline]
@[inline, expose]
def sum {α} [Add α] [Zero α] : Array α α :=
foldr (· + ·) 0
@@ -1091,7 +1097,7 @@ Examples:
* `#[1, 2, 3, 4, 5].countP (· < 5) = 4`
* `#[1, 2, 3, 4, 5].countP (· > 5) = 0`
-/
@[inline]
@[inline, expose]
def countP {α : Type u} (p : α Bool) (as : Array α) : Nat :=
as.foldr (init := 0) fun a acc => bif p a then acc + 1 else acc
@@ -1103,7 +1109,7 @@ Examples:
* `#[1, 1, 2, 3, 5].count 5 = 1`
* `#[1, 1, 2, 3, 5].count 4 = 0`
-/
@[inline]
@[inline, expose]
def count {α : Type u} [BEq α] (a : α) (as : Array α) : Nat :=
countP (· == a) as
@@ -1116,9 +1122,9 @@ Examples:
* `#["one", "two", "three"].map (·.length) = #[3, 3, 5]`
* `#["one", "two", "three"].map (·.reverse) = #["eno", "owt", "eerht"]`
-/
@[inline]
@[inline, expose]
def map {α : Type u} {β : Type v} (f : α β) (as : Array α) : Array β :=
Id.run <| as.mapM f
Id.run <| as.mapM (pure <| f ·)
instance : Functor Array where
map := map
@@ -1131,9 +1137,9 @@ that the index is valid.
`Array.mapIdx` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline]
@[inline, expose]
def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : (i : Nat) α (h : i < as.size) β) : Array β :=
Id.run <| as.mapFinIdxM f
Id.run <| as.mapFinIdxM (pure <| f · · ·)
/--
Applies a function to each element of the array along with the index at which that element is found,
@@ -1142,9 +1148,9 @@ returning the array of results.
`Array.mapFinIdx` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline]
@[inline, expose]
def mapIdx {α : Type u} {β : Type v} (f : Nat α β) (as : Array α) : Array β :=
Id.run <| as.mapIdxM f
Id.run <| as.mapIdxM (pure <| f · ·)
/--
Pairs each element of an array with its index, optionally starting from an index other than `0`.
@@ -1153,6 +1159,7 @@ Examples:
* `#[a, b, c].zipIdx = #[(a, 0), (b, 1), (c, 2)]`
* `#[a, b, c].zipIdx 5 = #[(a, 5), (b, 6), (c, 7)]`
-/
@[expose]
def zipIdx (xs : Array α) (start := 0) : Array (α × Nat) :=
xs.mapIdx fun i a => (a, start + i)
@@ -1166,7 +1173,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].find? (· < 5) = some 1`
* `#[7, 6, 5, 8, 1, 2, 6].find? (· < 1) = none`
-/
@[inline]
@[inline, expose]
def find? {α : Type u} (p : α Bool) (as : Array α) : Option α :=
Id.run do
for a in as do
@@ -1190,9 +1197,9 @@ Example:
some 10
```
-/
@[inline]
@[inline, expose]
def findSome? {α : Type u} {β : Type v} (f : α Option β) (as : Array α) : Option β :=
Id.run <| as.findSomeM? f
Id.run <| as.findSomeM? (pure <| f ·)
/--
Returns the first non-`none` result of applying the function `f` to each element of the
@@ -1226,7 +1233,7 @@ Examples:
-/
@[inline]
def findSomeRev? {α : Type u} {β : Type v} (f : α Option β) (as : Array α) : Option β :=
Id.run <| as.findSomeRevM? f
Id.run <| as.findSomeRevM? (pure <| f ·)
/--
Returns the last element of the array for which the predicate `p` returns `true`, or `none` if no
@@ -1238,7 +1245,7 @@ Examples:
-/
@[inline]
def findRev? {α : Type} (p : α Bool) (as : Array α) : Option α :=
Id.run <| as.findRevM? p
Id.run <| as.findRevM? (pure <| p ·)
/--
Returns the index of the first element for which `p` returns `true`, or `none` if there is no such
@@ -1248,7 +1255,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
-/
@[inline]
@[inline, expose]
def findIdx? {α : Type u} (p : α Bool) (as : Array α) : Option Nat :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
loop (j : Nat) :=
@@ -1302,7 +1309,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = 4`
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = 7`
-/
@[inline]
@[inline, expose]
def findIdx (p : α Bool) (as : Array α) : Nat := (as.findIdx? p).getD as.size
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
@@ -1356,10 +1363,6 @@ Examples:
def idxOf? [BEq α] (xs : Array α) (v : α) : Option Nat :=
(xs.finIdxOf? v).map (·.val)
@[deprecated idxOf? (since := "2024-11-20")]
def getIdx? [BEq α] (xs : Array α) (v : α) : Option Nat :=
xs.findIdx? fun a => a == v
/--
Returns `true` if `p` returns `true` for any element of `as`.
@@ -1375,9 +1378,9 @@ Examples:
* `#[2, 4, 5, 6].any (· % 2 = 0) = true`
* `#[2, 4, 5, 6].any (· % 2 = 1) = true`
-/
@[inline]
@[inline, expose]
def any (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.anyM p start stop
Id.run <| as.anyM (pure <| p ·) start stop
/--
Returns `true` if `p` returns `true` for every element of `as`.
@@ -1395,7 +1398,7 @@ Examples:
-/
@[inline]
def all (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.allM p start stop
Id.run <| as.allM (pure <| p ·) start stop
/--
Checks whether `a` is an element of `as`, using `==` to compare elements.
@@ -1406,6 +1409,7 @@ Examples:
* `#[1, 4, 2, 3, 3, 7].contains 3 = true`
* `Array.contains #[1, 4, 2, 3, 3, 7] 5 = false`
-/
@[expose]
def contains [BEq α] (as : Array α) (a : α) : Bool :=
as.any (a == ·)
@@ -1454,6 +1458,7 @@ Examples:
* `#[] ++ #[4, 5] = #[4, 5]`.
* `#[1, 2, 3] ++ #[] = #[1, 2, 3]`.
-/
@[expose]
protected def append (as : Array α) (bs : Array α) : Array α :=
bs.foldl (init := as) fun xs v => xs.push v
@@ -1491,7 +1496,7 @@ Examples:
* `#[2, 3, 2].flatMap Array.range = #[0, 1, 0, 1, 2, 0, 1]`
* `#[['a', 'b'], ['c', 'd', 'e']].flatMap List.toArray = #['a', 'b', 'c', 'd', 'e']`
-/
@[inline]
@[inline, expose]
def flatMap (f : α Array β) (as : Array α) : Array β :=
as.foldl (init := empty) fun bs a => bs ++ f a
@@ -1504,7 +1509,7 @@ Examples:
* `#[#[0, 1], #[], #[2], #[1, 0, 1]].flatten = #[0, 1, 2, 1, 0, 1]`
* `(#[] : Array Nat).flatten = #[]`
-/
@[inline] def flatten (xss : Array (Array α)) : Array α :=
@[inline, expose] def flatten (xss : Array (Array α)) : Array α :=
xss.foldl (init := empty) fun acc xs => acc ++ xs
/--
@@ -1517,6 +1522,7 @@ Examples:
* `#[0, 1].reverse = #[1, 0]`
* `#[0, 1, 2].reverse = #[2, 1, 0]`
-/
@[expose]
def reverse (as : Array α) : Array α :=
if h : as.size 1 then
as
@@ -1549,7 +1555,7 @@ Examples:
* `#[1, 2, 5, 2, 7, 7].filter (fun _ => true) (start := 3) = #[2, 7, 7]`
* `#[1, 2, 5, 2, 7, 7].filter (fun _ => true) (stop := 3) = #[1, 2, 5]`
-/
@[inline]
@[inline, expose]
def filter (p : α Bool) (as : Array α) (start := 0) (stop := as.size) : Array α :=
as.foldl (init := #[]) (start := start) (stop := stop) fun acc a =>
if p a then acc.push a else acc
@@ -1642,7 +1648,7 @@ Examining 7
#[10, 14, 14]
```
-/
@[specialize]
@[specialize, expose]
def filterMapM [Monad m] (f : α m (Option β)) (as : Array α) (start := 0) (stop := as.size) : m (Array β) :=
as.foldlM (init := #[]) (start := start) (stop := stop) fun bs a => do
match ( f a) with
@@ -1662,9 +1668,9 @@ Example:
#[10, 14, 14]
```
-/
@[inline]
@[inline, expose]
def filterMap (f : α Option β) (as : Array α) (start := 0) (stop := as.size) : Array β :=
Id.run <| as.filterMapM f (start := start) (stop := stop)
Id.run <| as.filterMapM (pure <| f ·) (start := start) (stop := stop)
/--
Returns the largest element of the array, as determined by the comparison `lt`, or `none` if
@@ -1782,7 +1788,7 @@ decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ h
induction xs, i, h using Array.eraseIdx.induct with
| @case1 xs i h h' xs' ih =>
unfold eraseIdx
simp +zetaDelta [h', xs', ih]
simp +zetaDelta [h', ih]
| case2 xs i h h' =>
unfold eraseIdx
simp [h']
@@ -1875,8 +1881,6 @@ Examples:
let as := as.push a
loop as j, size_push .. j.lt_succ_self
@[deprecated insertIdx (since := "2024-11-20")] abbrev insertAt := @insertIdx
/--
Inserts an element into an array at the specified index. Panics if the index is greater than the
size of the array.
@@ -1897,8 +1901,6 @@ def insertIdx! (as : Array α) (i : Nat) (a : α) : Array α :=
insertIdx as i a
else panic! "invalid index"
@[deprecated insertIdx! (since := "2024-11-20")] abbrev insertAt! := @insertIdx!
/--
Inserts an element into an array at the specified index. The array is returned unmodified if the
index is greater than the size of the array.
@@ -2021,11 +2023,6 @@ Examples:
def unzip (as : Array (α × β)) : Array α × Array β :=
as.foldl (init := (#[], #[])) fun (as, bs) (a, b) => (as.push a, bs.push b)
@[deprecated partition (since := "2024-11-06")]
def split (as : Array α) (p : α Bool) : Array α × Array α :=
as.foldl (init := (#[], #[])) fun (as, bs) a =>
if p a then (as.push a, bs) else (as, bs.push a)
/--
Replaces the first occurrence of `a` with `b` in an array. The modification is performed in-place
when the reference to the array is unique. Returns the array unmodified when `a` is not present.

View File

@@ -88,4 +88,4 @@ pointer equality, and does not allocate a new array if the result of each functi
pointer-equal to its argument.
-/
@[inline] def Array.mapMono (as : Array α) (f : α α) : Array α :=
Id.run <| as.mapMonoM f
Id.run <| as.mapMonoM (pure <| f ·)

View File

@@ -129,6 +129,6 @@ Examples:
* `#[].binInsert (· < ·) 1 = #[1]`
-/
@[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
Id.run <| binInsertM lt (fun _ => pure k) (fun _ => pure k) as k
end Array

View File

@@ -40,7 +40,7 @@ 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")]
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17"), expose]
def get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
@@ -78,7 +78,8 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
have : xs = #[] 0 < xs.size :=
match xs with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
match xs, this with | _, .inl rfl => simp [foldrM] | xs, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
simp only [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux]
simp [Array.size]
@[simp, grind =] theorem foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
@@ -89,9 +90,13 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
xs.toList.foldr f init = xs.foldr f init :=
List.foldr_eq_foldrM .. foldrM_toList ..
@[simp, grind =] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
@[simp, grind =] theorem toList_push {xs : Array α} {x : α} : (xs.push x).toList = xs.toList ++ [x] := by
rcases xs with xs
simp [push, List.concat_eq_append]
@[deprecated toList_push (since := "2025-05-26")]
abbrev push_toList := @toList_push
@[simp, grind =] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
simp [toListAppend, foldr_toList]
@@ -114,13 +119,13 @@ abbrev pop_toList := @Array.toList_pop
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
apply ext'; simp only [toList_append, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
apply ext'; simp only [toList_append, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@@ -138,26 +143,4 @@ abbrev nil_append := @empty_append
@[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 : β} {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 : β} {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 : β} {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 : β} {xs : Array α} :
xs.foldl f init = xs.toList.foldl f init:= by
simp
end Array

View File

@@ -52,17 +52,20 @@ theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p x
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]
@[grind =]
theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
simp
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)]
@[grind _=_]
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]
@[grind =]
theorem countP_eq_size_filter' : countP p = size filter p := by
funext xs
apply countP_eq_size_filter
@@ -71,7 +74,7 @@ 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
@[simp, grind =] 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
@@ -102,9 +105,11 @@ theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
rcases xs with xs
simp [List.boole_getElem_le_countP]
@[grind =]
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 at h
simp [List.countP_set, h]
theorem countP_filter {xs : Array α} :
@@ -145,7 +150,7 @@ theorem countP_flatMap {p : β → Bool} {xs : Array α} {f : α → Array β} :
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
@[simp, grind =] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
rcases xs with xs
simp [List.countP_reverse]
@@ -172,7 +177,7 @@ variable [BEq α]
cases xs
simp
@[simp] theorem count_empty {a : α} : count a #[] = 0 := rfl
@[simp, grind =] 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
@@ -185,21 +190,28 @@ theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
theorem count_le_size {a : α} {xs : Array α} : count a xs xs.size := countP_le_size
grind_pattern count_le_size => count a xs
@[grind =]
theorem count_eq_size_filter {a : α} {xs : Array α} : count a xs = (filter (· == a) xs).size := by
simp [count, countP_eq_size_filter]
theorem count_le_count_push {a b : α} {xs : Array α} : count a xs count a (xs.push b) := by
simp [count_push]
@[grind =]
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 :=
@[simp, grind =] 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 α)} :
@[simp, grind =] 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
@[simp, grind =] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
rcases xs with xs
simp
@@ -208,9 +220,10 @@ theorem boole_getElem_le_count {xs : Array α} {i : Nat} {a : α} (h : i < xs.si
rw [count_eq_countP]
apply boole_getElem_le_countP (p := (· == a))
@[grind =]
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]
simp [count_eq_countP, countP_set]
variable [LawfulBEq α]
@@ -218,7 +231,7 @@ variable [LawfulBEq α]
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]
simp_all [count_push]
theorem count_singleton_self {a : α} : count a #[a] = 1 := by simp
@@ -279,17 +292,17 @@ abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
theorem count_le_count_map [BEq β] [LawfulBEq β] {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]
simp [List.count_le_count_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]
simp [List.count_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]
simp [List.count_flatMap, Function.comp_def]
theorem countP_replace {a b : α} {xs : Array α} {p : α Bool} :
(xs.replace a b).countP p =

View File

@@ -23,7 +23,7 @@ private theorem rel_of_isEqvAux
induction i with
| zero => contradiction
| succ i ih =>
simp only [Array.isEqvAux, Bool.and_eq_true, decide_eq_true_eq] at heqv
simp only [Array.isEqvAux, Bool.and_eq_true] at heqv
by_cases hj' : j < i
next =>
exact ih _ heqv.right hj'
@@ -69,7 +69,7 @@ theorem isEqv_eq_decide (xs ys : Array α) (r) :
simpa [isEqv_iff_rel] using h'
@[simp, grind =] 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]
simp [isEqv_eq_decide, List.isEqv_eq_decide, Array.size]
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
@@ -100,7 +100,7 @@ theorem beq_eq_decide [BEq α] (xs ys : Array α) :
simp [BEq.beq, isEqv_eq_decide]
@[simp, grind =] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
simp [beq_eq_decide, List.beq_eq_decide]
simp [beq_eq_decide, List.beq_eq_decide, Array.size]
end Array

View File

@@ -24,7 +24,8 @@ open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
@[grind =]
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
@@ -64,6 +65,7 @@ theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
let _, ys, zs, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [size_append, e₁]
@[grind =]
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
@@ -81,11 +83,12 @@ theorem le_size_eraseP {xs : Array α} : xs.size - 1 ≤ (xs.eraseP p).size := b
rcases xs with xs
simpa using List.le_length_eraseP
@[grind ]
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
@[simp, grind] 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
@@ -93,15 +96,18 @@ theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
rcases xs with xs
simp
@[grind _=_]
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
@[grind =]
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
@[grind =]
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
@@ -119,6 +125,7 @@ theorem eraseP_append_right {xs : Array α} ys (h : ∀ b ∈ xs, ¬p b) :
rcases ys with ys
simpa using List.eraseP_append_right ys (by simpa using h)
@[grind =]
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
@@ -126,6 +133,7 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
split <;> simp
@[grind =]
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]
@@ -165,6 +173,7 @@ theorem eraseP_eq_iff {p} {xs : Array α} :
· exact Or.inl h
· exact Or.inr a, l₁, by simpa using h₁, h₂, l, by simp
@[grind =]
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
@@ -197,7 +206,7 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) (xs : Array α) : xs.erase a = x
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]
simp
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
@@ -208,6 +217,7 @@ theorem exists_erase_eq [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)
@[grind =]
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]
@@ -222,11 +232,12 @@ theorem le_size_erase [LawfulBEq α] {a : α} {xs : Array α} : xs.size - 1 ≤
rcases xs with xs
simpa using List.le_length_erase
@[grind ]
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) :
@[simp, grind] 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)
@@ -234,6 +245,7 @@ theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp [forall_mem_ne']
@[grind _=_]
theorem erase_filter [LawfulBEq α] {f : α Bool} {xs : Array α} :
(filter f xs).erase a = filter f (xs.erase a) := by
rcases xs with xs
@@ -251,6 +263,7 @@ theorem erase_append_right [LawfulBEq α] {a : α} {xs : Array α} (ys : Array
rcases ys with ys
simpa using List.erase_append_right ys (by simpa using h)
@[grind =]
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
@@ -258,6 +271,7 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
split <;> simp
@[grind =]
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]
@@ -269,6 +283,7 @@ abbrev erase_mkArray := @erase_replicate
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
theorem erase_comm [LawfulBEq α] (a b : α) {xs : Array α} :
(xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs
@@ -291,7 +306,7 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
@[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]
simp
@[deprecated erase_replicate_self (since := "2025-03-18")]
abbrev erase_mkArray_self := @erase_replicate_self
@@ -312,6 +327,7 @@ theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.siz
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
simp [eraseIdxIfInBounds, h]
@[grind =]
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
@@ -322,6 +338,7 @@ theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
rw [List.take_of_length_le]
simp
@[grind =]
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
@@ -335,10 +352,11 @@ theorem getElem?_eraseIdx_of_lt {xs : Array α} {i : Nat} (h : i < xs.size) {j :
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]
simp only [ite_eq_right_iff]
intro h'
omega
@[grind =]
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]
@@ -362,6 +380,7 @@ theorem eraseIdx_ne_empty_iff {xs : Array α} {i : Nat} {h} : xs.eraseIdx i ≠
simp [h]
· simp
@[grind ]
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)
@@ -373,13 +392,29 @@ theorem eraseIdx_append_of_lt_size {xs : Array α} {k : Nat} (hk : k < xs.size)
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) :
theorem eraseIdx_append_of_size_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, *]
@[deprecated eraseIdx_append_of_size_le (since := "2025-06-11")]
abbrev eraseIdx_append_of_length_le := @eraseIdx_append_of_size_le
@[grind =]
theorem eraseIdx_append {xs ys : Array α} (h : k < (xs ++ ys).size) :
eraseIdx (xs ++ ys) k =
if h' : k < xs.size then
eraseIdx xs k ++ ys
else
xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
split <;> rename_i h
· simp [eraseIdx_append_of_lt_size h]
· rw [eraseIdx_append_of_size_le]
omega
@[grind =]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
simp at h
@@ -428,6 +463,48 @@ theorem eraseIdx_set_gt {xs : Array α} {i : Nat} {j : Nat} {a : α} (h : i < j)
rcases xs with xs
simp [List.eraseIdx_set_gt, *]
@[grind =]
theorem eraseIdx_set {xs : Array α} {i : Nat} {a : α} {hi : i < xs.size} {j : Nat} {hj : j < (xs.set i a).size} :
(xs.set i a).eraseIdx j =
if h' : j < i then
(xs.eraseIdx j).set (i - 1) a (by simp; omega)
else if h'' : j = i then
xs.eraseIdx i
else
(xs.eraseIdx j (by simp at hj; omega)).set i a (by simp at hj ; omega) := by
split <;> rename_i h'
· rw [eraseIdx_set_lt]
omega
· split <;> rename_i h''
· subst h''
rw [eraseIdx_set_eq]
· rw [eraseIdx_set_gt]
omega
theorem set_eraseIdx_le {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : i j) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_lt]
· simp
· omega
theorem set_eraseIdx_gt {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : j < i) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set j a).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_gt]
omega
@[grind =]
theorem set_eraseIdx {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a =
if h' : i j then
(xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega)
else
(xs.set j a).eraseIdx i (by simp at ; omega) := by
split <;> rename_i h'
· rw [set_eraseIdx_le]
omega
· rw [set_eraseIdx_gt]
omega
@[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

View File

@@ -29,7 +29,7 @@ namespace Array
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [h]
simp
theorem size_extract_le {as : Array α} {i j : Nat} :
(as.extract i j).size j - i := by
@@ -46,7 +46,7 @@ theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j ≤ as.size) :
simp
omega
@[simp]
@[simp, grind =]
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₂
@@ -56,7 +56,7 @@ theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop ≤ a
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
@[simp]
@[simp, grind =]
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
as.extract 0 stop = as.pop := by
ext i h₁ h₂
@@ -65,7 +65,7 @@ theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
· simp only [size_extract, size_pop] at h₁ h₂
simp [getElem_extract, getElem_pop]
@[simp]
@[simp, grind _=_]
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₂
@@ -162,14 +162,14 @@ theorem extract_sub_one {as : Array α} {i j : Nat} (h : j < as.size) :
@[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]
simp [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} :
@[simp, grind =] 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
@@ -185,6 +185,7 @@ theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract
as #[] :=
mt extract_eq_empty_of_eq_empty h
@[grind =]
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
@@ -211,13 +212,14 @@ theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
simp [getElem_set]
omega
@[grind =]
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]
@[simp, grind =]
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₂
@@ -238,20 +240,18 @@ 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 α} :
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
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
@[simp, grind =] 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} :
@[simp, grind =] 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
@@ -299,6 +299,7 @@ theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as
simp at h
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
@[grind =]
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₂
@@ -309,6 +310,7 @@ theorem extract_reverse {as : Array α} {i j : Nat} :
congr 1
omega
@[grind =]
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]

View File

@@ -23,10 +23,10 @@ Examples:
-/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
@[simp] theorem size_finRange {n} : (Array.finRange n).size = n := by
@[simp, grind =] 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) :
@[simp, grind =] 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]
@@ -49,6 +49,7 @@ theorem finRange_succ_last {n} :
· simp_all
omega
@[grind _=_]
theorem finRange_reverse {n} : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
ext i h
· simp

View File

@@ -38,11 +38,22 @@ theorem findSome?_singleton {a : α} {f : α → Option β} : #[a].findSome? f =
@[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
@[grind =]
theorem findSomeRev?_push {xs : Array α} {a : α} {f : α Option β} :
(xs.push a).findSomeRev? f = (f a).or (xs.findSomeRev? f) := by
match h : f a with
| some b =>
rw [findSomeRev?_push_of_isSome]
all_goals simp_all
| none =>
rw [findSomeRev?_push_of_isNone]
all_goals simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Array α} (w : xs.findSome? f = some b) :
a, a xs f a = some b := by
cases xs; simp_all [List.exists_of_findSome?_eq_some]
@[simp] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
@[simp, grind =] 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 β} {xs : Array α} :
@@ -59,36 +70,39 @@ theorem findSome?_eq_some_iff {f : α → Option β} {xs : Array α} {b : β} :
· 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 {xs : Array α} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
@[simp, grind =] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard p) xs = find? p xs := by
cases xs; simp
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard p) xs :=
findSome?_guard.symm
@[simp] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
@[simp, grind =] 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 getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
@[simp, grind =] 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]
cases xs; simp [ getElem?_zero_filterMap]
@[simp] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
@[simp, grind =] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
cases xs; simp
@[simp] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
@[simp, grind =] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
(xs.filterMap f).back! = (xs.findSomeRev? f).getD default := by
cases xs; simp
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
@[simp, grind _=_] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
(xs.findSome? f).map g = xs.findSome? (Option.map g f) := by
cases xs; simp
@[grind _=_]
theorem findSome?_map {f : β γ} {xs : Array β} : findSome? p (xs.map f) = xs.findSome? (p f) := by
cases xs; simp [List.findSome?_map]
@[grind =]
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]
@[grind =]
theorem getElem?_zero_flatten (xss : Array (Array α)) :
(flatten xss)[0]? = xss.findSome? fun xs => xs[0]? := by
cases xss using array₂_induction
@@ -104,12 +118,14 @@ theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
@[grind =]
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 at t
simp [ t]
@[grind =]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@@ -140,21 +156,37 @@ abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp] theorem find?_empty : find? p #[] = none := rfl
@[simp, grind =] theorem find?_empty : find? p #[] = none := rfl
@[simp] theorem find?_singleton {a : α} {p : α Bool} :
@[grind =]
theorem find?_singleton {a : α} {p : α Bool} :
#[a].find? p = if p a then some a else none := by
simp [singleton_eq_toArray_singleton]
simp
@[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 {xs : Array α} (h : ¬p a) :
@[simp] theorem findRev?_push_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 xs = none x xs, ¬ p x := by
@[deprecated findRev?_push_of_neg (since := "2025-06-12")]
abbrev findRev?_cons_of_neg := @findRev?_push_of_neg
@[grind =]
theorem finRev?_push {xs : Array α} :
findRev? p (xs.push a) = (Option.guard p a).or (xs.findRev? p) := by
cases h : p a
· rw [findRev?_push_of_neg, Option.guard_eq_none_iff.mpr h]
all_goals simp [h]
· rw [findRev?_push_of_pos, Option.guard_eq_some_iff.mpr rfl, h]
all_goals simp [h]
@[deprecated finRev?_push (since := "2025-06-12")]
abbrev findRev?_cons := @finRev?_push
@[simp, grind =] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
cases xs; simp
theorem find?_eq_some_iff_append {xs : Array α} :
@@ -178,60 +210,63 @@ theorem find?_push_eq_some {xs : Array α} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
cases xs; simp
@[simp] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
@[simp, grind =] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
cases xs; simp
@[grind ]
theorem find?_some {xs : Array α} (h : find? p xs = some a) : p a := by
cases xs
simp at h
exact List.find?_some h
@[grind ]
theorem mem_of_find?_eq_some {xs : Array α} (h : find? p xs = some a) : a xs := by
cases xs
simp at h
simpa using List.mem_of_find?_eq_some h
@[grind]
theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h xs := by
cases xs
simp [List.get_find?_mem]
@[simp] theorem find?_filter {xs : Array α} (p q : α Bool) :
@[simp, grind =] theorem find?_filter {xs : Array α} (p q : α Bool) :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
cases xs; simp
@[simp] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
@[simp, grind =] 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} {xs : Array α} (h) :
@[simp, grind =] 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} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
@[simp, grind =] 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} {xs : Array α} :
@[simp, grind =] 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, grind =] 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, grind =] 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 {xs ys : Array α} :
@[simp, grind =] 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 {xss : Array (Array α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
@[simp, grind _=_] 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]
@@ -270,10 +305,10 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
@[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} :
@[simp, grind =] 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]
simp [List.find?_flatMap]
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
@@ -282,6 +317,7 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
@[grind =]
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]
@@ -312,7 +348,7 @@ 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?_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 [ List.toArray_replicate, Classical.or_iff_not_imp_left]
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
@@ -334,6 +370,7 @@ abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
@[deprecated get_find?_replicate (since := "2025-03-18")]
abbrev get_find?_mkArray := @get_find?_replicate
@[grind =]
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
@@ -347,11 +384,15 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
/-! ### findIdx -/
@[simp] theorem findIdx_empty : findIdx p #[] = 0 := rfl
@[grind =]
theorem findIdx_empty : findIdx p #[] = 0 := rfl
@[grind =]
theorem findIdx_singleton {a : α} {p : α Bool} :
#[a].findIdx p = if p a then 0 else 1 := by
simp
@[grind ]
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)
@@ -360,6 +401,8 @@ 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)
grind_pattern findIdx_getElem => xs[xs.findIdx p]
theorem findIdx_lt_size_of_exists {xs : Array α} (h : x xs, p x) :
xs.findIdx p < xs.size := by
rcases xs with xs
@@ -386,18 +429,24 @@ theorem findIdx_le_size {p : α → Bool} {xs : Array α} : xs.findIdx p ≤ xs.
· simp at e
exact Nat.le_of_eq (findIdx_eq_size.mpr e)
grind_pattern findIdx_le_size => xs.findIdx p, xs.size
@[simp]
theorem findIdx_lt_size {p : α Bool} {xs : Array α} :
xs.findIdx p < xs.size x xs, p x := by
rcases xs with xs
simp
grind_pattern findIdx_lt_size => xs.findIdx p, xs.size
/-- `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)
grind_pattern not_of_lt_findIdx => xs.findIdx p, xs[i]
/-- 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
@@ -425,6 +474,7 @@ theorem findIdx_eq {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
simp at h3
simp_all [not_of_lt_findIdx h3]
@[grind =]
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
@@ -432,12 +482,13 @@ theorem findIdx_append {p : α → Bool} {xs ys : Array α} :
rcases ys with ys
simp [List.findIdx_append]
@[grind =]
theorem findIdx_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx p = if xs.findIdx p < xs.size then xs.findIdx p else xs.size + if p a then 0 else 1 := by
simp only [push_eq_append, findIdx_append]
split <;> rename_i h
· rfl
· simp [findIdx_singleton, Nat.add_comm]
· simp [Nat.add_comm]
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
@@ -454,7 +505,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
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} :
@[simp, grind =] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
cases xs
simp
@@ -466,24 +517,24 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
theorem findIdx?_singleton {a : α} {p : α Bool} :
@[simp, grind =] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
@[grind =] theorem findIdx?_singleton {a : α} {p : α Bool} :
#[a].findIdx? p = if p a then some 0 else none := by
simp
@[simp]
@[simp, grind =]
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
@[simp]
@[simp, grind =]
theorem findIdx?_isSome {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
rcases xs with xs
simp [List.findIdx?_isSome]
@[simp]
@[simp, grind =]
theorem findIdx?_isNone {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
rcases xs with xs
@@ -502,7 +553,7 @@ theorem findIdx?_eq_some_of_exists {xs : Array α} {p : α → Bool} (h : ∃ x,
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]
simp
theorem findIdx?_eq_guard_findIdx_lt {xs : Array α} {p : α Bool} :
xs.findIdx? p = Option.guard (fun i => i < xs.size) (xs.findIdx p) := by
@@ -525,18 +576,19 @@ theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p
rcases xs with xs
simpa using List.of_findIdx?_eq_none (by simpa using w)
@[simp] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
@[simp, grind =] 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 :
@[simp, grind =] 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]
@[grind =]
theorem findIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx? p = (xs.findIdx? p).or (if p a then some xs.size else none) := by
simp only [push_eq_append, findIdx?_append]
@@ -552,7 +604,7 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_replicate :
@[simp, grind =] 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]
@@ -577,6 +629,7 @@ theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : Array α} {p q : α → Bool}
rcases xs with xs
simpa using List.findIdx?_eq_none_of_findIdx?_eq_none (by simpa using w)
@[grind =]
theorem findIdx_eq_getD_findIdx? {xs : Array α} {p : α Bool} :
xs.findIdx p = (xs.findIdx? p).getD xs.size := by
rcases xs with xs
@@ -593,14 +646,17 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases xs
simp [hf]
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
@[simp, grind =] 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
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp
@@ -618,7 +674,7 @@ theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α → Bool} :
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@[simp, grind =] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = none x, x xs ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@@ -634,12 +690,14 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[grind =]
theorem findFinIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or (if p a then some xs.size, by simp else none) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_push, Option.pmap_or]
split <;> rename_i h _ <;> split <;> simp [h]
@[grind =]
theorem findFinIdx?_append {xs ys : Array α} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
@@ -649,17 +707,17 @@ theorem findFinIdx?_append {xs ys : Array α} {p : α → Bool} :
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp]
@[simp, grind =]
theorem isSome_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs
simp
simp [Array.size]
@[simp]
@[simp, grind =]
theorem isNone_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs
simp
simp [Array.size]
@[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) :
@@ -667,7 +725,8 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp [Function.comp_def]
simp only [Option.map_map, Function.comp_def, Fin.cast_trans]
simp [Array.size]
/-! ### idxOf
@@ -675,6 +734,7 @@ The verification API for `idxOf` is still incomplete.
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
-/
@[grind =]
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]
@@ -688,10 +748,23 @@ theorem idxOf_eq_size [BEq α] [LawfulBEq α] {xs : Array α} (h : a ∉ xs) : x
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
theorem idxOf_lt_length_of_mem [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)]
simp [List.idxOf_lt_length_of_mem (by simpa using h)]
theorem idxOf_le_size [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a xs.size := by
rcases xs with xs
simp [List.idxOf_le_length]
grind_pattern idxOf_le_size => xs.idxOf a, xs.size
theorem idxOf_lt_size_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a < xs.size a xs := by
rcases xs with xs
simp [List.idxOf_lt_length_iff]
grind_pattern idxOf_lt_size_iff => xs.idxOf a, xs.size
/-! ### idxOf?
@@ -699,27 +772,24 @@ 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
@[grind =] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] 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]
@[simp]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isSome a xs := by
rcases xs with xs
simp
@[simp]
@[grind =]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isNone = ¬ a xs := by
rcases xs with xs
simp
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@@ -728,30 +798,31 @@ The lemmas below should be made consistent with those for `findFinIdx?` (and pro
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 [idxOf?, finIdxOf?]
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[grind =] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] 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 [List.finIdxOf?_eq_none_iff, Array.size]
@[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
unfold Array.size at i
simp [List.finIdxOf?_eq_some_iff]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome a xs := by
@[simp, grind =]
theorem isSome_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome = xs.contains a := by
rcases xs with xs
simp
simp [Array.size]
@[simp]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = ¬ a xs := by
@[simp, grind =]
theorem isNone_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = !xs.contains a := by
rcases xs with xs
simp
simp [Array.size]
end Array

View File

@@ -44,13 +44,19 @@ theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs
@[simp] theorem size_insertIdx {xs : Array α} (h : i xs.size) : (xs.insertIdx i a).size = xs.size + 1 := by
rcases xs with xs
simp at h
simp [List.length_insertIdx, h]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
theorem eraseIdx_insertIdx_self {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
@[deprecated eraseIdx_insertIdx_self (since := "2025-06-15")]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
simp [eraseIdx_insertIdx_self]
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 =
@@ -65,6 +71,18 @@ theorem insertIdx_eraseIdx_of_le {as : Array α}
cases as
simpa using List.insertIdx_eraseIdx_of_le (by simpa) (by simpa)
@[grind =]
theorem insertIdx_eraseIdx {as : Array α} (h₁ : i < as.size) (h₂ : j (as.eraseIdx i).size) :
(as.eraseIdx i).insertIdx j a =
if h : i j then
(as.insertIdx (j + 1) a (by simp_all; omega)).eraseIdx i (by simp_all; omega)
else
(as.insertIdx j a).eraseIdx (i + 1) (by simp_all) := by
split <;> rename_i h'
· rw [insertIdx_eraseIdx_of_ge] <;> omega
· rw [insertIdx_eraseIdx_of_le] <;> omega
@[grind =]
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
@@ -80,6 +98,7 @@ theorem insertIdx_size_self {xs : Array α} {x : α} : xs.insertIdx xs.size x =
rcases xs with xs
simp
@[grind =]
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
@@ -90,21 +109,22 @@ theorem getElem_insertIdx {xs : Array α} {x : α} {i k : Nat} (w : i ≤ xs.siz
else
xs[k-1]'(by simp [size_insertIdx] at h; omega) := by
cases xs
simp [List.getElem_insertIdx, w]
simp [List.getElem_insertIdx]
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]
simp [getElem_insertIdx, 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]
simp [getElem_insertIdx]
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]
simp [getElem_insertIdx]
rw [dif_neg (by omega), dif_neg (by omega)]
@[grind =]
theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i xs.size) :
(xs.insertIdx i x)[k]? =
if k < i then
@@ -115,7 +135,7 @@ theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i ≤ xs.si
else
xs[k-1]? := by
cases xs
simp [List.getElem?_insertIdx, h]
simp [List.getElem?_insertIdx]
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

File diff suppressed because it is too large Load Diff

View File

@@ -29,16 +29,12 @@ protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {xs ys
Decidable.not_not
@[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
simp only [lex, List.getElem_toArray, List.getElem_singleton]
cases lt a b <;> cases a != b <;> simp [Id.run]
simp [lex]
private theorem cons_lex_cons [BEq α] {lt : α α Bool} {a b : α} {xs ys : Array α} :
(#[a] ++ xs).lex (#[b] ++ ys) lt =
(lt a b || a == b && xs.lex ys lt) := by
simp only [lex, Id.run]
simp only [lex]
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,
@@ -51,13 +47,16 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
@[simp, grind =] 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]
| nil => cases l₂ <;> simp [lex]
| cons x l₁ ih =>
cases l₂ with
| nil => simp [lex, Id.run]
| nil => simp [lex]
| cons y l₂ =>
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
theorem singleton_lex_singleton [BEq α] {lt : α α Bool} : #[a].lex #[b] lt = lt a b := by
simp
@[simp, grind =] 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
@@ -163,7 +162,7 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = false ys xs := by
cases xs
cases ys
simp [List.not_lt_iff_ge]
simp
instance [DecidableEq α] [LT α] [DecidableLT α] : DecidableLT (Array α) :=
fun xs ys => decidable_of_iff (lex xs ys = true) lex_eq_true_iff_lt

View File

@@ -27,7 +27,7 @@ theorem mapFinIdx_induction (xs : Array α) (f : (i : Nat) → α → (h : i < x
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
let as : Array β := Id.run <| Array.mapFinIdxM.map xs (pure <| 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 =>
@@ -51,27 +51,27 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
i h, p i ((Array.mapFinIdx xs f)[i]) h :=
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] 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_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
@[simp, grind =] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
@[simp, grind =] 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} :
@[simp, grind =] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => some <| 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 {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] 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
@@ -91,20 +91,20 @@ theorem mapIdx_spec {f : Nat → α → β} {xs : Array α}
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 α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
@[simp, grind =] 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 α β} {xs : Array α} {i : Nat}
@[simp, grind =] 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 α β} {xs : Array α} {i : Nat} :
@[simp, grind =] 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 α β} {xs : Array α} :
@[simp, grind =] 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
@@ -126,7 +126,7 @@ namespace Array
/-! ### zipIdx -/
@[simp] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
@[simp, grind =] 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]
@@ -135,12 +135,12 @@ abbrev getElem_zipWithIndex := @getElem_zipIdx
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
ext i hi₁ hi₂ <;> simp [Nat.add_comm]
ext i hi₁ hi₂ <;> simp
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp] theorem toList_zipIdx {xs : Array α} {k : Nat} :
@[simp, grind =] theorem toList_zipIdx {xs : Array α} {k : Nat} :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@@ -185,24 +185,26 @@ abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
subst w
rfl
@[simp]
@[simp, grind =]
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]
simp only [List.mapFinIdx_toArray, List.mapFinIdx_eq_ofFn, Fin.getElem_fin, List.getElem_toArray]
simp [Array.size]
@[grind =]
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 [List.mapFinIdx_append, Array.size]
@[simp]
@[simp, grind =]
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
@@ -236,7 +238,7 @@ theorem exists_of_mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) → α
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) β} :
@[simp, grind =] 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
@@ -264,12 +266,12 @@ theorem mapFinIdx_eq_append_iff {xs : Array α} {f : (i : Nat) → α → (h : i
toArray_eq_append_iff]
constructor
· rintro l₁, l₂, rfl, rfl, rfl
refine l₁.toArray, l₂.toArray, by simp_all
refine l₁.toArray, l₂.toArray, by simp_all [Array.size]
· rintro l₁, l₂, rfl, h₁, h₂
simp [ toList_inj] at h₁ h₂
obtain rfl := h₁
obtain rfl := h₂
refine l₁, l₂, by simp_all
refine l₁, l₂, by simp_all [Array.size]
theorem mapFinIdx_eq_push_iff {xs : Array α} {b : β} {f : (i : Nat) α (h : i < xs.size) β} :
xs.mapFinIdx f = ys.push b
@@ -289,7 +291,7 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (
rw [eq_comm, mapFinIdx_eq_iff]
simp
@[simp] theorem mapFinIdx_mapFinIdx {xs : Array α}
@[simp, grind =] 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
@@ -304,14 +306,14 @@ theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h
@[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) β} :
@[simp, grind =] 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]
simp [List.mapFinIdx_reverse, Array.size]
/-! ### mapIdx -/
@[simp]
@[simp, grind =]
theorem mapIdx_empty {f : Nat α β} : mapIdx f #[] = #[] :=
rfl
@@ -331,13 +333,14 @@ theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
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]
@[simp, grind =]
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]
@@ -359,7 +362,7 @@ theorem exists_of_mem_mapIdx {b : β} {xs : Array α}
rw [mapIdx_eq_mapFinIdx] at h
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
@[simp] theorem mem_mapIdx {b : β} {xs : Array α} :
@[simp, grind =] 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
@@ -413,7 +416,7 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_eq_mapIdx_iff]
@[simp] theorem mapIdx_set {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
@[simp, grind =] theorem mapIdx_set {f : Nat α β} {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]
@@ -423,17 +426,17 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_set]
@[simp] theorem back?_mapIdx {xs : Array α} {f : Nat α β} :
@[simp, grind =] 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) :
@[simp, grind =] 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 β γ} :
@[simp, grind =] 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]
@@ -446,7 +449,7 @@ theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β}
@[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 α β} :
@[simp, grind =] 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]
@@ -455,7 +458,7 @@ end Array
namespace List
@[grind] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] 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) :
@@ -476,7 +479,7 @@ namespace List
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
@[grind] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] 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) :
@@ -486,7 +489,7 @@ namespace List
| x :: xs => simp only [mapFinIdxM.go, mapIdxM.go, go]
unfold Array.mapIdxM
rw [mapFinIdxM_toArray]
simp only [mapFinIdxM, mapIdxM]
simp only [mapFinIdxM, mapIdxM, Array.size]
rw [go]
end List

View File

@@ -25,16 +25,30 @@ open Nat
/-! ## Monadic operations -/
theorem map_toList_inj [Monad m] [LawfulMonad m]
{xs : m (Array α)} {ys : m (Array α)} :
toList <$> xs = toList <$> ys xs = ys := by
simp
/-! ### mapM -/
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Array α} {f : α β} :
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
induction xs; simp_all
@[simp] theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
@[simp, grind =] theorem idRun_mapM {xs : Array α} {f : α Id β} : (xs.mapM f).run = xs.map (f · |>.run) :=
mapM_pure
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α m β} {xs ys : Array α} :
@[deprecated idRun_mapM (since := "2025-05-21")]
theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
mapM_pure
@[simp, grind =] theorem mapM_map [Monad m] [LawfulMonad m] {f : α β} {g : β m γ} {xs : Array α} :
(xs.map f).mapM g = xs.mapM (g f) := by
rcases xs with xs
simp
@[simp, grind =] 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
@@ -45,7 +59,7 @@ theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] {f : α → m β} {xs : Ar
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]
simp only [Functor.map_map]
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 []
@@ -129,13 +143,13 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
cases as <;> cases bs
simp_all
@[simp] theorem forM_append [Monad m] [LawfulMonad m] {xs ys : Array α} {f : α m PUnit} :
@[simp, grind =] 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} :
@[simp, grind =] 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
@@ -181,14 +195,20 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
@[simp] theorem forIn'_yield_eq_foldl
theorem idRun_forIn'_yield_eq_foldl
{xs : Array α} (f : (a : α) a xs β Id β) (init : β) :
(forIn' xs init (fun a m b => .yield <$> f a m b)).run =
xs.attach.foldl (fun b a, h => f a h b |>.run) init := by
simp
@[deprecated idRun_forIn'_yield_eq_foldl (since := "2025-05-21")]
theorem forIn'_yield_eq_foldl
{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]
xs.attach.foldl (fun b a, h => f a h b) init :=
forIn'_pure_yield_eq_foldl _ _
@[simp] theorem forIn'_map [Monad m] [LawfulMonad m]
@[simp, grind =] theorem forIn'_map [Monad m] [LawfulMonad m]
{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
@@ -214,23 +234,29 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
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]
simp
@[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 [List.forIn_pure_yield_eq_foldl]
@[simp] theorem forIn_yield_eq_foldl
theorem idRun_forIn_yield_eq_foldl
{xs : Array α} (f : α β Id β) (init : β) :
(forIn xs init (fun a b => .yield <$> f a b)).run =
xs.foldl (fun b a => f a b |>.run) init := by
simp
@[deprecated idRun_forIn_yield_eq_foldl (since := "2025-05-21")]
theorem forIn_yield_eq_foldl
{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]
xs.foldl (fun b a => f a b) init :=
forIn_pure_yield_eq_foldl _ _
@[simp] theorem forIn_map [Monad m] [LawfulMonad m]
@[simp, grind =] theorem forIn_map [Monad m] [LawfulMonad m]
{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
@@ -284,7 +310,7 @@ namespace List
@[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]
simp [ filterM_toArray]
@[grind =] theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α m Bool} :
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
@@ -296,7 +322,7 @@ namespace List
@[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]
simp [ filterRevM_toArray]
@[grind =] theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α m (Option β)} :
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
@@ -314,7 +340,7 @@ namespace List
@[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 [ filterMapM_toArray]
@[simp, grind =] 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

View File

@@ -8,7 +8,9 @@ module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.Array.Monadic
import Init.Data.List.OfFn
import Init.Data.List.FinRange
/-!
# Theorems about `Array.ofFn`
@@ -19,7 +21,9 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
@[simp] theorem ofFn_zero {f : Fin 0 α} : ofFn f = #[] := by
/-! ### ofFn -/
@[simp, grind =] theorem ofFn_zero {f : Fin 0 α} : ofFn f = #[] := by
simp [ofFn, ofFn.go]
theorem ofFn_succ {f : Fin (n+1) α} :
@@ -32,18 +36,29 @@ theorem ofFn_succ {f : Fin (n+1) → α} :
intro h₃
simp only [show i = n by omega]
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n α} : (List.ofFn f).toArray = Array.ofFn f := by
theorem ofFn_add {n m} {f : Fin (n + m) α} :
ofFn f = (ofFn (fun i => f (i.castLE (Nat.le_add_right n m)))) ++ (ofFn (fun i => f (i.natAdd n))) := by
induction m with
| zero => simp
| succ m ih => simp [ofFn_succ, ih]
@[simp, grind =] 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
@[simp, grind =] theorem toList_ofFn {f : Fin n α} : (Array.ofFn f).toList = List.ofFn f := by
apply List.ext_getElem <;> simp
theorem ofFn_succ' {f : Fin (n+1) α} :
ofFn f = #[f 0] ++ ofFn (fun i => f i.succ) := by
apply Array.toList_inj.mp
simp [List.ofFn_succ]
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]
simp
@[simp 500]
@[simp 500, grind =]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
constructor
· intro w
@@ -52,4 +67,70 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
/-! ### ofFnM -/
/-- Construct (in a monadic context) an array by applying a monadic function to each index. -/
def ofFnM {n} [Monad m] (f : Fin n m α) : m (Array α) :=
Fin.foldlM n (fun xs i => xs.push <$> f i) (Array.emptyWithCapacity n)
@[simp, grind =]
theorem ofFnM_zero [Monad m] {f : Fin 0 m α} : ofFnM f = pure #[] := by
simp [ofFnM]
theorem ofFnM_succ' {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let a f 0
let as ofFnM fun i => f i.succ
pure (#[a] ++ as)) := by
simp [ofFnM, Fin.foldlM_eq_foldlM_finRange, List.foldlM_push_eq_append, List.finRange_succ, Function.comp_def]
theorem ofFnM_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) m α} :
ofFnM f = (do
let as ofFnM fun i => f i.castSucc
let a f (Fin.last n)
pure (as.push a)) := by
simp [ofFnM, Fin.foldlM_succ_last]
theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) m α} :
ofFnM f = (do
let as ofFnM fun i : Fin n => f (i.castLE (Nat.le_add_right n k))
let bs ofFnM fun i : Fin k => f (i.natAdd n)
pure (as ++ bs)) := by
induction k with
| zero => simp
| succ k ih =>
simp only [ofFnM_succ, Nat.add_eq, ih, Fin.castSucc_castLE, Fin.castSucc_natAdd, bind_pure_comp,
bind_assoc, bind_map_left, Fin.natAdd_last, map_bind, Functor.map_map]
congr 1
funext xs
congr 1
funext ys
congr 1
funext x
simp
@[simp, grind =] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toList <$> ofFnM f = List.ofFnM f := by
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ, List.ofFnM_succ_last, ih]
@[simp]
theorem ofFnM_pure_comp [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (pure f) = (pure (ofFn f) : m (Array α)) := by
apply Array.map_toList_inj.mp
simp
-- Variant of `ofFnM_pure_comp` using a lambda.
-- This is not marked a `@[simp]` as it would match on every occurrence of `ofFnM`.
theorem ofFnM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α} :
ofFnM (fun i => pure (f i)) = (pure (ofFn f) : m (Array α)) :=
ofFnM_pure_comp
@[simp, grind =] theorem idRun_ofFnM {f : Fin n Id α} :
Id.run (ofFnM f) = ofFn (fun i => Id.run (f i)) := by
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ', ofFn_succ', ih]
end Array

View File

@@ -91,17 +91,26 @@ theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a ∈ xs ↔ a
simp only [perm_iff_toList_perm] at p
simpa using p.mem_iff
grind_pattern Perm.mem_iff => xs ~ ys, a xs
grind_pattern Perm.mem_iff => xs ~ ys, a ys
theorem Perm.append {xs ys as bs : Array α} (p₁ : xs ~ ys) (p₂ : as ~ bs) :
xs ++ as ~ ys ++ bs := by
cases xs; cases ys; cases as; cases bs
simp only [append_toArray, perm_iff_toList_perm] at p₁ p₂
exact p₁.append p₂
grind_pattern Perm.append => xs ~ ys, as ~ bs, xs ++ as
grind_pattern Perm.append => xs ~ ys, as ~ bs, ys ++ bs
theorem Perm.push (x : α) {xs ys : Array α} (p : xs ~ ys) :
xs.push x ~ ys.push x := by
rw [push_eq_append_singleton]
exact p.append .rfl
grind_pattern Perm.push => xs ~ ys, xs.push x
grind_pattern Perm.push => xs ~ ys, ys.push x
theorem Perm.push_comm (x y : α) {xs ys : Array α} (p : xs ~ ys) :
(xs.push x).push y ~ (ys.push y).push x := by
cases xs; cases ys

View File

@@ -27,23 +27,27 @@ Internal implementation of `Array.qsort`.
It does so by first swapping the elements at indices `lo`, `mid := (lo + hi) / 2`, and `hi`
if necessary so that the middle (pivot) element is at index `hi`.
We then iterate from `j = lo` to `j = hi`, with a pointer `i` starting at `lo`, and
We then iterate from `k = lo` to `k = hi`, with a pointer `i` starting at `lo`, and
swapping each element which is less than the pivot to position `i`, and then incrementing `i`.
-/
def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {m : Nat // lo m m < n} × Vector α n :=
def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat) (w : lo hi := by omega)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {m : Nat // lo m m hi} × Vector α n :=
let mid := (lo + hi) / 2
let as := if lt as[mid] as[lo] then as.swap lo mid else as
let as := if lt as[hi] as[lo] then as.swap lo hi else as
let as := if lt as[mid] as[hi] then as.swap mid hi else as
let pivot := as[hi]
let rec loop (as : Vector α n) (i j : Nat)
(ilo : lo i := by omega) (jh : j < n := by omega) (w : i j := by omega) :=
if h : j < hi then
if lt as[j] pivot then
loop (as.swap i j) (i+1) (j+1)
-- During this loop, elements below in `[lo, i)` are less than `pivot`,
-- elements in `[i, k)` are greater than or equal to `pivot`,
-- elements in `[k, hi)` are unexamined,
-- while `as[hi]` is (by definition) the pivot.
let rec loop (as : Vector α n) (i k : Nat)
(ilo : lo i := by omega) (ik : i k := by omega) (w : k hi := by omega) :=
if h : k < hi then
if lt as[k] pivot then
loop (as.swap i k) (i+1) (k+1)
else
loop as i (j+1)
loop as i (k+1)
else
(i, ilo, by omega, as.swap i hi)
loop as lo lo
@@ -51,25 +55,28 @@ def qpartition {n} (as : Vector α n) (lt : αα → Bool) (lo hi : Nat)
/--
In-place quicksort.
`qsort as lt low high` sorts the subarray `as[low:high+1]` in-place using `lt` to compare elements.
`qsort as lt lo hi` sorts the subarray `as[lo:hi+1]` in-place using `lt` to compare elements.
-/
@[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)
(lo := 0) (hi := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat) (w : lo hi := by omega)
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
if h₁ : lo < hi then
let mid, hmid, as := qpartition as lt lo hi
if h₂ : mid hi then
-- This only occurs when `hi ≤ lo`,
-- and thus `as[lo:hi+1]` is trivially already sorted.
as
else
-- Otherwise, we recursively sort the two subarrays.
sort (sort as lo mid) (mid+1) hi
else as
if h : as.size = 0 then
as
else
let low := min low (as.size - 1)
let high := min high (as.size - 1)
sort as.toVector low high |>.toArray
let lo := min lo (as.size - 1)
let hi := max lo (min hi (as.size - 1))
sort as.toVector lo hi |>.toArray
set_option linter.unusedVariables.funArgs false in
/--

View File

@@ -29,6 +29,7 @@ open Nat
/-! ### range' -/
@[grind _=_]
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]
@@ -39,16 +40,17 @@ theorem range'_succ {s n step} : range' s (n + 1) step = #[s] ++ range' (s + ste
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, grind =] theorem range'_zero : range' s 0 step = #[] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by
@[simp, grind =] 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]
@[grind =]
theorem mem_range' {n} : m range' s n step i < n, m = s + step * i := by
simp [range']
constructor
@@ -57,6 +59,7 @@ theorem mem_range' {n} : m ∈ range' s n step ↔ ∃ i < n, m = s + step * i :
· rintro i, w, h'
exact i, w, by simp_all
@[simp, grind =]
theorem pop_range' : (range' s n step).pop = range' s (n - 1) step := by
ext <;> simp
@@ -66,6 +69,7 @@ theorem map_add_range' {a} (s n step) : map (a + ·) (range' s n step) = range'
theorem range'_succ_left : range' (s + 1) n step = (range' s n step).map (· + 1) := by
ext <;> simp <;> omega
@[grind _=_]
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₂
@@ -77,7 +81,8 @@ theorem range'_append {s m n step : Nat} :
have : step * m step * i := by exact mul_le_mul_left step h
omega
@[simp] theorem range'_append_1 {s m n : Nat} :
@[simp, grind _=_]
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
@@ -86,7 +91,7 @@ theorem range'_concat {s n : Nat} : range' s (n + 1) step = range' s n step ++ #
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, grind =] 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
@@ -116,14 +121,26 @@ theorem range'_eq_append_iff : range' s n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs =
simp only [List.find?_toArray]
simp
@[grind =]
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']
@[simp, grind =]
theorem count_range' {a s n step} (h : 0 < step := by simp) :
count a (range' s n step) = if i, i < n a = s + step * i then 1 else 0 := by
rw [ List.toArray_range', List.count_toArray, List.count_range' h]
@[simp, grind =]
theorem count_range_1' {a s n} :
count a (range' s n) = if s a a < s + n then 1 else 0 := by
rw [ List.toArray_range', List.count_toArray, List.count_range_1']
/-! ### range -/
@[grind _=_]
theorem range_eq_range' {n : Nat} : range n = range' 0 n := by
simp [range, range']
@@ -145,6 +162,7 @@ theorem range'_eq_map_range {s n : Nat} : range' s n = map (s + ·) (range n) :=
theorem range_ne_empty_iff {n : Nat} : range n #[] n 0 := by
cases n <;> simp
@[grind _=_]
theorem range_succ {n : Nat} : range (succ n) = range n ++ #[n] := by
ext i h₁ h₂
· simp
@@ -160,7 +178,7 @@ theorem range_add {n m : Nat} : range (n + m) = range n ++ (range m).map (n + ·
theorem reverse_range' {s n : Nat} : reverse (range' s n) = map (s + n - 1 - ·) (range n) := by
simp [ toList_inj, List.reverse_range']
@[simp]
@[simp, grind =]
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]
@@ -168,20 +186,25 @@ 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
@[simp, grind =] 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} :
@[simp, grind =] 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} :
@[simp, grind =] 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]
@[grind =]
theorem erase_range : (range n).erase i = range (min n i) ++ range' (i + 1) (n - (i + 1)) := by
simp [range_eq_range', erase_range']
@[simp, grind =]
theorem count_range {a n} :
count a (range n) = if a < n then 1 else 0 := by
rw [ List.toArray_range, List.count_toArray, List.count_range]
/-! ### zipIdx -/
@@ -190,13 +213,13 @@ theorem zipIdx_eq_empty_iff {xs : Array α} {i : Nat} : xs.zipIdx i = #[] ↔ xs
cases xs
simp
@[simp]
@[simp, grind =]
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
ext_getElem? fun i by simp [Nat.add_comm, Nat.add_left_comm]; rfl
-- Arguments are explicit for parity with `zipIdx_map_fst`.
@[simp]
@@ -233,7 +256,7 @@ theorem zipIdx_eq_map_add {xs : Array α} {i : Nat} :
simp only [zipIdx_toArray, List.map_toArray, mk.injEq]
rw [List.zipIdx_eq_map_add]
@[simp]
@[simp, grind =]
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx #[x] k = #[(x, k)] :=
rfl
@@ -281,6 +304,7 @@ theorem zipIdx_map {xs : Array α} {k : Nat} {f : α → β} :
cases xs
simp [List.zipIdx_map]
@[grind =]
theorem zipIdx_append {xs ys : Array α} {k : Nat} :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + xs.size) := by
cases xs

View File

@@ -24,7 +24,7 @@ Examples:
* `#[0, 1, 2].set 1 5 = #[0, 5, 2]`
* `#["orange", "apple"].set 1 "grape" = #["orange", "grape"]`
-/
@[extern "lean_array_fset"]
@[extern "lean_array_fset", expose]
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) :
Array α where
toList := xs.toList.set i v
@@ -40,17 +40,15 @@ Examples:
* `#["orange", "apple"].setIfInBounds 1 "grape" = #["orange", "grape"]`
* `#["orange", "apple"].setIfInBounds 5 "grape" = #["orange", "apple"]`
-/
@[inline] def Array.setIfInBounds (xs : Array α) (i : Nat) (v : α) : Array α :=
@[inline, expose] 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
/--
Set an element in an array, or panic if the index is out of bounds.
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
-/
@[extern "lean_array_set"]
@[extern "lean_array_set", expose]
def Array.set! (xs : Array α) (i : @& Nat) (v : α) : Array α :=
Array.setIfInBounds xs i v

View File

@@ -290,7 +290,7 @@ Examples:
-/
@[inline]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldlM f (init := init)
Id.run <| as.foldlM (pure <| f · ·) (init := init)
/--
Folds an operation from right to left over the elements in a subarray.
@@ -304,7 +304,7 @@ Examples:
-/
@[inline]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldrM f (init := init)
Id.run <| as.foldrM (pure <| f · ·) (init := init)
/--
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
@@ -314,7 +314,7 @@ an element that satisfies the predicate is found.
-/
@[inline]
def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.anyM p
Id.run <| as.anyM (pure <| p ·)
/--
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
@@ -324,7 +324,7 @@ 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
Id.run <| as.allM (pure <| p ·)
/--
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
@@ -394,7 +394,7 @@ Examples:
-/
@[inline]
def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α :=
Id.run <| as.findRevM? p
Id.run <| as.findRevM? (pure <| p ·)
end Subarray

View File

@@ -45,6 +45,7 @@ theorem zipWith_self {f : αα → δ} {xs : Array α} : zipWith f xs xs =
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
@[grind =]
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
@@ -76,31 +77,35 @@ theorem getElem?_zip_eq_some {as : Array α} {bs : Array β} {z : α × β} {i :
· rintro h₀, h₁
exact _, _, h₀, h₁, rfl
@[simp]
@[simp, grind =]
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]
@[grind =]
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]
@[grind =]
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]
@[grind =]
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]
@[grind =]
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
@@ -111,22 +116,26 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
theorem zipWith_eq_empty_iff {f : α β γ} {as : Array α} {bs : Array β} : zipWith f as bs = #[] as = #[] bs = #[] := by
cases as <;> cases bs <;> simp
@[grind =]
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]
@[grind =]
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]
@[grind =]
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]
@[grind =]
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
@@ -152,7 +161,7 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
· 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} :
@[simp, grind =] 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]
@@ -184,6 +193,7 @@ theorem zipWith_eq_zipWith_take_min (as : Array α) (bs : Array β) :
simp
rw [List.zipWith_eq_zipWith_take_min]
@[grind =]
theorem reverse_zipWith (h : as.size = bs.size) :
(zipWith f as bs).reverse = zipWith f as.reverse bs.reverse := by
cases as
@@ -200,7 +210,7 @@ theorem lt_size_right_of_zip {i : Nat} {as : Array α} {bs : Array β} (h : i <
i < bs.size :=
lt_size_right_of_zipWith h
@[simp]
@[simp, grind =]
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)) :=
@@ -211,18 +221,22 @@ theorem zip_eq_zipWith {as : Array α} {bs : Array β} : zip as bs = zipWith Pro
cases bs
simp [List.zip_eq_zipWith]
@[grind _=_]
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]
@[grind _=_]
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]
@[grind _=_]
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]
@[grind =]
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
@@ -231,6 +245,7 @@ theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size
cases ds
simp_all [List.zip_append]
@[grind =]
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
@@ -276,7 +291,7 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
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} :
@[simp, grind =] 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]
@@ -293,6 +308,7 @@ theorem zip_eq_zip_take_min {as : Array α} {bs : Array β} :
/-! ### zipWithAll -/
@[grind =]
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
@@ -301,31 +317,35 @@ theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
simp [List.getElem?_zipWithAll]
rfl
@[grind =]
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]
@[grind =]
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]
@[grind =]
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]
@[grind =]
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} :
@[simp, grind =] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
simp [ List.toArray_replicate]
@@ -334,12 +354,15 @@ abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
induction l <;> simp_all
@[deprecated fst_unzip (since := "2025-05-26")]
theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
simp
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
induction l <;> simp_all
@[deprecated snd_unzip (since := "2025-05-26")]
theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
simp
@[grind =]
theorem unzip_eq_map {xs : Array (α × β)} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs
simp [List.unzip_eq_map]
@@ -371,11 +394,13 @@ theorem unzip_zip {as : Array α} {bs : Array β} (h : as.size = bs.size) :
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]
rw [ hl, hr, zip_unzip xs, fst_unzip, snd_unzip, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
@[simp, grind =] 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
end Array

View File

@@ -27,7 +27,7 @@ class EquivBEq (α) [BEq α] : Prop extends PartialEquivBEq α, ReflBEq α
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
@[grind] theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm
theorem bne_comm [BEq α] [PartialEquivBEq α] {a b : α} : (a != b) = (b != a) := by

View File

@@ -6,7 +6,10 @@ Authors: Kim Morrison
module
prelude
import Init.Data.BitVec.BasicAux
import Init.Data.BitVec.Basic
import Init.Data.BitVec.Bootstrap
import Init.Data.BitVec.Bitblast
import Init.Data.BitVec.Folds
import Init.Data.BitVec.Decidable
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Folds

View File

@@ -61,7 +61,7 @@ end subsingleton
section zero_allOnes
/-- 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)
@[expose] protected def zero (n : Nat) : BitVec n := .ofNatLT 0 (Nat.two_pow_pos n)
instance : Inhabited (BitVec n) where default := .zero n
/-- Returns a bitvector of size `n` where all bits are `1`. -/
@@ -74,28 +74,30 @@ section getXsb
/--
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
@[inline, expose] def getLsb (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
@[deprecated getLsb (since := "2025-06-17"), inherit_doc getLsb]
abbrev getLsb' := @getLsb
/-- 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
@[inline, expose] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getLsb x i, h) else none
/--
Returns the `i`th most significant bit.
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
@[inline] def getMsb (x : BitVec w) (i : Fin w) : Bool := x.getLsb w-1-i, by omega
@[deprecated getMsb (since := "2025-06-17"), inherit_doc getMsb]
abbrev getMsb' := @getMsb
/-- 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
if h : i < w then some (getMsb x i, h) else none
/-- Returns the `i`th least significant bit or `false` if `i ≥ w`. -/
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
@[inline, expose] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
x.toNat.testBit i
/-- Returns the `i`th most significant bit, or `false` if `i ≥ w`. -/
@@ -110,11 +112,11 @@ end getXsb
section getElem
instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
getElem xs i h := xs.getLsb' i, h
getElem xs i h := xs.getLsb i, h
/-- We prefer `x[i]` as the simp normal form for `getLsb'` -/
@[simp] theorem getLsb'_eq_getElem (x : BitVec w) (i : Fin w) :
x.getLsb' i = x[i] := rfl
@[simp] theorem getLsb_eq_getElem (x : BitVec w) (i : Fin w) :
x.getLsb i = x[i] := rfl
/-- We prefer `x[i]?` as the simp normal form for `getLsb?` -/
@[simp] theorem getLsb?_eq_getElem? (x : BitVec w) (i : Nat) :
@@ -134,6 +136,7 @@ section Int
/--
Interprets the bitvector as an integer stored in two's complement form.
-/
@[expose]
protected def toInt (x : BitVec n) : Int :=
if 2 * x.toNat < 2^n then
x.toNat
@@ -147,6 +150,7 @@ 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)`.
-/
@[expose]
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
@@ -172,7 +176,7 @@ recommended_spelling "zero" for "0#n" in [BitVec.ofNat, «term__#__»]
recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
/-- Unexpander for bitvector literals. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNat] meta def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
@@ -181,7 +185,7 @@ scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bitvector literals without truncation. -/
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNatLT] meta def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
@@ -218,12 +222,14 @@ Usually accessed via the `-` prefix operator.
SMT-LIB name: `bvneg`.
-/
@[expose]
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
instance : Neg (BitVec n) := .neg
/--
Returns the absolute value of a signed bitvector.
-/
@[expose]
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
/--
@@ -232,6 +238,7 @@ modulo `2^n`. Usually accessed via the `*` operator.
SMT-LIB name: `bvmul`.
-/
@[expose]
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
instance : Mul (BitVec n) := .mul
@@ -242,6 +249,7 @@ Note that this is currently an inefficient implementation,
and should be replaced via an `@[extern]` with a native implementation.
See https://github.com/leanprover/lean4/issues/7887.
-/
@[expose]
protected def pow (x : BitVec n) (y : Nat) : BitVec n :=
match y with
| 0 => 1
@@ -253,6 +261,7 @@ instance : Pow (BitVec n) Nat where
Unsigned division of bitvectors using the Lean convention where division by zero returns zero.
Usually accessed via the `/` operator.
-/
@[expose]
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
@@ -262,6 +271,7 @@ Unsigned modulo for bitvectors. Usually accessed via the `%` operator.
SMT-LIB name: `bvurem`.
-/
@[expose]
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
@@ -273,6 +283,7 @@ where division by zero returns `BitVector.allOnes n`.
SMT-LIB name: `bvudiv`.
-/
@[expose]
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
/--
@@ -342,6 +353,7 @@ end arithmetic
section bool
/-- Turns a `Bool` into a bitvector of length `1`. -/
@[expose]
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
@@ -359,6 +371,7 @@ Unsigned less-than for bitvectors.
SMT-LIB name: `bvult`.
-/
@[expose]
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
/--
@@ -366,6 +379,7 @@ Unsigned less-than-or-equal-to for bitvectors.
SMT-LIB name: `bvule`.
-/
@[expose]
protected def ule (x y : BitVec n) : Bool := x.toNat y.toNat
/--
@@ -377,6 +391,7 @@ Examples:
* `BitVec.slt 6#4 7 = true`
* `BitVec.slt 7#4 8 = false`
-/
@[expose]
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
/--
@@ -384,6 +399,7 @@ Signed less-than-or-equal-to for bitvectors.
SMT-LIB name: `bvsle`.
-/
@[expose]
protected def sle (x y : BitVec n) : Bool := x.toInt y.toInt
end relations
@@ -397,7 +413,7 @@ 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)
@[inline, expose] 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
@@ -413,6 +429,7 @@ that can more consistently simplify `BitVec.cast` away.
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.
-/
@[expose]
def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toNat >>> start)
/--
@@ -423,6 +440,7 @@ The resulting bitvector has size `hi - lo + 1`.
SMT-LIB name: `extract`.
-/
@[expose]
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
/--
@@ -431,6 +449,7 @@ Increases the width of a bitvector to one that is at least as large by zero-exte
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.
-/
@[expose]
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
@@ -439,6 +458,7 @@ def setWidth' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
/--
Returns `zeroExtend (w+n) x <<< n` without needing to compute `x % 2^(2+n)`.
-/
@[expose]
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w + m) := by
simp [Nat.shiftLeft_eq, Nat.pow_add]
@@ -495,6 +515,7 @@ SMT-LIB name: `bvand`.
Example:
* `0b1010#4 &&& 0b0110#4 = 0b0010#4`
-/
@[expose]
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
@@ -507,6 +528,7 @@ SMT-LIB name: `bvor`.
Example:
* `0b1010#4 ||| 0b0110#4 = 0b1110#4`
-/
@[expose]
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
@@ -519,6 +541,7 @@ SMT-LIB name: `bvxor`.
Example:
* `0b1010#4 ^^^ 0b0110#4 = 0b1100#4`
-/
@[expose]
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
@@ -531,6 +554,7 @@ SMT-LIB name: `bvnot`.
Example:
* `~~~(0b0101#4) == 0b1010`
-/
@[expose]
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
instance : Complement (BitVec w) := .not
@@ -540,6 +564,7 @@ equivalent to `x * 2^s`, modulo `2^n`.
SMT-LIB name: `bvshl` except this operator uses a `Nat` shift value.
-/
@[expose]
protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft
@@ -551,6 +576,7 @@ 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.
-/
@[expose]
def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
(x.toNat >>> s)#'(by
let x, lt := x
@@ -568,6 +594,7 @@ As a numeric operation, this is equivalent to `x.toInt >>> s`.
SMT-LIB name: `bvashr` except this operator uses a `Nat` shift value.
-/
@[expose]
def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := fun x y => x <<< y.toNat
@@ -581,10 +608,12 @@ As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`.
SMT-LIB name: `bvashr`.
-/
@[expose]
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
/-- Auxiliary function for `rotateLeft`, which does not take into account the case where
the rotation amount is greater than the bitvector width. -/
@[expose]
def rotateLeftAux (x : BitVec w) (n : Nat) : BitVec w :=
x <<< n ||| x >>> (w - n)
@@ -599,6 +628,7 @@ SMT-LIB name: `rotate_left`, except this operator uses a `Nat` shift amount.
Example:
* `(0b0011#4).rotateLeft 3 = 0b1001`
-/
@[expose]
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
@@ -606,6 +636,7 @@ def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
Auxiliary function for `rotateRight`, which does not take into account the case where
the rotation amount is greater than the bitvector width.
-/
@[expose]
def rotateRightAux (x : BitVec w) (n : Nat) : BitVec w :=
x >>> n ||| x <<< (w - n)
@@ -620,6 +651,7 @@ SMT-LIB name: `rotate_right`, except this operator uses a `Nat` shift amount.
Example:
* `rotateRight 0b01001#5 1 = 0b10100`
-/
@[expose]
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
/--
@@ -631,6 +663,7 @@ SMT-LIB name: `concat`.
Example:
* `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
-/
@[expose]
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
@@ -653,6 +686,7 @@ result of appending a single bit to the front in the naive implementation).
/-- Append a single bit to the end of a bitvector, using big endian order (see `append`).
That is, the new bit is the least significant bit. -/
@[expose]
def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool lsb)
/--
@@ -660,6 +694,7 @@ Shifts all bits of `x` to the left by `1` and sets the least significant bit to
This is a non-dependent version of `BitVec.concat` that does not change the total bitwidth.
-/
@[expose]
def shiftConcat (x : BitVec n) (b : Bool) : BitVec n :=
(x.concat b).truncate n
@@ -668,6 +703,7 @@ Prepends a single bit to the front of a bitvector, using big-endian order (see `
The new bit is the most significant bit.
-/
@[expose]
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
((ofBool msb) ++ lsbs).cast (Nat.add_comm ..)
@@ -689,6 +725,12 @@ def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
end bitwise
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
def intMin (w : Nat) := twoPow w (w - 1)
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) := (twoPow w (w - 1)) - 1
/--
Computes a hash of a bitvector, combining 64-bit words using `mixHash`.
-/
@@ -752,6 +794,7 @@ Checks whether subtraction of `x` and `y` results in *unsigned* overflow.
SMT-Lib name: `bvusubo`.
-/
@[expose]
def usubOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat < y.toNat
/--
@@ -760,6 +803,7 @@ Checks whether the subtraction of `x` and `y` results in *signed* overflow, trea
SMT-Lib name: `bvssubo`.
-/
@[expose]
def ssubOverflow {w : Nat} (x y : BitVec w) : Bool :=
(x.toInt - y.toInt 2 ^ (w - 1)) || (x.toInt - y.toInt < - 2 ^ (w - 1))
@@ -770,6 +814,7 @@ For a bitvector `x` with nonzero width, this only happens if `x = intMin`.
SMT-Lib name: `bvnego`.
-/
@[expose]
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
x.toInt == - 2 ^ (w - 1)
@@ -779,6 +824,7 @@ For BitVecs `x` and `y` with nonzero width, this only happens if `x = intMin` an
SMT-LIB name: `bvsdivo`.
-/
@[expose]
def sdivOverflow {w : Nat} (x y : BitVec w) : Bool :=
(2 ^ (w - 1) x.toInt / y.toInt) || (x.toInt / y.toInt < - 2 ^ (w - 1))
@@ -804,4 +850,15 @@ treating `x` and `y` as 2's complement signed bitvectors.
def smulOverflow {w : Nat} (x y : BitVec w) : Bool :=
(x.toInt * y.toInt 2 ^ (w - 1)) || (x.toInt * y.toInt < - 2 ^ (w - 1))
/-- Count the number of leading zeros downward from the `n`-th bit to the `0`-th bit for the bitblaster.
This builds a tree of `if-then-else` lookups whose length is linear in the bitwidth,
and an efficient circuit for bitblasting `clz`. -/
def clzAuxRec {w : Nat} (x : BitVec w) (n : Nat) : BitVec w :=
match n with
| 0 => if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w
| n' + 1 => if x.getLsbD n then BitVec.ofNat w (w - 1 - n) else clzAuxRec x n'
/-- Count the number of leading zeros. -/
def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
end BitVec

View File

@@ -24,7 +24,7 @@ The bitvector with value `i mod 2^n`.
-/
@[expose, match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' (2^n) i
toFin := Fin.ofNat (2^n) i
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
@@ -41,6 +41,7 @@ Usually accessed via the `+` operator.
SMT-LIB name: `bvadd`.
-/
@[expose]
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
instance : Add (BitVec n) := BitVec.add
@@ -49,6 +50,7 @@ Subtracts one bitvector from another. This can be interpreted as either signed o
modulo `2^n`. Usually accessed via the `-` operator.
-/
@[expose]
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
instance : Sub (BitVec n) := BitVec.sub

View File

@@ -6,12 +6,14 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
module
prelude
import Init.Data.BitVec.Folds
import all Init.Data.Nat.Bitwise.Basic
import Init.Data.Nat.Mod
import all Init.Data.Int.DivMod
import Init.Data.Int.LemmasAux
import all Init.Data.BitVec.Lemmas
import all Init.Data.BitVec.Basic
import Init.Data.BitVec.Decidable
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Folds
/-!
# Bit blasting of bitvectors
@@ -238,7 +240,7 @@ theorem toNat_add_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
simp only [decide_eq_true_eq] at this
omega
rw [ carry_width]
simp [not_eq_true, carry_of_and_eq_zero h]
simp [carry_of_and_eq_zero h]
/-- Carry function for bitwise addition. -/
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, x ^^ (y ^^ c))
@@ -252,7 +254,7 @@ theorem getLsbD_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool
(getLsbD x i ^^ (getLsbD y i ^^ carry i x y c)) := by
let x, x_lt := x
let y, y_lt := y
simp only [getLsbD, toNat_add, toNat_setWidth, i_lt, toNat_ofFin, toNat_ofBool,
simp only [getLsbD, toNat_add, toNat_setWidth, toNat_ofFin, toNat_ofBool,
Nat.mod_add_mod, Nat.add_mod_mod]
apply Eq.trans
rw [ Nat.div_add_mod x (2^i), Nat.div_add_mod y (2^i)]
@@ -295,7 +297,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, getElem_add_add_bool]
simp [adcb, 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]
@@ -312,7 +314,7 @@ theorem msb_add {w : Nat} {x y: BitVec w} :
Bool.xor x.msb (Bool.xor y.msb (carry (w - 1) x y false)) := by
simp only [BitVec.msb, BitVec.getMsbD]
by_cases h : w 0
· simp [h, show w = 0 by omega]
· simp [show w = 0 by omega]
· rw [getLsbD_add (x := x)]
simp [show w > 0 by omega]
omega
@@ -332,15 +334,15 @@ theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
(h : x &&& y = 0#w) : x + y = x ||| y := by
rw [add_eq_adc, adc, iunfoldr_replace (fun _ => false) (x ||| y)]
· rfl
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false, getLsbD_or,
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false,
Prod.mk.injEq, and_eq_false_imp]
intros i
replace h : (x &&& y).getLsbD i = (0#w).getLsbD i := by rw [h]
simp only [getLsbD_and, getLsbD_zero, and_eq_false_imp] at h
constructor
· intros hx
simp_all [hx]
· by_cases hx : x.getLsbD i <;> simp_all [hx]
simp_all
· by_cases hx : x.getLsbD i <;> simp_all
/-! ### Sub-/
@@ -377,7 +379,7 @@ theorem bit_not_add_self (x : BitVec w) :
simp only [add_eq_adc]
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
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]
ofNat_eq_ofNat, 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]
@@ -409,7 +411,7 @@ theorem getLsbD_neg {i : Nat} {x : BitVec w} :
· rw [getLsbD_add hi]
have : 0 < w := by omega
simp only [getLsbD_not, hi, decide_true, Bool.true_and, getLsbD_one, this, not_bne,
_root_.true_and, not_eq_eq_eq_not]
not_eq_eq_eq_not]
cases i with
| zero =>
have carry_zero : carry 0 ?x ?y false = false := by
@@ -424,7 +426,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_of_ge _ _ h_ge, h_ge, hi]
simp [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
@@ -433,7 +435,7 @@ theorem getElem_neg {i : Nat} {x : BitVec w} (h : i < w) :
theorem getMsbD_neg {i : Nat} {x : BitVec w} :
getMsbD (-x) i =
(getMsbD x i ^^ decide ( j < w, i < j getMsbD x j = true)) := by
simp only [getMsbD, getLsbD_neg, Bool.decide_and, Bool.and_eq_true, decide_eq_true_eq]
simp only [getMsbD, getLsbD_neg, Bool.and_eq_true, decide_eq_true_eq]
by_cases hi : i < w
case neg =>
simp [hi]; omega
@@ -518,14 +520,11 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
@[simp] theorem 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} :
x.abs.msb = (decide (x = intMin w) && decide (0 < w)) := by
simp only [BitVec.abs, getMsbD_neg, ne_eq, decide_not, Bool.not_bne]
simp only [BitVec.abs]
by_cases h₀ : 0 < w
· by_cases h₁ : x = intMin w
· simp [h₁, msb_intMin]
@@ -548,54 +547,14 @@ theorem ult_eq_not_carry (x y : BitVec w) : x.ult y = !carry w x (~~~y) true :=
rw [Nat.mod_eq_of_lt (by omega)]
omega
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
simp [BitVec.ule, BitVec.ult, decide_not]
theorem ule_eq_carry (x y : BitVec w) : x.ule y = carry w y (~~~x) true := by
simp [ule_eq_not_ult, ult_eq_not_carry]
/-- If two bitvectors have the same `msb`, then signed and unsigned comparisons coincide -/
theorem slt_eq_ult_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) :
x.slt y = x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, BitVec.ult, decide_eq_decide, h]
cases y.msb <;> simp
/-- If two bitvectors have different `msb`s, then unsigned comparison is determined by this bit -/
theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.ult y = y.msb := by
simp only [BitVec.ult, msb_eq_decide, ne_eq, decide_eq_decide] at *
omega
/-- If two bitvectors have different `msb`s, then signed and unsigned comparisons are opposites -/
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.slt y = !x.ult y := by
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 [-Int.natCast_pow]; omega)
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} :
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
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} {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]
@@ -618,12 +577,6 @@ theorem neg_sle_zero (h : 0 < w) {x : BitVec w} :
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 -/
/--
@@ -631,6 +584,7 @@ A recurrence that describes multiplication as repeated addition.
This function is useful for bit blasting multiplication.
-/
@[expose]
def mulRec (x y : BitVec w) (s : Nat) : BitVec w :=
let cur := if y.getLsbD s then (x <<< s) else 0
match s with
@@ -657,7 +611,7 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i
getElem_twoPow]
by_cases hik : i = k
· subst hik
simp [h]
simp
· by_cases hik' : k < (i + 1)
· have hik'' : k < i := by omega
simp [hik', hik'']
@@ -666,8 +620,8 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i
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]
simp only [and_twoPow,
]
by_cases hi : x.getLsbD i <;> simp [hi] <;> omega
/--
@@ -824,7 +778,7 @@ private theorem Nat.div_add_eq_left_of_lt {x y z : Nat} (hx : z x) (hy : y <
· apply Nat.le_trans
· exact div_mul_le_self x z
· omega
· simp only [succ_eq_add_one, Nat.add_mul, Nat.one_mul]
· simp only [Nat.add_mul, Nat.one_mul]
apply Nat.add_lt_add_of_le_of_lt
· apply Nat.le_of_eq
exact (Nat.div_eq_iff_eq_mul_left hz hx).mp rfl
@@ -937,10 +891,10 @@ def DivModState.lawful_init {w : Nat} (args : DivModArgs w) (hd : 0#w < args.d)
hwrn := by simp only; omega,
hdPos := by assumption
hrLtDivisor := by simp [BitVec.lt_def] at hd ; assumption
hrWidth := by simp [DivModState.init],
hqWidth := by simp [DivModState.init],
hrWidth := by simp,
hqWidth := by simp,
hdiv := by
simp only [DivModState.init, toNat_ofNat, zero_mod, Nat.mul_zero, Nat.add_zero];
simp only [toNat_ofNat, zero_mod, Nat.mul_zero, Nat.add_zero];
rw [Nat.shiftRight_eq_div_pow]
apply Nat.div_eq_of_lt args.n.isLt
}
@@ -968,7 +922,7 @@ theorem DivModState.umod_eq_of_lawful {qr : DivModState w}
n % d = qr.r := by
apply umod_eq_of_mul_add_toNat h.hrLtDivisor
have hdiv := h.hdiv
simp only [shiftRight_zero] at hdiv
simp only at hdiv
simp only [h_final] at *
exact hdiv.symm
@@ -1022,7 +976,7 @@ theorem DivModState.toNat_shiftRight_sub_one_eq
{args : DivModArgs w} {qr : DivModState w} (h : qr.Poised args) :
args.n.toNat >>> (qr.wn - 1)
= (args.n.toNat >>> qr.wn) * 2 + (args.n.getLsbD (qr.wn - 1)).toNat := by
show BitVec.toNat (args.n >>> (qr.wn - 1)) = _
change BitVec.toNat (args.n >>> (qr.wn - 1)) = _
have {..} := h -- break the structure down for `omega`
rw [shiftRight_sub_one_eq_shiftConcat args.n h.hwn_lt]
rw [toNat_shiftConcat_eq_of_lt (k := w - qr.wn)]
@@ -1046,7 +1000,7 @@ obeys the division equation. -/
theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
DivModState.Lawful args (divSubtractShift args qr) := by
rcases args with n, d
simp only [divSubtractShift, decide_eq_true_eq]
simp only [divSubtractShift]
-- We add these hypotheses for `omega` to find them later.
have hrwn, hd, hrd, hr, hn, hrnd, hwn_lt := h
have : d.toNat * (qr.q.toNat * 2) = d.toNat * qr.q.toNat * 2 := by rw [Nat.mul_assoc]
@@ -1091,6 +1045,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
/-! ### Core division algorithm circuit -/
/-- A recursive definition of division for bit blasting, in terms of a shift-subtraction circuit. -/
@[expose]
def divRec {w : Nat} (m : Nat) (args : DivModArgs w) (qr : DivModState w) :
DivModState w :=
match m with
@@ -1182,7 +1137,7 @@ theorem getLsbD_udiv (n d : BitVec w) (hy : 0#w < d) (i : Nat) :
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)]
simp [getMsbD_eq_getLsbD, udiv_eq_divRec (by assumption)]
/- ### Arithmetic shift right (sshiftRight) recurrence -/
@@ -1349,7 +1304,7 @@ 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]
· simp [toInt_of_zero_length]
· 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.natCast_emod, Int.neg_inj]
rw_mod_cast [Nat.mod_eq_of_lt (by simp [Nat.pow_lt_pow_succ])]
@@ -1391,7 +1346,7 @@ theorem umulOverflow_eq {w : Nat} (x y : BitVec w) :
(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 [of_length_zero]
· 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]
@@ -1627,11 +1582,11 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
have := Nat.two_pow_pos (w - 1)
by_cases hbintMin : b = intMin w
· simp only [ne_eq, Decidable.not_not] at hbintMin
· simp only 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,
simp only [sdiv_intMin, 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,
@@ -1707,6 +1662,88 @@ theorem toInt_sdiv (a b : BitVec w) : (a.sdiv b).toInt = (a.toInt.tdiv b.toInt).
· rw [ toInt_bmod_cancel]
rw [BitVec.toInt_sdiv_of_ne_or_ne _ _ (by simpa only [Decidable.not_and_iff_not_or_not] using h)]
private theorem neg_udiv_eq_intMin_iff_eq_intMin_eq_one_of_msb_eq_true
{x y : BitVec w} (hx : x.msb = true) (hy : y.msb = false) :
-x / y = intMin w (x = intMin w y = 1#w) := by
constructor
· intros h
rcases w with _ | w; decide +revert
have : (-x / y).msb = true := by simp [h, msb_intMin]
rw [msb_udiv] at this
simp only [bool_to_prop] at this
obtain hx, hy := this
simp only [beq_iff_eq] at hy
subst hy
simp only [udiv_one, zero_lt_succ, neg_eq_intMin] at h
simp [h]
· rintro hx, hy
subst hx hy
simp
/--
the most significant bit of the signed division `x.sdiv y` can be computed
by the following cases:
(1) x nonneg, y nonneg: never neg.
(2) x nonneg, y neg: neg when result nonzero.
We know that y is nonzero since it is negative, so we only check `|x| ≥ |y|`.
(3) x neg, y nonneg: neg when result nonzero.
We check that `y ≠ 0` and `|x| ≥ |y|`.
(4) x neg, y neg: neg when `x = intMin, `y = -1`, since `intMin / -1 = intMin`.
The proof strategy is to perform a case analysis on the sign of `x` and `y`,
followed by unfolding the `sdiv` into `udiv`.
-/
theorem msb_sdiv_eq_decide {x y : BitVec w} :
(x.sdiv y).msb = (decide (0 < w) &&
(!x.msb && y.msb && decide (-y x)) ||
(x.msb && !y.msb && decide (y -x) && !decide (y = 0#w)) ||
(x.msb && y.msb && decide (x = intMin w) && decide (y = -1#w)))
:= by
rcases w; decide +revert
case succ w =>
simp only [decide_true, ne_eq, decide_and, decide_not, Bool.true_and,
sdiv_eq, udiv_eq]
rcases hxmsb : x.msb <;> rcases hymsb : y.msb
· simp [hxmsb, hymsb, msb_udiv_eq_false_of, Bool.not_false, Bool.and_false, Bool.false_and,
Bool.and_true, Bool.or_self, Bool.and_self]
· simp only [hxmsb, hymsb, msb_neg, msb_udiv_eq_false_of, bne_false, Bool.not_false,
Bool.and_self, ne_zero_of_msb_true, decide_false, Bool.and_true, Bool.true_and, Bool.not_true,
Bool.false_and, Bool.or_false, bool_to_prop]
have : x / -y intMin (w + 1) := by
intros h
have : (x / -y).msb = (intMin (w + 1)).msb := by simp only [h]
simp only [msb_udiv, msb_intMin, show 0 < w + 1 by omega, decide_true, and_eq_true, beq_iff_eq] at this
obtain hcontra, _ := this
simp only [hcontra, true_eq_false] at hxmsb
simp [this, hymsb, udiv_ne_zero_iff_ne_zero_and_le]
· simp only [hxmsb, hymsb, Bool.not_true, Bool.and_self, Bool.false_and, Bool.not_false,
Bool.true_and, Bool.false_or, Bool.and_false, Bool.or_false]
by_cases hx₁ : x = 0#(w + 1)
· simp [hx₁, neg_zero, zero_udiv, msb_zero, le_zero_iff, Bool.and_not_self]
· by_cases hy₁ : y = 0#(w + 1)
· simp [hy₁, udiv_zero, neg_zero, msb_zero, decide_true, Bool.not_true, Bool.and_false]
· simp only [hy₁, decide_false, Bool.not_false, Bool.and_true]
by_cases hxy₁ : (- x / y) = 0#(w + 1)
· simp only [hxy₁, neg_zero, msb_zero, false_eq_decide_iff, BitVec.not_le,
decide_eq_true_eq, BitVec.not_le]
simp only [udiv_eq_zero_iff_eq_zero_or_lt, hy₁, _root_.false_or] at hxy₁
bv_omega
· simp only [udiv_eq_zero_iff_eq_zero_or_lt, _root_.not_or, BitVec.not_lt,
hy₁, not_false_eq_true, _root_.true_and] at hxy₁
simp only [hxy₁, decide_true, msb_neg, bne_iff_ne, ne_eq,
bool_to_prop,
bne_iff_ne, ne_eq, udiv_eq_zero_iff_eq_zero_or_lt, hy₁, _root_.false_or,
BitVec.not_lt, hxy₁, _root_.true_and, decide_not, not_eq_eq_eq_not, not_eq_not,
msb_udiv, msb_neg]
simp only [hx₁, not_false_eq_true, _root_.true_and, decide_not, hxmsb, not_eq_eq_eq_not,
Bool.not_true, decide_eq_false_iff_not, Decidable.not_not, beq_iff_eq]
rw [neg_udiv_eq_intMin_iff_eq_intMin_eq_one_of_msb_eq_true hxmsb hymsb]
· simp only [msb_udiv, msb_neg, hxmsb, bne_true, Bool.not_and, Bool.not_true, Bool.and_true,
Bool.false_and, Bool.and_false, hymsb, ne_zero_of_msb_true, decide_false, Bool.not_false,
Bool.or_self, Bool.and_self, Bool.true_and, Bool.false_or]
simp only [bool_to_prop]
simp [BitVec.ne_zero_of_msb_true (x := x) hxmsb, neg_eq_iff_eq_neg]
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]
@@ -1726,7 +1763,7 @@ theorem msb_umod_of_le_of_ne_zero_of_le {x y : BitVec w}
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]
· simp only [hyz, 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
@@ -1750,6 +1787,116 @@ theorem toInt_srem (x y : BitVec w) : (x.srem y).toInt = x.toInt.tmod y.toInt :=
((not_congr neg_eq_zero_iff).mpr hyz)]
exact neg_le_intMin_of_msb_eq_true h'
@[simp]
theorem msb_intMin_umod_neg_of_msb_true {y : BitVec w} (hy : y.msb = true) :
(intMin w % -y).msb = false := by
by_cases hyintmin : y = intMin w
· simp [hyintmin]
· rw [msb_umod_of_msb_false_of_ne_zero (by simp [hyintmin, hy])]
simp [hy]
@[simp]
theorem msb_neg_umod_neg_of_msb_true_of_msb_true {x y : BitVec w} (hx : x.msb = true) (hy : y.msb = true) :
(-x % -y).msb = false := by
by_cases hx' : x = intMin w
· simp only [hx', neg_intMin, msb_intMin_umod_neg_of_msb_true hy]
· simp [show (-x).msb = false by simp [hx, hx']]
theorem toInt_dvd_toInt_iff {x y : BitVec w} :
y.toInt x.toInt (if x.msb then -x else x) % (if y.msb then -y else y) = 0#w := by
constructor
<;> by_cases hxmsb : x.msb <;> by_cases hymsb: y.msb
<;> intros h
<;> simp only [hxmsb, hymsb, reduceIte, false_eq_true, toNat_eq, toNat_umod, toNat_ofNat,
zero_mod, toInt_eq_neg_toNat_neg_of_msb_true, Int.dvd_neg, Int.neg_dvd,
toInt_eq_toNat_of_msb] at h
<;> simp only [hxmsb, hymsb, toInt_eq_neg_toNat_neg_of_msb_true, toInt_eq_toNat_of_msb,
Int.dvd_neg, Int.neg_dvd, toNat_eq, toNat_umod, reduceIte, toNat_ofNat, zero_mod]
<;> norm_cast
<;> norm_cast at h
<;> simp only [dvd_of_mod_eq_zero, h, dvd_iff_mod_eq_zero.mp, reduceIte]
theorem toInt_dvd_toInt_iff_of_msb_true_msb_false {x y : BitVec w} (hx : x.msb = true) (hy : y.msb = false) :
y.toInt x.toInt (-x) % y = 0#w := by
simpa [hx, hy] using toInt_dvd_toInt_iff (x := x) (y := y)
theorem toInt_dvd_toInt_iff_of_msb_false_msb_true {x y : BitVec w} (hx : x.msb = false) (hy : y.msb = true) :
y.toInt x.toInt x % (-y) = 0#w := by
simpa [hx, hy] using toInt_dvd_toInt_iff (x := x) (y := y)
@[simp]
theorem neg_toInt_neg_umod_eq_of_msb_true_msb_true {x y : BitVec w} (hx : x.msb = true) (hy : y.msb = true) :
-(-(-x % -y)).toInt = (-x % -y).toNat := by
rw [neg_toInt_neg]
by_cases h : -x % -y = 0#w
· simp [h]
· rw [msb_neg_umod_neg_of_msb_true_of_msb_true hx hy]
@[simp]
theorem toInt_umod_neg_add {x y : BitVec w} (hymsb : y.msb = true) (hxmsb : x.msb = false) (hdvd : ¬y.toInt x.toInt) :
(x % -y + y).toInt = x.toInt % y.toInt + y.toInt := by
rcases w with _|w ; simp [of_length_zero]
have hypos : 0 < y.toNat := toNat_pos_of_ne_zero (by simp [hymsb])
have hxnonneg := toInt_nonneg_of_msb_false hxmsb
have hynonpos := toInt_neg_of_msb_true hymsb
have hylt : (-y).toNat 2 ^ (w) := toNat_neg_lt_of_msb y hymsb
have hmodlt := Nat.mod_lt x.toNat (y := (-y).toNat)
(by rw [toNat_neg, Nat.mod_eq_of_lt (by omega)]; omega)
simp only [toInt_add]
rw [toInt_umod, toInt_eq_neg_toNat_neg_of_msb_true hymsb, Int.bmod_add_bmod,
Int.bmod_eq_of_le (by omega) (by omega),
toInt_eq_toNat_of_msb hxmsb, Int.emod_neg]
@[simp]
theorem toInt_sub_neg_umod {x y : BitVec w} (hxmsb : x.msb = true) (hymsb : y.msb = false) (hdvd : ¬y.toInt x.toInt) :
(y - -x % y).toInt = x.toInt % y.toInt := by
rcases w with _|w
· simp [of_length_zero]
· have : y.toNat < 2 ^ w := toNat_lt_of_msb_false hymsb
by_cases hyzero : y = 0#(w+1)
· subst hyzero; simp
· simp only [toNat_eq, toNat_ofNat, zero_mod] at hyzero
have hypos : 0 < y.toNat := by omega
simp only [toInt_sub, toInt_eq_toNat_of_msb hymsb, toInt_umod,
Int.sub_bmod_bmod, toInt_eq_neg_toNat_neg_of_msb_true hxmsb, Int.neg_emod]
have hmodlt := Nat.mod_lt (x := (-x).toNat) (y := y.toNat) hypos
rw [Int.bmod_eq_of_le (by omega) (by omega)]
simp only [toInt_eq_toNat_of_msb hymsb, BitVec.toInt_eq_neg_toNat_neg_of_msb_true hxmsb,
Int.dvd_neg] at hdvd
simp only [hdvd, reduceIte, Int.natAbs_cast]
theorem toInt_smod {x y : BitVec w} :
(x.smod y).toInt = x.toInt.fmod y.toInt := by
rcases w with _|w
· decide +revert
· by_cases hyzero : y = 0#(w + 1)
· simp [hyzero]
· rw [smod_eq]
cases hxmsb : x.msb <;> cases hymsb : y.msb
<;> simp only [umod_eq]
· have : 0 < y.toNat := by simp [toNat_eq] at hyzero; omega
have : y.toNat < 2 ^ w := toNat_lt_of_msb_false hymsb
have : x.toNat % y.toNat < y.toNat := Nat.mod_lt x.toNat (by omega)
rw [toInt_umod, Int.fmod_eq_emod_of_nonneg x.toInt (toInt_nonneg_of_msb_false hymsb),
toInt_eq_toNat_of_msb hxmsb, toInt_eq_toNat_of_msb hymsb,
Int.bmod_eq_of_le_mul_two (by omega) (by omega)]
· have := toInt_dvd_toInt_iff_of_msb_false_msb_true hxmsb hymsb
by_cases hx_dvd_y : y.toInt x.toInt
· simp [show x % -y = 0#(w + 1) by simp_all, hx_dvd_y, Int.fmod_eq_zero_of_dvd]
· have hynonpos := toInt_neg_of_msb_true hymsb
simp only [show ¬x % -y = 0#(w + 1) by simp_all, reduceIte,
toInt_umod_neg_add hymsb hxmsb hx_dvd_y, Int.fmod_eq_emod, show ¬0 y.toInt by omega,
hx_dvd_y, _root_.or_self]
· have hynonneg := toInt_nonneg_of_msb_false hymsb
rw [Int.fmod_eq_emod_of_nonneg x.toInt (b := y.toInt) (by omega)]
have hdvd := toInt_dvd_toInt_iff_of_msb_true_msb_false hxmsb hymsb
by_cases hx_dvd_y : y.toInt x.toInt
· simp [show -x % y = 0#(w + 1) by simp_all, hx_dvd_y, Int.emod_eq_zero_of_dvd]
· simp [show ¬-x % y = 0#(w + 1) by simp_all, toInt_sub_neg_umod hxmsb hymsb hx_dvd_y]
· rw [Int.neg_inj, neg_toInt_neg_umod_eq_of_msb_true_msb_true hxmsb hymsb]
simp [BitVec.toInt_eq_neg_toNat_neg_of_msb_true, hxmsb, hymsb,
Int.fmod_eq_emod_of_nonneg _]
/-! ### Lemmas that use bit blasting circuits -/
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by
@@ -1782,7 +1929,7 @@ 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,
simp only [carry, extractLsb'_toNat, shiftRight_zero, ge_iff_le,
decide_eq_decide]
have : 2 ^ i 2^len := by
apply Nat.pow_dvd_pow

View File

@@ -0,0 +1,146 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import all Init.Data.BitVec.Basic
namespace BitVec
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i := rfl
@[simp] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp] theorem getLsbD_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getLsbD x i = false := by
let x, x_lt := x
simp only [getLsbD_ofFin]
apply Nat.testBit_lt_two_pow
have p : 2^w 2^i := Nat.pow_le_pow_right (by omega) ge
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toNat_eq {n} : {x y : BitVec n}, x.toNat = y.toNat x = y
| _, _, _, _, rfl => rfl
theorem eq_of_getLsbD_eq {x y : BitVec w}
(pred : i, i < w x.getLsbD i = y.getLsbD i) : x = y := by
apply eq_of_toNat_eq
apply Nat.eq_of_testBit_eq
intro i
if i_lt : i < w then
exact pred i i_lt
else
have p : i w := Nat.le_of_not_gt i_lt
simp [testBit_toNat, getLsbD_of_ge _ _ p]
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat]
@[ext] theorem eq_of_getElem_eq {x y : BitVec n} :
( i (hi : i < n), x[i] = y[i]) x = y :=
fun h => BitVec.eq_of_getLsbD_eq (h ·)
@[simp] theorem toNat_append (x : BitVec m) (y : BitVec n) :
(x ++ y).toNat = x.toNat <<< n ||| y.toNat :=
rfl
@[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat := by
cases b <;> rfl
@[simp, bitvec_to_nat] theorem toNat_cast (h : w = v) (x : BitVec w) : (x.cast h).toNat = x.toNat := rfl
@[simp, bitvec_to_nat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofNatLT (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem toNat_cons (b : Bool) (x : BitVec w) :
(cons b x).toNat = (b.toNat <<< w) ||| x.toNat := by
let x, _ := x
simp only [cons, toNat_cast, toNat_append, toNat_ofBool, toNat_ofFin]
theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
(cons b x)[i] = if h : i = n then b else x[i] := by
simp only [getElem_eq_testBit_toNat, toNat_cons, Nat.testBit_or]
rw [Nat.testBit_shiftLeft]
rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
· have p1 : ¬(n i) := by omega
have p2 : i n := by omega
simp [p1, p2]
· simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_true, Nat.sub_self, Nat.testBit_zero,
Bool.true_and, testBit_toNat, getLsbD_of_ge, Bool.or_false]
cases b <;> trivial
· have p1 : i n := by omega
have p2 : i - n 0 := by omega
simp [p1, p2, Nat.testBit_bool_to_nat]
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_right (by trivial : 0 < 2) le)
@[simp, bitvec_to_nat] theorem toNat_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toNat = x.toNat := by
simp only [setWidth', toNat_ofNatLT]
@[simp, bitvec_to_nat] theorem toNat_setWidth (i : Nat) (x : BitVec n) :
BitVec.toNat (setWidth i x) = x.toNat % 2^i := by
let x, lt_n := x
simp only [setWidth]
if n_le_i : n i then
have x_lt_two_i : x < 2 ^ i := lt_two_pow_of_le lt_n n_le_i
simp [n_le_i, Nat.mod_eq_of_lt, x_lt_two_i]
else
simp [n_le_i, toNat_ofNat]
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = setWidth m x := by
apply eq_of_toNat_eq
simp only [toNat_ofNat, toNat_setWidth]
theorem getElem_setWidth' (x : BitVec w) (i : Nat) (h : w v) (hi : i < v) :
(setWidth' h x)[i] = x.getLsbD i := by
rw [getElem_eq_testBit_toNat, toNat_setWidth', getLsbD]
@[simp]
theorem getElem_setWidth (m : Nat) (x : BitVec n) (i : Nat) (h : i < m) :
(setWidth m x)[i] = x.getLsbD i := by
rw [setWidth]
split
· rw [getElem_setWidth']
· simp only [ofNat_toNat, getElem_eq_testBit_toNat, toNat_setWidth, Nat.testBit_mod_two_pow,
getLsbD, Bool.and_eq_right_iff_imp, decide_eq_true_eq]
omega
@[simp] theorem cons_msb_setWidth (x : BitVec (w+1)) : (cons x.msb (x.setWidth w)) = x := by
ext i
simp only [getElem_cons]
split <;> rename_i h
· simp [BitVec.msb, getMsbD, h]
· by_cases h' : i < w
· simp_all only [getElem_setWidth, getLsbD_eq_getElem]
· omega
@[simp, bitvec_to_nat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
simp [Neg.neg, BitVec.neg]
@[simp] theorem setWidth_neg_of_le {x : BitVec v} (h : w v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
apply BitVec.eq_of_toNat_eq
simp only [toNat_setWidth, toNat_neg]
rw [Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
rw [Nat.mod_eq_mod_iff]
rw [Nat.mod_def]
refine 1 + x.toNat / 2^w, 2^(v-w), ?_
rw [ Nat.pow_add]
have : v - w + w = v := by omega
rw [this]
rw [Nat.add_mul, Nat.one_mul, Nat.mul_comm (2^w)]
have sub_sub : (a : Nat) {b c : Nat} (h : c b), a - (b - c) = a + c - b := by omega
rw [sub_sub _ (Nat.div_mul_le_self x.toNat (2 ^ w))]
have : x.toNat / 2 ^ w * 2 ^ w x.toNat := Nat.div_mul_le_self x.toNat (2 ^ w)
have : x.toNat < 2 ^w x.toNat - 2 ^ w < x.toNat / 2 ^ w * 2 ^ w := by
have := Nat.lt_div_mul_add (a := x.toNat) (b := 2 ^ w) (Nat.two_pow_pos w)
omega
omega
end BitVec

View File

@@ -0,0 +1,79 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import Init.Data.BitVec.Bootstrap
set_option linter.missingDocs true
namespace BitVec
/-! ### Decidable quantifiers -/
theorem forall_zero_iff {P : BitVec 0 Prop} :
( v, P v) P 0#0 := by
constructor
· intro h
apply h
· intro h v
obtain (rfl : v = 0#0) := (by ext i )
apply h
theorem forall_cons_iff {P : BitVec (n + 1) Prop} :
( v : BitVec (n + 1), P v) ( (x : Bool) (v : BitVec n), P (v.cons x)) := by
constructor
· intro h _ _
apply h
· intro h v
have w : v = (v.setWidth n).cons v.msb := by simp only [cons_msb_setWidth]
rw [w]
apply h
instance instDecidableForallBitVecZero (P : BitVec 0 Prop) :
[Decidable (P 0#0)], Decidable ( v, P v)
| .isTrue h => .isTrue fun v => by
obtain (rfl : v = 0#0) := (by ext i )
exact h
| .isFalse h => .isFalse (fun w => h (w _))
instance instDecidableForallBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff' ( x (v : BitVec n), P (v.cons x)) forall_cons_iff
instance instDecidableExistsBitVecZero (P : BitVec 0 Prop) [Decidable (P 0#0)] :
Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
instance instDecidableExistsBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), ¬ P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow,
and you should use `bv_decide` if possible.
-/
instance instDecidableForallBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| n + 1, _, _ =>
have := instDecidableForallBitVec n
inferInstance
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow.
-/
instance instDecidableExistsBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| _ + 1, _, _ => inferInstance
end BitVec

View File

@@ -82,9 +82,9 @@ theorem iunfoldr_getLsbD' {f : Fin w → αα × Bool} (state : Nat → α)
simp only [getLsbD_cons]
have hj2 : j.val w := by simp
cases (Nat.lt_or_eq_of_le (Nat.lt_succ.mp i.isLt)) with
| inl h3 => simp [if_neg, (Nat.ne_of_lt h3)]
| inl h3 => simp [(Nat.ne_of_lt h3)]
exact (ih hj2).1 i.val, h3
| inr h3 => simp [h3, if_pos]
| inr h3 => simp [h3]
cases (Nat.eq_zero_or_pos j.val) with
| inl hj3 => congr
rw [ (ih hj2).2]

File diff suppressed because it is too large Load Diff

View File

@@ -455,7 +455,7 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
/--
Converts `true` to `1` and `false` to `0`.
-/
def toInt (b : Bool) : Int := cond b 1 0
@[expose] def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem toInt_false : false.toInt = 0 := rfl
@@ -488,7 +488,7 @@ def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem ite_eq_true_else_eq_false {q : Prop} :
(if b = true then q else b = false) (b = true q) := by
cases b <;> simp [not_eq_self]
cases b <;> simp
/-
`not_ite_eq_true_eq_true` and related theorems below are added for

View File

@@ -205,7 +205,7 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
@[inline]
def foldl {β : Type v} (f : β UInt8 β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM f init start stop
Id.run <| as.foldlM (pure <| f · ·) init start stop
/-- Iterator over the bytes (`UInt8`) of a `ByteArray`.

View File

@@ -46,15 +46,12 @@ Returns `a` modulo `n` as a `Fin n`.
The assumption `NeZero n` ensures that `Fin n` is nonempty.
-/
@[expose] protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
@[expose] protected def ofNat (n : Nat) [NeZero n] (a : Nat) : Fin n :=
a % n, Nat.mod_lt _ (pos_of_neZero n)
/--
Returns `a` modulo `n + 1` as a `Fin n.succ`.
-/
@[deprecated Fin.ofNat' (since := "2024-11-27")]
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
@[deprecated Fin.ofNat (since := "2025-05-28")]
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
Fin.ofNat n a
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
@@ -84,7 +81,7 @@ Examples:
* `(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
| a, h, b, _ => (a + b) % n, by exact mlt h
/--
Multiplication modulo `n`, usually invoked via the `*` operator.
@@ -95,7 +92,7 @@ Examples:
* `(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
| a, h, b, _ => (a * b) % n, by exact mlt h
/--
Subtraction modulo `n`, usually invoked via the `-` operator.
@@ -122,7 +119,7 @@ protected def sub : Fin n → Fin n → Fin n
using recursion on the second argument.
See issue #4413.
-/
| a, h, b, _ => ((n - b) + a) % n, mlt h
| a, h, b, _ => ((n - b) + a) % n, by exact mlt h
/-!
Remark: land/lor can be defined without using (% n), but
@@ -164,19 +161,19 @@ def modn : Fin n → Nat → Fin n
Bitwise and.
-/
def land : Fin n Fin n Fin n
| a, h, b, _ => (Nat.land a b) % n, mlt h
| a, h, b, _ => (Nat.land a b) % n, by exact mlt h
/--
Bitwise or.
-/
def lor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.lor a b) % n, mlt h
| a, h, b, _ => (Nat.lor a b) % n, by exact mlt h
/--
Bitwise xor (“exclusive or”).
-/
def xor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.xor a b) % n, mlt h
| a, h, b, _ => (Nat.xor a b) % n, by exact mlt h
/--
Bitwise left shift of bounded numbers, with wraparound on overflow.
@@ -187,7 +184,7 @@ Examples:
* `(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
| a, h, b, _ => (a <<< b) % n, by exact mlt h
/--
Bitwise right shift of bounded numbers.
@@ -201,7 +198,7 @@ Examples:
* `(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
| a, h, b, _ => (a >>> b) % n, by exact mlt h
instance : Add (Fin n) where
add := Fin.add
@@ -230,7 +227,7 @@ instance : ShiftRight (Fin n) where
shiftRight := Fin.shiftRight
instance instOfNat {n : Nat} [NeZero n] {i : Nat} : OfNat (Fin n) i where
ofNat := Fin.ofNat' n i
ofNat := Fin.ofNat n i
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
protected theorem pos (i : Fin n) : 0 < n :=

View File

@@ -100,6 +100,11 @@ Fin.foldrM n f xₙ = do
/-! ### foldlM -/
@[congr] theorem foldlM_congr [Monad m] {n k : Nat} (w : n = k) (f : α Fin n m α) :
foldlM n f = foldlM k (fun x i => f x (i.cast w.symm)) := by
subst w
rfl
theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
foldlM.loop n f x i = f x i, h >>= (foldlM.loop n f . (i+1)) := by
rw [foldlM.loop, dif_pos h]
@@ -120,14 +125,49 @@ theorem foldlM_loop [Monad m] (f : α → Fin (n+1) → m α) (x) (h : i < n+1)
rw [foldlM_loop_eq, foldlM_loop_eq]
termination_by n - i
@[simp] theorem foldlM_zero [Monad m] (f : α Fin 0 m α) (x) : foldlM 0 f x = pure x :=
foldlM_loop_eq ..
@[simp] theorem foldlM_zero [Monad m] (f : α Fin 0 m α) : foldlM 0 f = pure := by
funext x
exact foldlM_loop_eq ..
theorem foldlM_succ [Monad m] (f : α Fin (n+1) m α) (x) :
foldlM (n+1) f x = f x 0 >>= foldlM n (fun x j => f x j.succ) := foldlM_loop ..
theorem foldlM_succ [Monad m] (f : α Fin (n+1) m α) :
foldlM (n+1) f = fun x => f x 0 >>= foldlM n (fun x j => f x j.succ) := by
funext x
exact foldlM_loop ..
/-- Variant of `foldlM_succ` that splits off `Fin.last n` rather than `0`. -/
theorem foldlM_succ_last [Monad m] [LawfulMonad m] (f : α Fin (n+1) m α) :
foldlM (n+1) f = fun x => foldlM n (fun x j => f x j.castSucc) x >>= (f · (Fin.last n)) := by
funext x
induction n generalizing x with
| zero =>
simp [foldlM_succ]
| succ n ih =>
rw [foldlM_succ]
conv => rhs; rw [foldlM_succ]
simp only [castSucc_zero, castSucc_succ, bind_assoc]
congr 1
funext x
rw [ih]
simp
theorem foldlM_add [Monad m] [LawfulMonad m] (f : α Fin (n + k) m α) :
foldlM (n + k) f =
fun x => foldlM n (fun x i => f x (i.castLE (Nat.le_add_right n k))) x >>= foldlM k (fun x i => f x (i.natAdd n)) := by
induction k with
| zero =>
funext x
simp
| succ k ih =>
funext x
simp [foldlM_succ_last, Nat.add_assoc, ih]
/-! ### foldrM -/
@[congr] theorem foldrM_congr [Monad m] {n k : Nat} (w : n = k) (f : Fin n α m α) :
foldrM n f = foldrM k (fun i => f (i.cast w.symm)) := by
subst w
rfl
theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
foldrM.loop n f 0, Nat.zero_le _ x = pure x := by
rw [foldrM.loop]
@@ -143,21 +183,47 @@ 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
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
rfl
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..
@[simp] theorem foldrM_zero [Monad m] (f : Fin 0 α m α) (x) : foldrM 0 f x = pure x :=
foldrM_loop_zero ..
@[simp] theorem foldrM_zero [Monad m] (f : Fin 0 α m α) : foldrM 0 f = pure := by
funext x
exact foldrM_loop_zero ..
theorem foldrM_succ [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) :
foldrM (n+1) f x = foldrM n (fun i => f i.succ) x >>= f 0 := foldrM_loop ..
theorem foldrM_succ [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) :
foldrM (n+1) f = fun x => foldrM n (fun i => f i.succ) x >>= f 0 := by
funext x
exact foldrM_loop ..
theorem foldrM_succ_last [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) :
foldrM (n+1) f = fun x => f (Fin.last n) x >>= foldrM n (fun i => f i.castSucc) := by
funext x
induction n generalizing x with
| zero => simp [foldrM_succ]
| succ n ih =>
rw [foldrM_succ]
conv => rhs; rw [foldrM_succ]
simp [ih]
theorem foldrM_add [Monad m] [LawfulMonad m] (f : Fin (n + k) α m α) :
foldrM (n + k) f =
fun x => foldrM k (fun i => f (i.natAdd n)) x >>= foldrM n (fun i => f (i.castLE (Nat.le_add_right n k))) := by
induction k with
| zero =>
simp
| succ k ih =>
funext x
simp [foldrM_succ_last, Nat.add_assoc, ih]
/-! ### foldl -/
@[congr] theorem foldl_congr {n k : Nat} (w : n = k) (f : α Fin n α) :
foldl n f = foldl k (fun x i => f x (i.cast w.symm)) := by
subst w
rfl
theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
foldl.loop n f x i = foldl.loop n f (f x i, h) (i+1) := by
rw [foldl.loop, dif_pos h]
@@ -186,15 +252,35 @@ theorem foldl_succ_last (f : α → Fin (n+1) → α) (x) :
foldl (n+1) f x = f (foldl n (f · ·.castSucc) x) (last n) := by
rw [foldl_succ]
induction n generalizing x with
| zero => simp [foldl_succ, Fin.last]
| succ n ih => rw [foldl_succ, ih (f · ·.succ), foldl_succ]; simp [succ_castSucc]
| zero => simp [Fin.last]
| succ n ih => rw [foldl_succ, ih (f · ·.succ), foldl_succ]; simp
theorem foldl_add (f : α Fin (n + m) α) (x) :
foldl (n + m) f x =
foldl m (fun x i => f x (i.natAdd n))
(foldl n (fun x i => f x (i.castLE (Nat.le_add_right n m))) x):= by
induction m with
| zero => simp
| succ m ih => simp [foldl_succ_last, ih, Nat.add_assoc]
theorem foldl_eq_foldlM (f : α Fin n α) (x) :
foldl n f x = foldlM (m:=Id) n f x := by
foldl n f x = (foldlM (m := Id) n (pure <| f · ·) x).run := by
induction n generalizing x <;> simp [foldl_succ, foldlM_succ, *]
-- This is not marked `@[simp]` as it would match on every occurrence of `foldlM`.
theorem foldlM_pure [Monad m] [LawfulMonad m] {n} {f : α Fin n α} :
foldlM n (fun x i => pure (f x i)) x = (pure (foldl n f x) : m α) := by
induction n generalizing x with
| zero => simp
| succ n ih => simp [foldlM_succ, foldl_succ, ih]
/-! ### foldr -/
@[congr] theorem foldr_congr {n k : Nat} (w : n = k) (f : Fin n α α) :
foldr n f = foldr k (fun i => f (i.cast w.symm)) := by
subst w
rfl
theorem foldr_loop_zero (f : Fin n α α) (x) :
foldr.loop n f 0 (Nat.zero_le _) x = x := by
rw [foldr.loop]
@@ -220,10 +306,18 @@ theorem foldr_succ_last (f : Fin (n+1) → αα) (x) :
foldr (n+1) f x = foldr n (f ·.castSucc) (f (last n) x) := by
induction n generalizing x with
| zero => simp [foldr_succ, Fin.last]
| succ n ih => rw [foldr_succ, ih (f ·.succ), foldr_succ]; simp [succ_castSucc]
| succ n ih => rw [foldr_succ, ih (f ·.succ), foldr_succ]; simp
theorem foldr_add (f : Fin (n + m) α α) (x) :
foldr (n + m) f x =
foldr n (fun i => f (i.castLE (Nat.le_add_right n m)))
(foldr m (fun i => f (i.natAdd n)) x) := by
induction m generalizing x with
| zero => simp
| succ m ih => simp [foldr_succ_last, ih, Nat.add_assoc]
theorem foldr_eq_foldrM (f : Fin n α α) (x) :
foldr n f x = foldrM (m:=Id) n f x := by
foldr n f x = (foldrM (m := Id) n (pure <| f · ·) x).run := by
induction n <;> simp [foldr_succ, foldrM_succ, *]
theorem foldl_rev (f : Fin n α α) (x) :
@@ -238,4 +332,11 @@ theorem foldr_rev (f : α → Fin n → α) (x) :
| zero => simp
| succ n ih => rw [foldl_succ_last, foldr_succ, ih]; simp [rev_succ]
-- This is not marked `@[simp]` as it would match on every occurrence of `foldrM`.
theorem foldrM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n α α} :
foldrM n (fun i x => pure (f i x)) x = (pure (foldr n f x) : m α) := by
induction n generalizing x with
| zero => simp
| succ n ih => simp [foldrM_succ, foldr_succ, ih]
end Fin

View File

@@ -15,10 +15,9 @@ import Init.Omega
namespace Fin
@[simp] theorem ofNat'_zero (n : Nat) [NeZero n] : Fin.ofNat' n 0 = 0 := rfl
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[deprecated Fin.pos (since := "2024-11-11")]
theorem size_pos (i : Fin n) : 0 < n := i.pos
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a % m) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
@@ -29,8 +28,6 @@ theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b) + a) % n) (Nat.mod_lt _
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@[deprecated pos' (since := "2024-11-11")] abbrev size_pos' := @pos'
@[simp] theorem is_lt (a : Fin n) : (a : Nat) < n := a.2
theorem pos_iff_nonempty {n : Nat} : 0 < n Nonempty (Fin n) :=
@@ -66,19 +63,25 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
0 = (a, ha : Fin n) a = 0 := by
simp [eq_comm]
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl
@[simp] theorem val_ofNat (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat n a).val = a % n := rfl
@[simp] theorem ofNat'_self {n : Nat} [NeZero n] : Fin.ofNat' n n = 0 := by
@[deprecated val_ofNat (since := "2025-05-28")] abbrev val_ofNat' := @val_ofNat
@[simp] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
ext
simp
congr
@[simp] theorem ofNat'_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat' n x) = x := by
@[deprecated ofNat_self (since := "2025-05-28")] abbrev ofNat'_self := @ofNat_self
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x) = x := by
ext
rw [val_ofNat', Nat.mod_eq_of_lt]
rw [val_ofNat, Nat.mod_eq_of_lt]
exact x.2
@[deprecated ofNat_val_eq_self (since := "2025-05-28")] abbrev ofNat'_val_eq_self := @ofNat_val_eq_self
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
rfl
@@ -99,20 +102,55 @@ theorem dite_val {n : Nat} {c : Prop} [Decidable c] {x y : Fin n} :
(if c then x else y).val = if c then x.val else y.val := by
by_cases c <;> simp [*]
instance (n : Nat) [NeZero n] : NatCast (Fin n) where
natCast a := Fin.ofNat' n a
namespace NatCast
/--
This is not a global instance, but may be activated locally via `open Fin.NatCast in ...`.
This is not an instance because the `binop%` elaborator assumes that
there are no non-trivial coercion loops,
but this introduces a coercion from `Nat` to `Fin n` and back.
Non-trivial loops lead to undesirable and counterintuitive elaboration behavior.
For example, for `x : Fin k` and `n : Nat`,
it causes `x < n` to be elaborated as `x < ↑n` rather than `↑x < n`,
silently introducing wraparound arithmetic.
Note: as of 2025-06-03, Mathlib has such a coercion for `Fin n` anyway!
-/
@[expose]
def instNatCast (n : Nat) [NeZero n] : NatCast (Fin n) where
natCast a := Fin.ofNat n a
attribute [scoped instance] instNatCast
end NatCast
@[expose]
def intCast [NeZero n] (a : Int) : Fin n :=
if 0 a then
Fin.ofNat' n a.natAbs
Fin.ofNat n a.natAbs
else
- Fin.ofNat' n a.natAbs
- Fin.ofNat n a.natAbs
instance (n : Nat) [NeZero n] : IntCast (Fin n) where
namespace IntCast
/--
This is not a global instance, but may be activated locally via `open Fin.IntCast in ...`.
See the doc-string for `Fin.NatCast.instNatCast` for more details.
-/
@[expose]
def instIntCast (n : Nat) [NeZero n] : IntCast (Fin n) where
intCast := Fin.intCast
attribute [scoped instance] instIntCast
end IntCast
open IntCast in
theorem intCast_def {n : Nat} [NeZero n] (x : Int) :
(x : Fin n) = if 0 x then Fin.ofNat' n x.natAbs else -Fin.ofNat' n x.natAbs := rfl
(x : Fin n) = if 0 x then Fin.ofNat n x.natAbs else -Fin.ofNat n x.natAbs := rfl
/-! ### order -/
@@ -343,7 +381,7 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
@[simp] theorem val_succ (j : Fin n) : (j.succ : Nat) = j + 1 := rfl
@[simp] theorem succ_pos (a : Fin n) : (0 : Fin (n + 1)) < a.succ := by
simp [Fin.lt_def, Nat.succ_pos]
simp [Fin.lt_def]
@[simp] theorem succ_le_succ_iff {a b : Fin n} : a.succ b.succ a b := Nat.succ_le_succ_iff
@@ -376,7 +414,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
simp only [lt_def, val_add, val_last, Fin.ext_iff]
let k, hk := k
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
| .inl h => cases h; simp [Nat.succ_pos]
| .inl h => cases h; simp
| .inr hk' => simp [Nat.ne_of_lt hk', Nat.mod_eq_of_lt (Nat.succ_lt_succ hk'), Nat.le_succ]
@[simp] theorem add_one_le_iff {n : Nat} : {k : Fin (n + 1)}, k + 1 k k = last _ := by
@@ -388,7 +426,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
intro (k : Fin (n+2))
rw [ add_one_lt_iff, lt_def, le_def, Nat.lt_iff_le_and_ne, and_iff_left]
rw [val_add_one]
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
split <;> simp [*, Nat.ne_of_gt (Nat.lt_succ_self _)]
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n k k = last n := by
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
@@ -646,6 +684,20 @@ theorem rev_castSucc (k : Fin n) : rev (castSucc k) = succ (rev k) := k.rev_cast
theorem rev_succ (k : Fin n) : rev (succ k) = castSucc (rev k) := k.rev_addNat 1
@[simp, grind _=_]
theorem castSucc_succ (i : Fin n) : i.succ.castSucc = i.castSucc.succ := rfl
@[simp, grind =]
theorem castLE_refl (h : n n) (i : Fin n) : i.castLE h = i := rfl
@[simp, grind =]
theorem castSucc_castLE (h : n m) (i : Fin n) :
(i.castLE h).castSucc = i.castLE (by omega) := rfl
@[simp, grind =]
theorem castSucc_natAdd (n : Nat) (i : Fin k) :
(i.natAdd n).castSucc = (i.castSucc).natAdd n := rfl
/-! ### pred -/
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@@ -686,7 +738,7 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
{a b : Fin (n + 1)} {ha : a 0} {hb : b 0}, a.pred ha = b.pred hb a = b
| 0, _, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
| i + 1, _, 0, _, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff]
@[simp] theorem pred_one {n : Nat} :
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
@@ -783,7 +835,7 @@ parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.rev
version that starts at the greatest value instead of `0`.
-/
-- FIXME: Performance review
@[elab_as_elim] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[elab_as_elim, expose] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) :
i : Fin (n + 1), motive i
| i, hi => go i hi
@@ -825,7 +877,7 @@ The two cases are:
The corresponding induction principle is `Fin.induction`.
-/
@[elab_as_elim] def cases {motive : Fin (n + 1) Sort _}
@[elab_as_elim, expose] def cases {motive : Fin (n + 1) Sort _}
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@@ -951,30 +1003,38 @@ theorem val_ne_zero_iff [NeZero n] {a : Fin n} : a.val ≠ 0 ↔ a ≠ 0 :=
/-! ### add -/
theorem ofNat'_add [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x + y = Fin.ofNat' n (x + y.val) := by
theorem ofNat_add [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat n x + y = Fin.ofNat n (x + y.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
simp [Fin.ofNat, Fin.add_def]
theorem add_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x + Fin.ofNat' n y = Fin.ofNat' n (x.val + y) := by
@[deprecated ofNat_add (since := "2025-05-28")] abbrev ofNat_add' := @ofNat_add
theorem add_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x + Fin.ofNat n y = Fin.ofNat n (x.val + y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
simp [Fin.ofNat, Fin.add_def]
@[deprecated add_ofNat (since := "2025-05-28")] abbrev add_ofNat' := @add_ofNat
/-! ### sub -/
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
theorem ofNat'_sub [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x - y = Fin.ofNat' n ((n - y.val) + x) := by
theorem ofNat_sub [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat n x - y = Fin.ofNat n ((n - y.val) + x) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]
simp [Fin.ofNat, Fin.sub_def]
theorem sub_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x - Fin.ofNat' n y = Fin.ofNat' n ((n - y % n) + x.val) := by
@[deprecated ofNat_sub (since := "2025-05-28")] abbrev ofNat_sub' := @ofNat_sub
theorem sub_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x - Fin.ofNat n y = Fin.ofNat n ((n - y % n) + x.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]
simp [Fin.ofNat, Fin.sub_def]
@[deprecated sub_ofNat (since := "2025-05-28")] abbrev sub_ofNat' := @sub_ofNat
@[simp] protected theorem sub_self [NeZero n] {x : Fin n} : x - x = 0 := by
ext
@@ -1019,17 +1079,32 @@ theorem val_neg {n : Nat} [NeZero n] (x : Fin n) :
have := Fin.val_ne_zero_iff.mpr h
omega
protected theorem sub_eq_add_neg {n : Nat} (x y : Fin n) : x - y = x + -y := by
by_cases h : n = 0
· subst h
apply elim0 x
· replace h : NeZero n := h
ext
rw [Fin.coe_sub, Fin.val_add, val_neg]
split
· simp_all
· simp [Nat.add_comm]
/-! ### mul -/
theorem ofNat'_mul [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x * y = Fin.ofNat' n (x * y.val) := by
theorem ofNat_mul [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat n x * y = Fin.ofNat n (x * y.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.mul_def]
simp [Fin.ofNat, Fin.mul_def]
theorem mul_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x * Fin.ofNat' n y = Fin.ofNat' n (x.val * y) := by
@[deprecated ofNat_mul (since := "2025-05-28")] abbrev ofNat_mul' := @ofNat_mul
theorem mul_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x * Fin.ofNat n y = Fin.ofNat n (x.val * y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.mul_def]
simp [Fin.ofNat, Fin.mul_def]
@[deprecated mul_ofNat (since := "2025-05-28")] abbrev mul_ofNat' := @mul_ofNat
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl
@@ -1042,7 +1117,7 @@ protected theorem mul_one [i : NeZero n] (k : Fin n) : k * 1 = k := by
| n + 1, _ =>
match n with
| 0 => exact Subsingleton.elim (α := Fin 1) ..
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
| n+1 => simp [mul_def, Nat.mod_eq_of_lt (is_lt k)]
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]

View File

@@ -161,8 +161,7 @@ This function does not reduce in the kernel. It is compiled to the C inequality
match a, b with
| a, b => floatSpec.decLe a b
instance floatDecLt (a b : Float) : Decidable (a < b) := Float.decLt a b
instance floatDecLe (a b : Float) : Decidable (a b) := Float.decLe a b
attribute [instance] Float.decLt Float.decLe
/--
Converts a floating-point number to a string.

View File

@@ -145,7 +145,7 @@ Compares two floating point numbers for strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLt"] opaque Float32.decLt (a b : Float32) : Decidable (a < b) :=
@[extern "lean_float32_decLt", instance] opaque Float32.decLt (a b : Float32) : Decidable (a < b) :=
match a, b with
| a, b => float32Spec.decLt a b
@@ -154,13 +154,10 @@ Compares two floating point numbers for non-strict inequality.
This function does not reduce in the kernel. It is compiled to the C inequality operator.
-/
@[extern "lean_float32_decLe"] opaque Float32.decLe (a b : Float32) : Decidable (a b) :=
@[extern "lean_float32_decLe", instance] opaque Float32.decLe (a b : Float32) : Decidable (a b) :=
match a, b with
| a, b => float32Spec.decLe a b
instance float32DecLt (a b : Float32) : Decidable (a < b) := Float32.decLt a b
instance float32DecLe (a b : Float32) : Decidable (a b) := Float32.decLe a b
/--
Converts a floating-point number to a string.

View File

@@ -165,7 +165,7 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → Float →
@[inline]
def foldl {β : Type v} (f : β Float β) (init : β) (as : FloatArray) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM f init start stop
Id.run <| as.foldlM (pure <| f · ·) init start stop
end FloatArray

View File

@@ -142,17 +142,36 @@ private structure WorkItem where
indent : Int
activeTags : Nat
/--
A directive indicating whether a given work group is able to be flattened.
- `allow` indicates that the group is allowed to be flattened; its argument is `true` if
there is sufficient space for it to be flattened (and so it should be), or `false` if not.
- `disallow` means that this group should not be flattened irrespective of space concerns.
This is used at levels of a `Format` outside of any flattening groups. It is necessary to track
this so that, after a hard line break, we know whether to try to flatten the next line.
-/
inductive FlattenAllowability where
| allow (fits : Bool)
| disallow
deriving BEq
/-- Whether the given directive indicates that flattening should occur. -/
def FlattenAllowability.shouldFlatten : FlattenAllowability Bool
| allow true => true
| _ => false
private structure WorkGroup where
flatten : Bool
flb : FlattenBehavior
items : List WorkItem
fla : FlattenAllowability
flb : FlattenBehavior
items : List WorkItem
private partial def spaceUptoLine' : List WorkGroup Nat Nat SpaceResult
| [], _, _ => {}
| { items := [], .. }::gs, col, w => spaceUptoLine' gs col w
| g@{ items := i::is, .. }::gs, col, w =>
merge w
(spaceUptoLine i.f g.flatten (w + col - i.indent) w)
(spaceUptoLine i.f g.fla.shouldFlatten (w + col - i.indent) w)
(spaceUptoLine' ({ g with items := is }::gs) col)
/-- A monad in which we can pretty-print `Format` objects. -/
@@ -169,11 +188,11 @@ open MonadPrettyFormat
private def pushGroup (flb : FlattenBehavior) (items : List WorkItem) (gs : List WorkGroup) (w : Nat) [Monad m] [MonadPrettyFormat m] : m (List WorkGroup) := do
let k currColumn
-- Flatten group if it + the remainder (gs) fits in the remaining space. For `fill`, measure only up to the next (ungrouped) line break.
let g := { flatten := flb == FlattenBehavior.allOrNone, flb := flb, items := items : WorkGroup }
let g := { fla := .allow (flb == FlattenBehavior.allOrNone), flb := flb, items := items : WorkGroup }
let r := spaceUptoLine' [g] k (w-k)
let r' := merge (w-k) r (spaceUptoLine' gs k)
-- Prevent flattening if any item contains a hard line break, except within `fill` if it is ungrouped (=> unflattened)
return { g with flatten := !r.foundFlattenedHardLine && r'.space <= w-k }::gs
return { g with fla := .allow (!r.foundFlattenedHardLine && r'.space <= w-k) }::gs
private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGroup m Unit
| [] => pure ()
@@ -200,11 +219,15 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
pushNewline i.indent.toNat
let is := { i with f := text (s.extract (s.next p) s.endPos) }::is
-- after a hard line break, re-evaluate whether to flatten the remaining group
pushGroup g.flb is gs w >>= be w
-- note that we shouldn't start flattening after a hard break outside a group
if g.fla == .disallow then
be w (gs' is)
else
pushGroup g.flb is gs w >>= be w
| line =>
match g.flb with
| FlattenBehavior.allOrNone =>
if g.flatten then
if g.fla.shouldFlatten then
-- flatten line = text " "
pushOutput " "
endTags i.activeTags
@@ -220,10 +243,10 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
endTags i.activeTags
pushGroup FlattenBehavior.fill is gs w >>= be w
-- if preceding fill item fit in a single line, try to fit next one too
if g.flatten then
if g.fla.shouldFlatten then
let gs'@(g'::_) pushGroup FlattenBehavior.fill is gs (w - " ".length)
| panic "unreachable"
if g'.flatten then
if g'.fla.shouldFlatten then
pushOutput " "
endTags i.activeTags
be w gs' -- TODO: use `return`
@@ -232,7 +255,7 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
else
breakHere
| align force =>
if g.flatten && !force then
if g.fla.shouldFlatten && !force then
-- flatten (align false) = nil
endTags i.activeTags
be w (gs' is)
@@ -247,7 +270,7 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
endTags i.activeTags
be w (gs' is)
| group f flb =>
if g.flatten then
if g.fla.shouldFlatten then
-- flatten (group f) = flatten f
be w (gs' ({ i with f }::is))
else
@@ -256,7 +279,7 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
/-- Render the given `f : Format` with a line width of `w`.
`indent` is the starting amount to indent each line by. -/
def prettyM (f : Format) (w : Nat) (indent : Nat := 0) [Monad m] [MonadPrettyFormat m] : m Unit :=
be w [{ flb := FlattenBehavior.allOrNone, flatten := false, items := [{ f := f, indent, activeTags := 0 }]}]
be w [{ flb := FlattenBehavior.allOrNone, fla := .disallow, items := [{ f := f, indent, activeTags := 0 }]}]
/-- Create a format `l ++ f ++ r` with a flatten group.
FlattenBehaviour is `allOrNone`; for `fill` use `bracketFill`. -/
@@ -294,7 +317,7 @@ private structure State where
out : String := ""
column : Nat := 0
instance : MonadPrettyFormat (StateM State) where
private instance : MonadPrettyFormat (StateM State) where
-- We avoid a structure instance update, and write these functions using pattern matching because of issue #316
pushOutput s := modify fun out, col => out ++ s, col + s.length
pushNewline indent := modify fun out, _ => out ++ "\n".pushn ' ' indent, indent

View File

@@ -31,19 +31,19 @@ Examples:
@[inline, expose]
def uncurry : (α β φ) α × β φ := fun f a => f a.1 a.2
@[simp]
@[simp, grind]
theorem curry_uncurry (f : α β φ) : curry (uncurry f) = f :=
rfl
@[simp]
@[simp, grind]
theorem uncurry_curry (f : α × β φ) : uncurry (curry f) = f :=
funext fun _a, _b => rfl
@[simp]
@[simp, grind]
theorem uncurry_apply_pair {α β γ} (f : α β γ) (x : α) (y : β) : uncurry f (x, y) = f x y :=
rfl
@[simp]
@[simp, grind]
theorem curry_apply {α β γ} (f : α × β γ) (x : α) (y : β) : curry f x y = f (x, y) :=
rfl

View File

@@ -57,9 +57,6 @@ instance : Hashable UInt64 where
instance : Hashable USize where
hash n := n.toUInt64
instance : Hashable ByteArray where
hash as := as.foldl (fun r a => mixHash r (hash a)) 7
instance : Hashable (Fin n) where
hash v := v.val.toUInt64

View File

@@ -269,7 +269,7 @@ set_option bootstrap.genMatcherCode false in
Implemented by efficient native code. -/
@[extern "lean_int_dec_nonneg"]
private def decNonneg (m : @& Int) : Decidable (NonNeg m) :=
def decNonneg (m : @& Int) : Decidable (NonNeg m) :=
match m with
| ofNat m => isTrue <| NonNeg.mk m
| -[_ +1] => isFalse <| fun h => nomatch h

View File

@@ -41,6 +41,7 @@ Examples:
* `(-0b1000 : Int) >>> 1 = -0b0100`
* `(-0b0111 : Int) >>> 1 = -0b0100`
-/
@[expose]
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)
| Int.negSucc n, s => Int.negSucc (n >>> s)

View File

@@ -37,7 +37,7 @@ theorem compare_eq_ite_le (a b : Int) :
· next hlt => simp [Int.le_of_lt hlt, Int.not_le.2 hlt]
· next hge =>
split
· next hgt => simp [Int.le_of_lt hgt, Int.not_le.2 hgt]
· next hgt => simp [Int.not_le.2 hgt]
· next hle => simp [Int.not_lt.1 hge, Int.not_lt.1 hle]
protected theorem compare_swap (a b : Int) : (compare a b).swap = compare b a := by

View File

@@ -3,7 +3,6 @@ Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro
-/
module
prelude
@@ -57,7 +56,7 @@ protected theorem dvd_trans : ∀ {a b c : Int}, a b → b c → a c
@[simp] protected theorem dvd_neg {a b : Int} : a -b a b := by
constructor <;> exact fun k, e =>
-k, by simp [ e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
-k, by simp [ e, Int.mul_neg, Int.neg_neg]
@[simp] theorem natAbs_dvd_natAbs {a b : Int} : natAbs a natAbs b a b := by
refine fun k, hk => ?_, fun k, hk => natAbs k, hk.symm natAbs_mul a k
@@ -99,7 +98,7 @@ theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := natCast_emod m n
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| ofNat m, -[n+1] => by
show (m % succ n + -(succ n) * -(m / succ n) : Int) = m
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
rw [Int.neg_mul_neg]; exact congrArg ofNat <| Nat.mod_add_div ..
| -[_+1], 0 => by rw [emod_zero]; rfl
| -[m+1], succ n => aux m n.succ
@@ -149,7 +148,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
fun {k n} => @fun
| ofNat _ => congrArg ofNat <| Nat.add_mul_div_right _ _ k.succ_pos
| -[m+1] => by
show ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
change ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
by_cases h : m < n * k.succ
· rw [ Int.ofNat_sub h, Int.ofNat_sub ((Nat.div_lt_iff_lt_mul k.succ_pos).2 h)]
apply congrArg ofNat
@@ -158,7 +157,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
have H {a b : Nat} (h : a b) : (a : Int) + -((b : Int) + 1) = -[b - a +1] := by
rw [negSucc_eq, Int.ofNat_sub h]
simp only [Int.sub_eq_add_neg, Int.neg_add, Int.neg_neg, Int.add_left_comm, Int.add_assoc]
show ediv ((n * succ k) + -((m : Int) + 1)) (succ k) = n + -((m / succ k) + 1 : Int)
change ediv ((n * succ k) + -((m : Int) + 1)) (succ k) = n + -((m / succ k) + 1 : Int)
rw [H h, H ((Nat.le_div_iff_mul_le k.succ_pos).2 h)]
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div_of_le]; rwa [Nat.mul_comm]
@@ -264,8 +263,8 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_emod_self_left]
@[simp] theorem emod_emod (a b : Int) : (a % b) % b = a % b := by
conv => rhs; rw [ emod_add_ediv a b, add_mul_emod_self_left]
theorem emod_emod (a b : Int) : (a % b) % b = a % b := by
simp
theorem sub_emod (a b n : Int) : (a - b) % n = (a % n - b % n) % n := by
apply (emod_add_cancel_right b).mp

View File

@@ -3,7 +3,6 @@ Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro, Kim Morrison, Markus Himmel
-/
module
prelude
@@ -203,6 +202,9 @@ theorem tdiv_eq_ediv_of_nonneg : ∀ {a b : Int}, 0 ≤ a → a.tdiv b = a / b
| succ _, succ _, _ => rfl
| succ _, -[_+1], _ => rfl
@[simp] theorem natCast_tdiv_eq_ediv {a : Nat} {b : Int} : (a : Int).tdiv b = a / b :=
tdiv_eq_ediv_of_nonneg (by simp)
theorem tdiv_eq_ediv {a b : Int} :
a.tdiv b = a / b + if 0 a b a then 0 else sign b := by
simp only [dvd_iff_emod_eq_zero]
@@ -215,7 +217,7 @@ theorem tdiv_eq_ediv {a b : Int} :
negSucc_not_nonneg, sign_of_add_one]
simp only [negSucc_emod_ofNat_succ_eq_zero_iff]
norm_cast
simp only [subNat_eq_zero_iff, Nat.succ_eq_add_one, sign_negSucc, Int.sub_neg, false_or]
simp only [Nat.succ_eq_add_one, false_or]
split <;> rename_i h
· rw [Int.add_zero, neg_ofNat_eq_negSucc_iff]
exact Nat.succ_div_of_mod_eq_zero h
@@ -329,17 +331,17 @@ theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.fdiv b = a / b := by
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
| ofNat m, -[n+1] => by
show (m % succ n + -(succ n) * -(m / succ n) : Int) = m
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
rw [Int.neg_mul_neg]; exact congrArg ofNat (Nat.mod_add_div ..)
| -[m+1], 0 => by
show -(((succ m) % 0) : Int) + 0 * -(succ m / 0) = -(succ m)
change -(((succ m) % 0) : Int) + 0 * -(succ m / 0) = -(succ m)
rw [Nat.mod_zero, Int.zero_mul, Int.add_zero]
| -[m+1], ofNat n => by
show -(((succ m) % n) : Int) + n * -(succ m / n) = -(succ m)
change -(((succ m) % n) : Int) + n * -(succ m / n) = -(succ m)
rw [Int.mul_neg, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
| -[m+1], -[n+1] => by
show -((succ m % succ n) : Int) + -(succ n) * (succ m / succ n) = -(succ m)
change -((succ m % succ n) : Int) + -(succ n) * (succ m / succ n) = -(succ m)
rw [Int.neg_mul, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
@@ -361,17 +363,17 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
| succ _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| succ m, -[n+1] => by
show subNatNat (m % succ n) n + ((succ n * (m / succ n)) + n + 1) = (m + 1)
change subNatNat (m % succ n) n + ((succ n * (m / succ n)) + n + 1) = (m + 1)
rw [Int.add_comm _ n, Int.add_assoc, Int.add_assoc,
Int.subNatNat_eq_coe, Int.sub_add_cancel]
exact congrArg (ofNat · + 1) <| Nat.mod_add_div ..
| -[_+1], 0 => by rw [fmod_zero]; rfl
| -[m+1], succ n => by
show subNatNat .. - ((succ n * (m / succ n)) + (succ n)) = -(succ m)
change subNatNat .. - ((succ n * (m / succ n)) + (succ n)) = -(succ m)
rw [Int.subNatNat_eq_coe, Int.sub_sub, Int.neg_sub, Int.sub_sub, Int.sub_sub_self]
exact congrArg (-ofNat ·) <| Nat.succ_add .. Nat.mod_add_div .. rfl
| -[m+1], -[n+1] => by
show -((succ m % succ n) : Int) + -(succ n * (succ m / succ n)) = -(succ m)
change -((succ m % succ n) : Int) + -(succ n * (succ m / succ n)) = -(succ m)
rw [ Int.neg_add]; exact congrArg (-ofNat ·) <| Nat.mod_add_div ..
/-- Variant of `fmod_add_fdiv` with the multiplication written the other way around. -/
@@ -572,7 +574,7 @@ theorem neg_one_ediv (b : Int) : -1 / b = -b.sign :=
· refine Nat.le_trans ?_ (Nat.le_add_right _ _)
rw [ Nat.mul_div_mul_left _ _ m.succ_pos]
apply Nat.div_mul_le_self
· show m.succ * n.succ _
· change m.succ * n.succ _
rw [Nat.mul_left_comm]
apply Nat.mul_le_mul_left
apply (Nat.div_lt_iff_lt_mul k.succ_pos).1
@@ -1315,7 +1317,7 @@ protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
| 0, n => by simp [Int.neg_zero]
| succ _, (n:Nat) => by simp [tdiv, Int.negSucc_eq]
| -[_+1], 0 | -[_+1], -[_+1] => by
simp only [tdiv, neg_negSucc, Int.natCast_succ, Int.neg_neg]
simp only [tdiv, neg_negSucc, Int.neg_neg]
| succ _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
@@ -1406,12 +1408,11 @@ theorem mul_tmod (a b n : Int) : (a * b).tmod n = (a.tmod n * b.tmod n).tmod n :
case inv => simp [Int.dvd_neg]
induction m using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
simp only [ ofNat_tmod]
norm_cast at h
rw [Nat.mod_mod_of_dvd _ h]
@[simp] theorem tmod_tmod (a b : Int) : (a.tmod b).tmod b = a.tmod b :=
tmod_tmod_of_dvd a (Int.dvd_refl b)
theorem tmod_tmod (a b : Int) : (a.tmod b).tmod b = a.tmod b := by simp
theorem tmod_eq_zero_of_dvd : {a b : Int}, a b tmod b a = 0
| _, _, _, rfl => mul_tmod_right ..
@@ -1469,9 +1470,8 @@ protected theorem tdiv_mul_cancel {a b : Int} (H : b a) : a.tdiv b * b = a :
protected theorem mul_tdiv_cancel' {a b : Int} (H : a b) : a * b.tdiv a = b := by
rw [Int.mul_comm, Int.tdiv_mul_cancel H]
@[simp] theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
simp
theorem lt_tdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.tdiv b + 1) * b := by
rw [Int.add_mul, Int.one_mul, Int.mul_comm]
@@ -1568,17 +1568,15 @@ theorem dvd_tmod_sub_self {x m : Int} : m x.tmod m - x := by
theorem dvd_self_sub_tmod {x m : Int} : m x - x.tmod m :=
Int.dvd_neg.1 (by simpa only [Int.neg_sub] using dvd_tmod_sub_self)
@[simp] theorem neg_mul_tmod_right (a b : Int) : (-(a * b)).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
theorem neg_mul_tmod_right (a b : Int) : (-(a * b)).tmod a = 0 := by
simp
@[simp] theorem neg_mul_tmod_left (a b : Int) : (-(a * b)).tmod b = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left a b
theorem neg_mul_tmod_left (a b : Int) : (-(a * b)).tmod b = 0 := by
simp
@[simp] protected theorem tdiv_one : a : Int, a.tdiv 1 = a
| (n:Nat) => congrArg ofNat (Nat.div_one _)
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
| -[n+1] => by simp [Int.tdiv]; rfl
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
@@ -1700,7 +1698,7 @@ theorem lt_ediv_iff_of_dvd_of_neg {a b c : Int} (hc : c < 0) (hcb : c b) :
theorem ediv_le_ediv_iff_of_dvd_of_pos_of_pos {a b c d : Int} (hb : 0 < b) (hd : 0 < d)
(hba : b a) (hdc : d c) : a / b c / d d * a c * b := by
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
simp [*, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
theorem ediv_le_ediv_iff_of_dvd_of_pos_of_neg {a b c d : Int} (hb : 0 < b) (hd : d < 0)
(hba : b a) (hdc : d c) : a / b c / d c * b d * a := by
@@ -1715,12 +1713,12 @@ theorem ediv_le_ediv_iff_of_dvd_of_neg_of_pos {a b c d : Int} (hb : b < 0) (hd :
theorem ediv_le_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd : d < 0)
(hba : b a) (hdc : d c) : a / b c / d d * a c * b := by
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
simp [*, Int.ne_of_lt, d.mul_assoc, b.mul_comm]
theorem ediv_lt_ediv_iff_of_dvd_of_pos {a b c d : Int} (hb : 0 < b) (hd : 0 < d) (hba : b a)
(hdc : d c) : a / b < c / d d * a < c * b := by
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
simp [*, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
theorem ediv_lt_ediv_iff_of_dvd_of_pos_of_neg {a b c d : Int} (hb : 0 < b) (hd : d < 0)
(hba : b a) (hdc : d c) : a / b < c / d c * b < d * a := by
@@ -1735,7 +1733,7 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_pos {a b c d : Int} (hb : b < 0) (hd :
theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd : d < 0)
(hba : b a) (hdc : d c) : a / b < c / d d * a < c * b := by
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, Int.ne_of_gt, d.mul_assoc, b.mul_comm]
simp [*, Int.ne_of_lt, d.mul_assoc, b.mul_comm]
/-! ### `tdiv` and ordering -/
@@ -2193,8 +2191,8 @@ theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n :
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_fmod_self_left]
@[simp] theorem fmod_fmod (a b : Int) : (a.fmod b).fmod b = a.fmod b :=
fmod_fmod_of_dvd _ (Int.dvd_refl b)
theorem fmod_fmod (a b : Int) : (a.fmod b).fmod b = a.fmod b := by
simp
theorem sub_fmod (a b n : Int) : (a - b).fmod n = (a.fmod n - b.fmod n).fmod n := by
apply (fmod_add_cancel_right b).mp
@@ -2448,7 +2446,7 @@ theorem lt_mul_fdiv_self_add {x k : Int} (h : 0 < k) : x < k * (x.fdiv k) + k :=
@[simp]
theorem emod_bmod (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n := by
simp [bmod, Int.emod_emod]
simp [bmod]
@[deprecated emod_bmod (since := "2025-04-11")]
theorem emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n :=
@@ -2749,7 +2747,7 @@ theorem bmod_lt {x : Int} {m : Nat} (h : 0 < m) : bmod x m < (m + 1) / 2 := by
split
· assumption
· apply Int.lt_of_lt_of_le
· show _ < 0
· change _ < 0
have : x % m < m := emod_lt_of_pos x (natCast_pos.mpr h)
exact Int.sub_neg_of_lt this
· exact Int.le.intro_sub _ rfl
@@ -2989,7 +2987,7 @@ theorem self_le_ediv_of_nonpos_of_nonneg {x y : Int} (hx : x ≤ 0) (hy : 0 ≤
· simp [hx', zero_ediv]
· by_cases hy : y = 0
· simp [hy]; omega
· simp only [ge_iff_le, Int.le_ediv_iff_mul_le (c := y) (a := x) (b := x) (by omega),
· simp only [Int.le_ediv_iff_mul_le (c := y) (a := x) (b := x) (by omega),
show (x * y x) = (x * y x * 1) by rw [Int.mul_one], Int.mul_one]
apply Int.mul_le_mul_of_nonpos_left (a := x) (b := y) (c := (1 : Int)) (by omega) (by omega)

View File

@@ -35,6 +35,7 @@ Examples:
* `Int.gcd 0 5 = 5`
* `Int.gcd (-7) 0 = 7`
-/
@[expose]
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
theorem gcd_eq_natAbs_gcd_natAbs (m n : Int) : gcd m n = Nat.gcd m.natAbs n.natAbs := rfl
@@ -428,6 +429,7 @@ Examples:
* `Int.lcm 0 3 = 0`
* `Int.lcm (-3) 0 = 0`
-/
@[expose]
def lcm (m n : Int) : Nat := m.natAbs.lcm n.natAbs
theorem lcm_eq_natAbs_lcm_natAbs (m n : Int) : lcm m n = Nat.lcm m.natAbs n.natAbs := rfl
@@ -629,7 +631,7 @@ theorem lcm_mul_left_dvd_mul_lcm (k m n : Nat) : lcm (m * n) k lcm m k * lcm
simpa [lcm_comm, Nat.mul_comm] using lcm_mul_right_dvd_mul_lcm _ _ _
theorem lcm_dvd_mul_self_left_iff_dvd_mul {k n m : Nat} : lcm k n k * m n k * m := by
simp [ natAbs_dvd_natAbs, natAbs_mul, Nat.lcm_dvd_mul_self_left_iff_dvd_mul,
simp [Nat.lcm_dvd_mul_self_left_iff_dvd_mul,
lcm_eq_natAbs_lcm_natAbs]
theorem lcm_dvd_mul_self_right_iff_dvd_mul {k m n : Nat} : lcm n k m * k n m * k := by

View File

@@ -339,7 +339,7 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
match m with
| 0 => rfl
| succ m =>
show ofNat (n - succ m) = subNatNat n (succ m)
change ofNat (n - succ m) = subNatNat n (succ m)
rw [subNatNat, Nat.sub_eq_zero_of_le h]
@[deprecated negSucc_eq (since := "2025-03-11")]
@@ -454,7 +454,7 @@ theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases
theorem ofNat_mul_subNatNat (m n k : Nat) :
m * subNatNat n k = subNatNat (m * n) (m * k) := by
cases m with
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul, subNatNat_self]
| zero => simp [Int.zero_mul, Nat.zero_mul, subNatNat_self]
| succ m => cases n.lt_or_ge k with
| inl h =>
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)

View File

@@ -121,7 +121,7 @@ theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat ↔ n < m :=
/-! ### min and max -/
@[simp] protected theorem min_assoc : (a b c : Int), min (min a b) c = min a (min b c) := by omega
instance : Std.Associative (α := Nat) min := Nat.min_assoc
instance : Std.Associative (α := Int) min := Int.min_assoc
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
rw [ Int.min_assoc, Int.min_self]
@@ -130,7 +130,7 @@ instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
rw [Int.min_comm m n, Int.min_assoc, Int.min_self]
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
instance : Std.Associative (α := Nat) max := Nat.max_assoc
instance : Std.Associative (α := Int) max := Int.max_assoc
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
rw [ Int.max_assoc, Int.max_self]

View File

@@ -23,6 +23,7 @@ namespace Int.Linear
abbrev Var := Nat
abbrev Context := Lean.RArray Int
@[expose]
def Var.denote (ctx : Context) (v : Var) : Int :=
ctx.get v
@@ -36,6 +37,7 @@ inductive Expr where
| mulR (a : Expr) (k : Int)
deriving Inhabited, BEq
@[expose]
def Expr.denote (ctx : Context) : Expr Int
| .add a b => Int.add (denote ctx a) (denote ctx b)
| .sub a b => Int.sub (denote ctx a) (denote ctx b)
@@ -50,6 +52,7 @@ inductive Poly where
| add (k : Int) (v : Var) (p : Poly)
deriving BEq
@[expose]
def Poly.denote (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
@@ -59,6 +62,7 @@ def Poly.denote (ctx : Context) (p : Poly) : Int :=
Similar to `Poly.denote`, but produces a denotation better for `simp +arith`.
Remark: we used to convert `Poly` back into `Expr` to achieve that.
-/
@[expose]
def Poly.denote' (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
@@ -75,8 +79,8 @@ where
theorem Poly.denote'_go_eq_denote (ctx : Context) (p : Poly) (r : Int) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next => rw [Int.add_comm]
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
next ih => simp at ih; rw [ih]; ac_rfl
next ih => simp at ih; rw [ih]; ac_rfl
theorem Poly.denote'_eq_denote (ctx : Context) (p : Poly) : p.denote' ctx = p.denote ctx := by
unfold denote' <;> split <;> simp [denote, denote'_go_eq_denote] <;> ac_rfl
@@ -84,11 +88,13 @@ theorem Poly.denote'_eq_denote (ctx : Context) (p : Poly) : p.denote' ctx = p.de
theorem Poly.denote'_add (ctx : Context) (a : Int) (x : Var) (p : Poly) : (Poly.add a x p).denote' ctx = a * x.denote ctx + p.denote ctx := by
simp [Poly.denote'_eq_denote, denote]
@[expose]
def Poly.addConst (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k+k')
| .add k' v' p => .add k' v' (addConst p k)
@[expose]
def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
match p with
| .num k' => .add k v (.num k')
@@ -104,16 +110,19 @@ def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
.add k' v' (insert k v p)
/-- Normalizes the given polynomial by fusing monomial and constants. -/
@[expose]
def Poly.norm (p : Poly) : Poly :=
match p with
| .num k => .num k
| .add k v p => (norm p).insert k v
@[expose]
def Poly.append (p₁ p₂ : Poly) : Poly :=
match p₁ with
| .num k₁ => p₂.addConst k₁
| .add k x p₁ => .add k x (append p₁ p₂)
@[expose]
def Poly.combine' (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
match fuel with
| 0 => p₁.append p₂
@@ -133,10 +142,12 @@ def Poly.combine' (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
else
.add a₂ x₂ (combine' fuel (.add a₁ x₁ p₁) p₂)
@[expose]
def Poly.combine (p₁ p₂ : Poly) : Poly :=
combine' 100000000 p₁ p₂
/-- Converts the given expression into a polynomial. -/
@[expose]
def Expr.toPoly' (e : Expr) : Poly :=
go 1 e (.num 0)
where
@@ -150,6 +161,7 @@ where
| .neg a => go (-coeff) a
/-- Converts the given expression into a polynomial, and then normalizes it. -/
@[expose]
def Expr.norm (e : Expr) : Poly :=
e.toPoly'.norm
@@ -159,6 +171,7 @@ Examples:
- `cdiv 7 3` returns `3`
- `cdiv (-7) 3` returns `-2`.
-/
@[expose]
def cdiv (a b : Int) : Int :=
-((-a)/b)
@@ -173,6 +186,7 @@ See theorem `cdiv_add_cmod`. We also have
-b < cmod a b ≤ 0
```
-/
@[expose]
def cmod (a b : Int) : Int :=
-((-a)%b)
@@ -219,6 +233,7 @@ theorem cdiv_eq_div_of_divides {a b : Int} (h : a % b = 0) : a/b = cdiv a b := b
next => rw [Int.mul_eq_mul_right_iff h] at this; assumption
/-- Returns the constant of the given linear polynomial. -/
@[expose]
def Poly.getConst : Poly Int
| .num k => k
| .add _ _ p => getConst p
@@ -230,6 +245,7 @@ Notes:
- We only use this function with `k`s that divides all coefficients.
- We use `cdiv` for the constant to implement the inequality tightening rule.
-/
@[expose]
def Poly.div (k : Int) : Poly Poly
| .num k' => .num (cdiv k' k)
| .add k' x p => .add (k'/k) x (div k p)
@@ -238,6 +254,7 @@ def Poly.div (k : Int) : Poly → Poly
Returns `true` if `k` divides all coefficients and the constant of the given
linear polynomial.
-/
@[expose]
def Poly.divAll (k : Int) : Poly Bool
| .num k' => k' % k == 0
| .add k' _ p => k' % k == 0 && divAll k p
@@ -245,6 +262,7 @@ def Poly.divAll (k : Int) : Poly → Bool
/--
Returns `true` if `k` divides all coefficients of the given linear polynomial.
-/
@[expose]
def Poly.divCoeffs (k : Int) : Poly Bool
| .num _ => true
| .add k' _ p => k' % k == 0 && divCoeffs k p
@@ -252,11 +270,13 @@ def Poly.divCoeffs (k : Int) : Poly → Bool
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
@[expose]
def Poly.mul' (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul' p k)
@[expose]
def Poly.mul (p : Poly) (k : Int) : Poly :=
if k == 0 then
.num 0
@@ -343,7 +363,7 @@ theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
simp [eq_of_beq h]
| case2 k k' h =>
simp only [toPoly'.go, h, cond_false]
simp [Var.denote]
simp
| case3 k i => simp [toPoly'.go]
| case4 k a b iha ihb => simp [toPoly'.go, iha, ihb]
| case5 k a b iha ihb =>
@@ -351,7 +371,7 @@ theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
rw [Int.sub_eq_add_neg, Int.neg_mul, Int.add_assoc]
| case6 k k' a h
| case8 k a k' h =>
simp only [toPoly'.go, h, cond_false]
simp only [toPoly'.go, h]
simp [eq_of_beq h]
| case7 k a k' h ih =>
simp only [toPoly'.go, h, cond_false]
@@ -383,9 +403,10 @@ attribute [local simp] Poly.denote'_eq_denote
theorem Expr.eq_of_norm_eq (ctx : Context) (e : Expr) (p : Poly) (h : e.norm == p) : e.denote ctx = p.denote' ctx := by
have h := congrArg (Poly.denote ctx) (eq_of_beq h)
simp [Poly.norm] at h
simp at h
simp [*]
@[expose]
def norm_eq_cert (lhs rhs : Expr) (p : Poly) : Bool :=
p == (lhs.sub rhs).norm
@@ -401,6 +422,7 @@ theorem norm_le (ctx : Context) (lhs rhs : Expr) (p : Poly) (h : norm_eq_cert lh
· exact Int.sub_nonpos_of_le
· exact Int.le_of_sub_nonpos
@[expose]
def norm_eq_var_cert (lhs rhs : Expr) (x y : Var) : Bool :=
(lhs.sub rhs).norm == .add 1 x (.add (-1) y (.num 0))
@@ -411,6 +433,7 @@ theorem norm_eq_var (ctx : Context) (lhs rhs : Expr) (x y : Var) (h : norm_eq_va
simp at h
rw [Int.sub_eq_zero, h, @Int.sub_eq_zero (Var.denote ctx x), Int.sub_eq_add_neg]
@[expose]
def norm_eq_var_const_cert (lhs rhs : Expr) (x : Var) (k : Int) : Bool :=
(lhs.sub rhs).norm == .add 1 x (.num (-k))
@@ -429,6 +452,7 @@ private theorem mul_eq_zero_iff (a k : Int) (h₁ : k > 0) : k * a = 0 ↔ a = 0
theorem norm_eq_coeff' (ctx : Context) (p p' : Poly) (k : Int) : p = p'.mul k k > 0 (p.denote ctx = 0 p'.denote ctx = 0) := by
intro; subst p; intro h; simp [mul_eq_zero_iff, *]
@[expose]
def norm_eq_coeff_cert (lhs rhs : Expr) (p : Poly) (k : Int) : Bool :=
(lhs.sub rhs).norm == p.mul k && k > 0
@@ -448,7 +472,7 @@ private theorem mul_le_zero_iff (a k : Int) (h₁ : k > 0) : k * a ≤ 0 ↔ a
simp at h; assumption
private theorem norm_le_coeff' (ctx : Context) (p p' : Poly) (k : Int) : p = p'.mul k k > 0 (p.denote ctx 0 p'.denote ctx 0) := by
simp [norm_eq_coeff_cert]
simp
intro; subst p; intro h; simp [mul_le_zero_iff, *]
theorem norm_le_coeff (ctx : Context) (lhs rhs : Expr) (p : Poly) (k : Int)
@@ -492,6 +516,7 @@ private theorem eq_of_norm_eq_of_divCoeffs {ctx : Context} {p₁ p₂ : Poly} {k
apply mul_add_cmod_le_iff
assumption
@[expose]
def norm_le_coeff_tight_cert (lhs rhs : Expr) (p : Poly) (k : Int) : Bool :=
let p' := lhs.sub rhs |>.norm
k > 0 && (p'.divCoeffs k && p == p'.div k)
@@ -502,11 +527,13 @@ theorem norm_le_coeff_tight (ctx : Context) (lhs rhs : Expr) (p : Poly) (k : Int
rw [norm_le ctx lhs rhs (lhs.sub rhs).norm BEq.rfl, Poly.denote'_eq_denote]
apply eq_of_norm_eq_of_divCoeffs
@[expose]
def Poly.isUnsatEq (p : Poly) : Bool :=
match p with
| .num k => k != 0
| _ => false
@[expose]
def Poly.isValidEq (p : Poly) : Bool :=
match p with
| .num k => k == 0
@@ -530,11 +557,13 @@ theorem eq_eq_true (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isValid
rw [ Int.sub_eq_zero, h]
assumption
@[expose]
def Poly.isUnsatLe (p : Poly) : Bool :=
match p with
| .num k => k > 0
| _ => false
@[expose]
def Poly.isValidLe (p : Poly) : Bool :=
match p with
| .num k => k 0
@@ -595,6 +624,7 @@ private theorem poly_eq_zero_eq_false (ctx : Context) {p : Poly} {k : Int} : p.d
have high := h₃
exact contra h₂ low high this
@[expose]
def unsatEqDivCoeffCert (lhs rhs : Expr) (k : Int) : Bool :=
let p := (lhs.sub rhs).norm
p.divCoeffs k && k > 0 && cmod p.getConst k < 0
@@ -621,6 +651,7 @@ private theorem gcd_dvd_step {k a b x : Int} (h : k a*x + b) : gcd a k b
have h₂ : gcd a k a*x := Int.dvd_trans (gcd_dvd_left a k) (Int.dvd_mul_right a x)
exact Int.dvd_iff_dvd_of_dvd_add h₁ |>.mp h₂
@[expose]
def Poly.gcdCoeffs : Poly Int Int
| .num _, k => k
| .add k' _ p, k => gcdCoeffs p (gcd k' k)
@@ -631,6 +662,7 @@ theorem Poly.gcd_dvd_const {ctx : Context} {p : Poly} {k : Int} (h : k p.den
rw [Int.add_comm] at h
exact ih (gcd_dvd_step h)
@[expose]
def Poly.isUnsatDvd (k : Int) (p : Poly) : Bool :=
p.getConst % p.gcdCoeffs k != 0
@@ -668,9 +700,11 @@ theorem dvd_eq_false (ctx : Context) (k : Int) (e : Expr) (h : e.norm.isUnsatDvd
rw [norm_dvd ctx k e e.norm BEq.rfl]
apply dvd_eq_false' ctx k e.norm h
@[expose]
def dvd_coeff_cert (k₁ : Int) (p₁ : Poly) (k₂ : Int) (p₂ : Poly) (k : Int) : Bool :=
k != 0 && (k₁ == k*k₂ && p₁ == p₂.mul k)
@[expose]
def norm_dvd_gcd_cert (k₁ : Int) (e₁ : Expr) (k₂ : Int) (p₂ : Poly) (k : Int) : Bool :=
dvd_coeff_cert k₁ e₁.norm k₂ p₂ k
@@ -702,6 +736,7 @@ private theorem dvd_gcd_of_dvd (d a x p : Int) (h : d a * x + p) : gcd d a
rw [Int.mul_assoc, Int.mul_assoc, Int.mul_sub] at h
exists k₁ * k - k₂ * x
@[expose]
def dvd_elim_cert (k₁ : Int) (p₁ : Poly) (k₂ : Int) (p₂ : Poly) : Bool :=
match p₁ with
| .add a _ p => k₂ == gcd k₁ a && p₂ == p
@@ -764,6 +799,7 @@ private theorem dvd_solve_elim' {x : Int} {d₁ a₁ p₁ : Int} {d₂ a₂ p₂
rw [h₃, h₄, Int.mul_assoc, Int.mul_assoc, Int.mul_sub] at this
exact k₄ * k₁ - k₃ * k₂, this
@[expose]
def dvd_solve_combine_cert (d₁ : Int) (p₁ : Poly) (d₂ : Int) (p₂ : Poly) (d : Int) (p : Poly) (g α β : Int) : Bool :=
match p₁, p₂ with
| .add a₁ x₁ p₁, .add a₂ x₂ p₂ =>
@@ -779,12 +815,13 @@ theorem dvd_solve_combine (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int
split <;> simp
next a₁ x₁ p₁ a₂ x₂ p₂ =>
intro _ hg hd hp; subst x₁ p
simp [Poly.denote'_add]
simp
intro h₁ h₂
rw [Int.add_comm] at h₁ h₂
rw [Int.add_comm _ (g * x₂.denote ctx), Int.add_left_comm, Int.add_assoc, hd]
exact dvd_solve_combine' hg.symm h₁ h₂
@[expose]
def dvd_solve_elim_cert (d₁ : Int) (p₁ : Poly) (d₂ : Int) (p₂ : Poly) (d : Int) (p : Poly) : Bool :=
match p₁, p₂ with
| .add a₁ x₁ p₁, .add a₂ x₂ p₂ =>
@@ -816,6 +853,7 @@ theorem le_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p
simp at h
simp [*]
@[expose]
def le_coeff_cert (p₁ p₂ : Poly) (k : Int) : Bool :=
k > 0 && (p₁.divCoeffs k && p₂ == p₁.div k)
@@ -824,6 +862,7 @@ theorem le_coeff (ctx : Context) (p₁ p₂ : Poly) (k : Int) : le_coeff_cert p
intro h₁ h₂ h₃
exact eq_of_norm_eq_of_divCoeffs h₁ h₂ h₃ |>.mp
@[expose]
def le_neg_cert (p₁ p₂ : Poly) : Bool :=
p₂ == (p₁.mul (-1) |>.addConst 1)
@@ -834,11 +873,13 @@ theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ → ¬
simp at h
exact h
@[expose]
def Poly.leadCoeff (p : Poly) : Int :=
match p with
| .add a _ _ => a
| _ => 1
@[expose]
def le_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
@@ -854,6 +895,7 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
· rw [ Int.zero_mul (Poly.denote ctx p₂)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
· rw [ Int.zero_mul (Poly.denote ctx p₁)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
@[expose]
def le_combine_coeff_cert (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
@@ -883,6 +925,7 @@ theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p
simp at h
simp [*]
@[expose]
def eq_coeff_cert (p p' : Poly) (k : Int) : Bool :=
p == p'.mul k && k > 0
@@ -893,6 +936,7 @@ theorem eq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p' k
theorem eq_unsat (ctx : Context) (p : Poly) : p.isUnsatEq p.denote' ctx = 0 False := by
simp [Poly.isUnsatEq] <;> split <;> simp
@[expose]
def eq_unsat_coeff_cert (p : Poly) (k : Int) : Bool :=
p.divCoeffs k && k > 0 && cmod p.getConst k < 0
@@ -902,6 +946,7 @@ theorem eq_unsat_coeff (ctx : Context) (p : Poly) (k : Int) : eq_unsat_coeff_cer
have h := poly_eq_zero_eq_false ctx h₁ h₂ h₃; clear h₁ h₂ h₃
simp [h]
@[expose]
def Poly.coeff (p : Poly) (x : Var) : Int :=
match p with
| .add a y p => bif x == y then a else coeff p x
@@ -916,7 +961,8 @@ private theorem dvd_of_eq' {a x p : Int} : a*x + p = 0 → a p := by
rw [Int.mul_comm, Int.neg_mul, Eq.comm, Int.mul_comm] at h
exact -x, h
private def abs (x : Int) : Int :=
@[expose]
def abs (x : Int) : Int :=
Int.ofNat x.natAbs
private theorem abs_dvd {a p : Int} (h : a p) : abs a p := by
@@ -924,6 +970,7 @@ private theorem abs_dvd {a p : Int} (h : a p) : abs a p := by
· simp at h; assumption
· simp [Int.negSucc_eq] at h; assumption
@[expose]
def dvd_of_eq_cert (x : Var) (p₁ : Poly) (d₂ : Int) (p₂ : Poly) : Bool :=
let a := p₁.coeff x
d₂ == abs a && p₂ == p₁.insert (-a) x
@@ -950,6 +997,7 @@ private theorem eq_dvd_subst' {a x p d b q : Int} : a*x + p = 0 → d b*x +
rw [ Int.mul_assoc] at h
exact z, h
@[expose]
def eq_dvd_subst_cert (x : Var) (p₁ : Poly) (d₂ : Int) (p₂ : Poly) (d₃ : Int) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
@@ -979,6 +1027,7 @@ theorem eq_dvd_subst (ctx : Context) (x : Var) (p₁ : Poly) (d₂ : Int) (p₂
apply abs_dvd
simp [this, Int.neg_mul]
@[expose]
def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
@@ -991,6 +1040,7 @@ theorem eq_eq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃
intro h₁ h₂
simp [*]
@[expose]
def eq_le_subst_nonneg_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
@@ -1006,6 +1056,7 @@ theorem eq_le_subst_nonneg (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
simp at h₂
simp [*]
@[expose]
def eq_le_subst_nonpos_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
@@ -1022,6 +1073,7 @@ theorem eq_le_subst_nonpos (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
rw [Int.mul_comm]
assumption
@[expose]
def eq_of_core_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
p₃ == p₁.combine (p₂.mul (-1))
@@ -1031,6 +1083,7 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
intro; subst p₃; simp
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self]
@[expose]
def Poly.isUnsatDiseq (p : Poly) : Bool :=
match p with
| .num 0 => true
@@ -1047,11 +1100,12 @@ theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p'
intro _ _; simp [mul_eq_zero_iff, *]
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) p.denote' ctx 0 p'.denote' ctx 0 := by
simp; intro _ _; simp [mul_eq_zero_iff, *]
simp; intro _ _; simp [*]
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq p.denote' ctx 0 False := by
simp [Poly.isUnsatDiseq] <;> split <;> simp
@[expose]
def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
@@ -1071,6 +1125,7 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
intro h; rw [ Int.sub_eq_zero] at h
rw [Int.add_neg_eq_sub]; assumption
@[expose]
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
@@ -1081,6 +1136,7 @@ theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
intro h₁ h₂
simp [Int.eq_iff_le_and_ge, *]
@[expose]
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
@@ -1095,6 +1151,7 @@ theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
@[expose]
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
p₂ == p₁.addConst 1 &&
p₃ == (p₁.mul (-1)).addConst 1
@@ -1113,6 +1170,7 @@ theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
intro h₁ h₂ h₃
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
@[expose]
def OrOver (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => False
@@ -1127,6 +1185,7 @@ theorem orOver_resolve {n p} : OrOver (n+1) p → ¬ p n → OrOver n p := by
· contradiction
· assumption
@[expose]
def OrOver_cases_type (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => p 0
@@ -1186,6 +1245,7 @@ private theorem cooper_dvd_left_core
rw [this] at h₃
exists k.toNat
@[expose]
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
@@ -1194,11 +1254,13 @@ def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
@[expose]
def Poly.tail (p : Poly) : Poly :=
match p with
| .add _ _ p => p
| _ => p
@[expose]
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
@@ -1238,6 +1300,7 @@ theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : N
simp only [denote'_addConst_eq]
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
@[expose]
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
@@ -1248,8 +1311,9 @@ def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' :
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
intros; subst p' b; simp; assumption
@[expose]
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
@@ -1258,6 +1322,7 @@ theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d :
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
intros; subst a p'; simp; assumption
@[expose]
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let p := p₁.tail
let s := p₃.tail
@@ -1283,16 +1348,18 @@ private theorem cooper_left_core
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true] at h
assumption
@[expose]
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
n == a.natAbs
@[expose]
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
@@ -1320,6 +1387,7 @@ theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
simp only [denote'_addConst_eq]
assumption
@[expose]
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
@@ -1330,8 +1398,9 @@ def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Pol
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_left_split_ineq_cert, cooper_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
intros; subst p' b; simp; assumption
@[expose]
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
@@ -1353,7 +1422,7 @@ private theorem cooper_dvd_right_core
have h₁' : p (-a)*x := by rw [Int.neg_mul, Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
have h₂' : b * x -q := by rw [ Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
have k, h₁, h₂, h₃, h₄, h₅ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp x, h₁', h₂', h₃
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
simp only [Int.neg_mul, Int.mul_neg, Int.neg_neg] at *
apply orOver_of_exists
have hlt := ofNat_lt h₁ h₂
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
@@ -1363,8 +1432,9 @@ private theorem cooper_dvd_right_core
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
rw [this] at h₅; clear this
exists k.toNat
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
simp only [hlt, and_true, cast_toNat h₁, h₃, h₄, h₅]
@[expose]
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
@@ -1373,6 +1443,7 @@ def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
@[expose]
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
@@ -1402,9 +1473,10 @@ theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n :
intro h₁ h₂ h₃
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
simp only [denote'_addConst_eq]
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
@[expose]
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
@@ -1415,8 +1487,9 @@ def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p'
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
intros; subst a p'; simp; assumption
@[expose]
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
@@ -1425,6 +1498,7 @@ theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d :
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
intros; subst b p'; simp; assumption
@[expose]
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let q := p₂.tail
let s := p₃.tail
@@ -1448,17 +1522,19 @@ private theorem cooper_right_core
have d_pos : (0 : Int) < 1 := by decide
have h₃ : 1 0*x + 0 := Int.one_dvd _
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos),
Int.ediv_self (Int.ne_of_gt b_pos), lcm_one,
Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true, Int.neg_zero] at h
assumption
@[expose]
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
@[expose]
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
@@ -1483,9 +1559,10 @@ theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
intro h₁ h₂
have := cooper_right_core ha hb h₁ h₂
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
simp only [denote'_addConst_eq]
assumption
@[expose]
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
@@ -1496,8 +1573,9 @@ def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Po
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_right_split_ineq_cert, cooper_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
intros; subst a p'; simp; assumption
@[expose]
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
@@ -1587,6 +1665,7 @@ abbrev Poly.casesOnAdd (p : Poly) (k : Int → Var → Poly → Bool) : Bool :=
abbrev Poly.casesOnNum (p : Poly) (k : Int Bool) : Bool :=
p.casesOn k (fun _ _ _ => false)
@[expose]
def cooper_unsat_cert (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int) : Bool :=
p₁.casesOnAdd fun k₁ x p₁ =>
p₂.casesOnAdd fun k₂ y p₂ =>
@@ -1603,7 +1682,7 @@ theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β :
: cooper_unsat_cert p₁ p₂ p₃ d α β
p₁.denote' ctx 0 p₂.denote' ctx 0 d p₃.denote' ctx False := by
unfold cooper_unsat_cert <;> cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnAdd,
Bool.false_eq_true, Poly.denote'_add, mul_def, add_def, false_implies]
Bool.false_eq_true, Poly.denote'_add, false_implies]
next k₁ x p₁ k₂ y p₂ c z p₃ =>
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnNum, Int.reduceNeg,
Bool.and_eq_true, beq_iff_eq, decide_eq_true_eq, and_imp, Bool.false_eq_true,
@@ -1626,6 +1705,7 @@ theorem emod_nonneg (x y : Int) : y != 0 → -1 * (x % y) ≤ 0 := by
simp at this
assumption
@[expose]
def emod_le_cert (y n : Int) : Bool :=
y != 0 && n == 1 - y.natAbs
@@ -1665,7 +1745,7 @@ theorem natCast_sub (x y : Nat)
(NatCast.natCast x : Int) + -1*NatCast.natCast y
else
(0 : Int) := by
show ((x - y) : Int) = if (y : Int) + (-1)*x 0 then x + (-1)*y else 0
change ((x - y) : Int) = if (y : Int) + (-1)*x 0 then (x : Int) + (-1)*y else 0
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
split
@@ -1708,6 +1788,7 @@ private theorem eq_neg_addConst_add (ctx : Context) (p : Poly)
rw [Int.add_right_neg]
simp
@[expose]
def dvd_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
@@ -1728,6 +1809,7 @@ theorem dvd_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
simp only [Poly.denote'_eq_denote]
exact dvd_le_tight' hd
@[expose]
def dvd_neg_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
@@ -1737,7 +1819,7 @@ def dvd_neg_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
theorem Poly.mul_minus_one_getConst_eq (p : Poly) : (p.mul (-1)).getConst = -p.getConst := by
simp [Poly.mul, Poly.getConst]
simp [Poly.mul]
induction p <;> simp [Poly.mul', Poly.getConst, *]
theorem dvd_neg_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
@@ -1764,6 +1846,7 @@ theorem le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
intro h₁ h₂; rwa [norm_le ctx lhs rhs p h₁] at h₂
@[expose]
def not_le_norm_expr_cert (lhs rhs : Expr) (p : Poly) : Bool :=
p == (((lhs.sub rhs).norm).mul (-1)).addConst 1
@@ -1796,6 +1879,7 @@ theorem of_not_dvd (a b : Int) : a != 0 → ¬ (a b) → b % a > 0 := by
simp [h₁] at h₂
assumption
@[expose]
def le_of_le_cert (p q : Poly) (k : Nat) : Bool :=
q == p.addConst (- k)
@@ -1806,6 +1890,7 @@ theorem le_of_le (ctx : Context) (p q : Poly) (k : Nat)
simp [Lean.Omega.Int.add_le_zero_iff_le_neg']
exact Int.le_trans h (Int.ofNat_zero_le _)
@[expose]
def not_le_of_le_cert (p q : Poly) (k : Nat) : Bool :=
q == (p.mul (-1)).addConst (1 + k)
@@ -1815,10 +1900,11 @@ theorem not_le_of_le (ctx : Context) (p q : Poly) (k : Nat)
intro h
apply Int.pos_of_neg_neg
apply Int.lt_of_add_one_le
simp [Int.neg_add, Int.neg_sub]
simp [Int.neg_add]
rw [ Int.add_assoc, Int.add_assoc, Int.add_neg_cancel_right, Lean.Omega.Int.add_le_zero_iff_le_neg']
simp; exact Int.le_trans h (Int.ofNat_zero_le _)
@[expose]
def eq_def_cert (x : Var) (xPoly : Poly) (p : Poly) : Bool :=
p == .add (-1) x xPoly
@@ -1827,6 +1913,7 @@ theorem eq_def (ctx : Context) (x : Var) (xPoly : Poly) (p : Poly)
simp [eq_def_cert]; intro _ h; subst p; simp [h]
rw [ Int.sub_eq_add_neg, Int.sub_self]
@[expose]
def eq_def'_cert (x : Var) (e : Expr) (p : Poly) : Bool :=
p == .add (-1) x e.norm

View File

@@ -19,6 +19,7 @@ We use them to implement the arithmetic theories in `grind`
abbrev Var := Nat
abbrev Context := Lean.RArray Nat
@[expose]
def Var.denote (ctx : Context) (v : Var) : Nat :=
ctx.get v
@@ -31,6 +32,7 @@ inductive Expr where
| mod (a b : Expr)
deriving BEq
@[expose]
def Expr.denote (ctx : Context) : Expr Nat
| .num k => k
| .var v => v.denote ctx
@@ -39,6 +41,7 @@ def Expr.denote (ctx : Context) : Expr → Nat
| .div a b => Nat.div (denote ctx a) (denote ctx b)
| .mod a b => Nat.mod (denote ctx a) (denote ctx b)
@[expose]
def Expr.denoteAsInt (ctx : Context) : Expr Int
| .num k => Int.ofNat k
| .var v => Int.ofNat (v.denote ctx)
@@ -48,7 +51,7 @@ def Expr.denoteAsInt (ctx : Context) : Expr → Int
| .mod a b => Int.emod (denoteAsInt ctx a) (denoteAsInt ctx b)
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
induction e <;> simp [denote, denoteAsInt, Int.natCast_ediv, *] <;> rfl
induction e <;> simp [denote, denoteAsInt, *] <;> rfl
theorem Expr.eq_denoteAsInt (ctx : Context) (e : Expr) : e.denote ctx = e.denoteAsInt ctx := by
apply Eq.symm; apply denoteAsInt_eq

View File

@@ -448,7 +448,7 @@ protected theorem le_max_left (a b : Int) : a ≤ max a b := by rw [Int.max_def]
protected theorem le_max_right (a b : Int) : b max a b := Int.max_comm .. Int.le_max_left ..
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
simp [Int.max_def, h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
@@ -638,7 +638,7 @@ theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
@[simp] theorem toNat_natCast (n : Nat) : toNat n = n := rfl
@[deprecated toNat_natCast (since := "2025-04-16")]
theorem toNat_ofNat (n : Nat) : toNat n = n := toNat_natCast n
theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
simp [toNat]

View File

@@ -19,6 +19,13 @@ protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
protected theorem zero_pow {n : Nat} (h : n 0) : (0 : Int) ^ n = 0 := by
match n, h with
| n + 1, _ => simp [Int.pow_succ]
protected theorem one_pow {n : Nat} : (1 : Int) ^ n = 1 := by
induction n with simp_all [Int.pow_succ]
protected theorem pow_pos {n : Int} {m : Nat} : 0 < n 0 < n ^ m := by
induction m with
| zero => simp

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Basic
import Init.Data.Iterators.PostconditionMonad
import Init.Data.Iterators.Consumers
import Init.Data.Iterators.Lemmas
import Init.Data.Iterators.Internal
/-!
# Iterators
See `Std.Data.Iterators` for an overview over the iterator API.
-/

View File

@@ -0,0 +1,694 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Core
import Init.Classical
import Init.Ext
import Init.NotationExtra
import Init.TacticsExtra
/-!
### Definition of iterators
This module defines iterators and what it means for an iterator to be finite and productive.
-/
namespace Std
namespace Iterators
/--
An iterator that sequentially emits values of type `β` in the monad `m`. It may be finite
or infinite.
See the root module `Std.Data.Iterators` for a more comprehensive overview over the iterator
framework.
See `Std.Data.Iterators.Producers` for ways to iterate over common data structures.
By convention, the monadic iterator associated with an object can be obtained via dot notation.
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `Iter` for a more convenient interface in case that no monadic effects are needed (`m = Id`).
Internally, `IterM m β` wraps an element of type `α` containing state information.
The type `α` determines the implementation of the iterator using a typeclass mechanism.
The concrete typeclass implementing the iterator is `Iterator α m β`.
When using combinators, `α` can become very complicated. It is an implicit parameter
of `α` so that the pretty printer will not print this large type by default. If a declaration
returns an iterator, the following will not work:
```lean
def x : IterM IO Nat := [1, 2, 3].iterM IO
```
Instead the declaration type needs to be completely omitted:
```lean
def x := [1, 2, 3].iterM IO
-- if you want to ensure that `x` is an iterator in `IO` emitting `Nat`
def x := ([1, 2, 3].iterM IO : IterM IO Nat)
```
-/
@[ext]
structure IterM {α : Type w} (m : Type w Type w') (β : Type w) where
/-- Internal implementation detail of the iterator. -/
internalState : α
/--
An iterator that sequentially emits values of type `β`. It may be finite
or infinite.
See the root module `Std.Data.Iterators` for a more comprehensive overview over the iterator
framework.
See `Std.Data.Iterators.Producers` for ways to iterate over common data structures.
By convention, the monadic iterator associated with an object can be obtained via dot notation.
For example, `List.iterM IO` creates an iterator over a list in the monad `IO`.
See `Init.Data.Iterators.Consumers` for ways to use an iterator. For example, `it.toList` will
convert a provably finite iterator `it` into a list and `it.allowNontermination.toList` will
do so even if finiteness cannot be proved. It is also always possible to manually iterate using
`it.step`, relying on the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
See `IterM` for iterators that operate in a monad.
Internally, `Iter β` wraps an element of type `α` containing state information.
The type `α` determines the implementation of the iterator using a typeclass mechanism.
The concrete typeclass implementing the iterator is `Iterator α m β`.
When using combinators, `α` can become very complicated. It is an implicit parameter
of `α` so that the pretty printer will not print this large type by default. If a declaration
returns an iterator, the following will not work:
```lean
def x : Iter Nat := [1, 2, 3].iter
```
Instead the declaration type needs to be completely omitted:
```lean
def x := [1, 2, 3].iter
-- if you want to ensure that `x` is an iterator emitting `Nat`
def x := ([1, 2, 3].iter : Iter Nat)
```
-/
structure Iter {α : Type w} (β : Type w) where
/-- Internal implementation detail of the iterator. -/
internalState : α
/--
Converts a pure iterator (`Iter β`) into a monadic iterator (`IterM Id β`) in the
identity monad `Id`.
-/
@[expose]
def Iter.toIterM {α : Type w} {β : Type w} (it : Iter (α := α) β) : IterM (α := α) Id β :=
it.internalState
/--
Converts a monadic iterator (`IterM Id β`) over `Id` into a pure iterator (`Iter β`).
-/
@[expose]
def IterM.toIter {α : Type w} {β : Type w} (it : IterM (α := α) Id β) : Iter (α := α) β :=
it.internalState
@[simp]
theorem Iter.toIter_toIterM {α : Type w} {β : Type w} (it : Iter (α := α) β) :
it.toIterM.toIter = it :=
rfl
@[simp]
theorem Iter.toIter_comp_toIterM {α : Type w} {β : Type w} :
IterM.toIter Iter.toIterM (α := α) (β := β) = id :=
rfl
@[simp]
theorem Iter.toIterM_toIter {α : Type w} {β : Type w} (it : IterM (α := α) Id β) :
it.toIter.toIterM = it :=
rfl
@[simp]
theorem Iter.toIterM_comp_toIter {α : Type w} {β : Type w} :
Iter.toIterM IterM.toIter (α := α) (β := β) = id :=
rfl
section IterStep
variable {α : Type u} {β : Type w}
/--
`IterStep α β` represents a step taken by an iterator (`Iter β` or `IterM m β`).
-/
inductive IterStep (α β) where
/--
`IterStep.yield it out` describes the situation that an iterator emits `out` and provides `it`
as the succeeding iterator.
-/
| yield : (it : α) (out : β) IterStep α β
/--
`IterStep.skip it` describes the situation that an iterator does not emit anything in this
iteration and provides `it'` as the succeeding iterator.
Allowing `skip` steps is necessary to generate efficient code from a loop over an iterator.
-/
| skip : (it : α) IterStep α β
/--
`IterStep.done` describes the situation that an iterator has finished and will neither emit
more values nor cause any monadic effects. In this case, no succeeding iterator is provided.
-/
| done : IterStep α β
/--
Returns the succeeding iterator stored in an iterator step or `none` if the step is `.done`
and the iterator has finished.
-/
@[expose]
def IterStep.successor : IterStep α β Option α
| .yield it _ => some it
| .skip it => some it
| .done => none
/--
If present, applies `f` to the iterator of an `IterStep` and replaces the iterator
with the result of the application of `f`.
-/
@[always_inline, inline, expose]
def IterStep.mapIterator {α' : Type u'} (f : α α') : IterStep α β IterStep α' β
| .yield it out => .yield (f it) out
| .skip it => .skip (f it)
| .done => .done
@[simp]
theorem IterStep.mapIterator_yield {α' : Type u'} {f : α α'} {it : α} {out : β} :
(IterStep.yield it out).mapIterator f = IterStep.yield (f it) out :=
rfl
@[simp]
theorem IterStep.mapIterator_skip {α' : Type u'} {f : α α'} {it : α} :
(IterStep.skip it (β := β)).mapIterator f = IterStep.skip (f it) :=
rfl
@[simp]
theorem IterStep.mapIterator_done {α' : Type u'} {f : α α'} :
(IterStep.done (α := α) (β := β)).mapIterator f = IterStep.done :=
rfl
@[simp]
theorem IterStep.mapIterator_mapIterator {α' : Type u'} {α'' : Type u''}
{f : α α'} {g : α' α''} {step : IterStep α β} :
(step.mapIterator f).mapIterator g = step.mapIterator (g f) := by
cases step <;> rfl
theorem IterStep.mapIterator_comp {α' : Type u'} {α'' : Type u''}
{f : α α'} {g : α' α''} :
IterStep.mapIterator (β := β) (g f) = mapIterator g mapIterator f := by
apply funext
exact fun _ => mapIterator_mapIterator.symm
@[simp]
theorem IterStep.mapIterator_id {step : IterStep α β} :
step.mapIterator id = step := by
cases step <;> rfl
/--
A variant of `IterStep` that bundles the step together with a proof that it is "plausible".
The plausibility predicate will later be chosen to assert that a state is a plausible successor
of another state. Having this proof bundled up with the step is important for termination proofs.
See `IterM.Step` and `Iter.Step` for the concrete choice of the plausibility predicate.
-/
@[expose]
def PlausibleIterStep (IsPlausibleStep : IterStep α β Prop) := Subtype IsPlausibleStep
/--
Match pattern for the `yield` case. See also `IterStep.yield`.
-/
@[match_pattern, simp, expose]
def PlausibleIterStep.yield {IsPlausibleStep : IterStep α β Prop}
(it' : α) (out : β) (h : IsPlausibleStep (.yield it' out)) :
PlausibleIterStep IsPlausibleStep :=
.yield it' out, h
/--
Match pattern for the `skip` case. See also `IterStep.skip`.
-/
@[match_pattern, simp, expose]
def PlausibleIterStep.skip {IsPlausibleStep : IterStep α β Prop}
(it' : α) (h : IsPlausibleStep (.skip it')) : PlausibleIterStep IsPlausibleStep :=
.skip it', h
/--
Match pattern for the `done` case. See also `IterStep.done`.
-/
@[match_pattern, simp, expose]
def PlausibleIterStep.done {IsPlausibleStep : IterStep α β Prop}
(h : IsPlausibleStep .done) : PlausibleIterStep IsPlausibleStep :=
.done, h
/--
A more convenient `cases` eliminator for `PlausibleIterStep`.
-/
@[elab_as_elim, cases_eliminator]
abbrev PlausibleIterStep.casesOn {IsPlausibleStep : IterStep α β Prop}
{motive : PlausibleIterStep IsPlausibleStep Sort x} (s : PlausibleIterStep IsPlausibleStep)
(yield : it' out h, motive .yield it' out, h)
(skip : it' h, motive .skip it', h)
(done : h, motive .done, h) : motive s :=
match s with
| .yield it' out h => yield it' out h
| .skip it' h => skip it' h
| .done h => done h
end IterStep
/--
The typeclass providing the step function of an iterator in `Iter (α := α) β` or
`IterM (α := α) m β`.
In order to allow intrinsic termination proofs when iterating with the `step` function, the
step object is bundled with a proof that it is a "plausible" step for the given current iterator.
-/
class Iterator (α : Type w) (m : Type w Type w') (β : outParam (Type w)) where
IsPlausibleStep : IterM (α := α) m β IterStep (IterM (α := α) m β) β Prop
step : (it : IterM (α := α) m β) m (PlausibleIterStep <| IsPlausibleStep it)
section Monadic
/--
Converts wraps the state of an iterator into an `IterM` object.
-/
@[always_inline, inline, expose]
def toIterM {α : Type w} (it : α) (m : Type w Type w') (β : Type w) :
IterM (α := α) m β :=
it
@[simp]
theorem toIterM_internalState {α m β} (it : IterM (α := α) m β) :
toIterM it.internalState m β = it :=
rfl
@[simp]
theorem internalState_toIterM {α m β} (it : α) :
(toIterM it m β).internalState = it :=
rfl
/--
Asserts that certain step is plausibly the successor of a given iterator. What "plausible" means
is up to the `Iterator` instance but it should be strong enough to allow termination proofs.
-/
@[expose]
abbrev IterM.IsPlausibleStep {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] :
IterM (α := α) m β IterStep (IterM (α := α) m β) β Prop :=
Iterator.IsPlausibleStep (α := α) (m := m)
/--
The type of the step object returned by `IterM.step`, containing an `IterStep`
and a proof that this is a plausible step for the given iterator.
-/
@[expose]
abbrev IterM.Step {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) :=
PlausibleIterStep it.IsPlausibleStep
/--
Asserts that a certain output value could plausibly be emitted by the given iterator in its next
step.
-/
@[expose]
def IterM.IsPlausibleOutput {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) (out : β) : Prop :=
it', it.IsPlausibleStep (.yield it' out)
/--
Asserts that a certain iterator `it'` could plausibly be the directly succeeding iterator of another
given iterator `it`.
-/
@[expose]
def IterM.IsPlausibleSuccessorOf {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it' it : IterM (α := α) m β) : Prop :=
step, step.successor = some it' it.IsPlausibleStep step
/--
Asserts that a certain iterator `it'` could plausibly be the directly succeeding iterator of another
given iterator `it` while no value is emitted (see `IterStep.skip`).
-/
@[expose]
def IterM.IsPlausibleSkipSuccessorOf {α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (it' it : IterM (α := α) m β) : Prop :=
it.IsPlausibleStep (.skip it')
/--
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
-/
@[always_inline, inline]
def IterM.step {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : m it.Step :=
Iterator.step it
end Monadic
section Pure
/--
Asserts that certain step is plausibly the successor of a given iterator. What "plausible" means
is up to the `Iterator` instance but it should be strong enough to allow termination proofs.
-/
@[expose]
def Iter.IsPlausibleStep {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) (step : IterStep (Iter (α := α) β) β) : Prop :=
it.toIterM.IsPlausibleStep (step.mapIterator Iter.toIterM)
/--
Asserts that a certain iterator `it` could plausibly yield the value `out` after an arbitrary
number of steps.
-/
inductive IterM.IsPlausibleIndirectOutput {α β : Type w} {m : Type w Type w'} [Iterator α m β]
: IterM (α := α) m β β Prop where
| direct {it : IterM (α := α) m β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
| indirect {it it' : IterM (α := α) m β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
/--
The type of the step object returned by `Iter.step`, containing an `IterStep`
and a proof that this is a plausible step for the given iterator.
-/
@[expose]
def Iter.Step {α : Type w} {β : Type w} [Iterator α Id β] (it : Iter (α := α) β) :=
PlausibleIterStep (Iter.IsPlausibleStep it)
/--
Converts an `Iter.Step` into an `IterM.Step`.
-/
@[always_inline, inline]
def Iter.Step.toMonadic {α : Type w} {β : Type w} [Iterator α Id β] {it : Iter (α := α) β}
(step : it.Step) : it.toIterM.Step :=
step.val.mapIterator Iter.toIterM, step.property
/--
Converts an `IterM.Step` into an `Iter.Step`.
-/
@[always_inline, inline, expose]
def IterM.Step.toPure {α : Type w} {β : Type w} [Iterator α Id β] {it : IterM (α := α) Id β}
(step : it.Step) : it.toIter.Step :=
step.val.mapIterator IterM.toIter, (by simp [Iter.IsPlausibleStep, step.property])
@[simp]
theorem IterM.Step.toPure_yield {α β : Type w} [Iterator α Id β] {it : IterM (α := α) Id β}
{it' out h} : IterM.Step.toPure (.yield it' out, h : it.Step) = .yield it'.toIter out h :=
rfl
@[simp]
theorem IterM.Step.toPure_skip {α β : Type w} [Iterator α Id β] {it : IterM (α := α) Id β}
{it' h} : IterM.Step.toPure (.skip it', h : it.Step) = .skip it'.toIter h :=
rfl
@[simp]
theorem IterM.Step.toPure_done {α β : Type w} [Iterator α Id β] {it : IterM (α := α) Id β}
{h} : IterM.Step.toPure (.done, h : it.Step) = .done h :=
rfl
/--
Asserts that a certain output value could plausibly be emitted by the given iterator in its next
step.
-/
@[expose]
def Iter.IsPlausibleOutput {α : Type w} {β : Type w} [Iterator α Id β]
(it : Iter (α := α) β) (out : β) : Prop :=
it.toIterM.IsPlausibleOutput out
/--
Asserts that a certain iterator `it'` could plausibly be the directly succeeding iterator of another
given iterator `it`.
-/
@[expose]
def Iter.IsPlausibleSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β]
(it' it : Iter (α := α) β) : Prop :=
it'.toIterM.IsPlausibleSuccessorOf it.toIterM
/--
Asserts that a certain iterator `it` could plausibly yield the value `out` after an arbitrary
number of steps.
-/
inductive Iter.IsPlausibleIndirectOutput {α β : Type w} [Iterator α Id β] :
Iter (α := α) β β Prop where
| direct {it : Iter (α := α) β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
| indirect {it it' : Iter (α := α) β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
theorem Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM {α β : Type w}
[Iterator α Id β] {it : Iter (α := α) β} {out : β} :
it.IsPlausibleIndirectOutput out it.toIterM.IsPlausibleIndirectOutput out := by
constructor
· intro h
induction h with
| direct h =>
exact .direct h
| indirect h _ ih =>
exact .indirect h ih
· intro h
rw [ Iter.toIter_toIterM (it := it)]
generalize it.toIterM = it at h
induction h with
| direct h =>
exact .direct h
| indirect h h' ih =>
rename_i it it' out
replace h : it'.toIter.IsPlausibleSuccessorOf it.toIter := h
exact .indirect (α := α) h ih
/--
Asserts that a certain iterator `it'` could plausibly be the directly succeeding iterator of another
given iterator `it` while no value is emitted (see `IterStep.skip`).
-/
def Iter.IsPlausibleSkipSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β]
(it' it : Iter (α := α) β) : Prop :=
it'.toIterM.IsPlausibleSkipSuccessorOf it.toIterM
/--
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
-/
@[always_inline, inline, expose]
def Iter.step {α β : Type w} [Iterator α Id β] (it : Iter (α := α) β) : it.Step :=
it.toIterM.step.run.toPure
end Pure
section Finite
/--
`Finite α m` asserts that `IterM (α := α) m` terminates after finitely many steps. Technically,
this means that the relation of plausible successors is well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySteps` as a termination measure.
-/
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
/--
This type is a wrapper around `IterM` so that it becomes a useful termination measure for
recursion over finite iterators. See also `IterM.finitelyManySteps` and `Iter.finitelyManySteps`.
-/
structure IterM.TerminationMeasures.Finite
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
it : IterM (α := α) m β
/--
The relation of plausible successors on `IterM.TerminationMeasures.Finite`. It is well-founded
if there is a `Finite` instance.
-/
@[expose]
def IterM.TerminationMeasures.Finite.Rel
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] :
TerminationMeasures.Finite α m TerminationMeasures.Finite α m Prop :=
Relation.TransGen <| InvImage IterM.IsPlausibleSuccessorOf IterM.TerminationMeasures.Finite.it
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Finite α m] : WellFoundedRelation (IterM.TerminationMeasures.Finite α m) where
rel := IterM.TerminationMeasures.Finite.Rel
wf := by exact (InvImage.wf _ Finite.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
(see also `Finite`).
-/
@[expose]
def IterM.finitelyManySteps {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
it
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySteps`.
-/
theorem IterM.TerminationMeasures.Finite.rel_of_yield
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{it it' : IterM (α := α) m β} {out : β} (h : it.IsPlausibleStep (.yield it' out)) :
Rel it' it := by
exact .single _, rfl, h
@[inherit_doc IterM.TerminationMeasures.Finite.rel_of_yield]
theorem IterM.TerminationMeasures.Finite.rel_of_skip
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{it it' : IterM (α := α) m β} (h : it.IsPlausibleStep (.skip it')) :
Rel it' it := by
exact .single _, rfl, h
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
first
| exact IterM.TerminationMeasures.Finite.rel_of_yield _
| exact IterM.TerminationMeasures.Finite.rel_of_skip _
| fail)
@[inherit_doc IterM.finitelyManySteps, expose]
def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySteps`.
-/
theorem Iter.TerminationMeasures.Finite.rel_of_yield
{α : Type w} {β : Type w} [Iterator α Id β]
{it it' : Iter (α := α) β} {out : β} (h : it.IsPlausibleStep (.yield it' out)) :
IterM.TerminationMeasures.Finite.Rel it'.toIterM it.toIterM :=
IterM.TerminationMeasures.Finite.rel_of_yield h
@[inherit_doc Iter.TerminationMeasures.Finite.rel_of_yield]
theorem Iter.TerminationMeasures.Finite.rel_of_skip
{α : Type w} {β : Type w} [Iterator α Id β]
{it it' : Iter (α := α) β} (h : it.IsPlausibleStep (.skip it')) :
IterM.TerminationMeasures.Finite.Rel it'.toIterM it.toIterM :=
IterM.TerminationMeasures.Finite.rel_of_skip h
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
first
| exact Iter.TerminationMeasures.Finite.rel_of_yield _
| exact Iter.TerminationMeasures.Finite.rel_of_skip _
| fail)
theorem IterM.isPlausibleSuccessorOf_of_yield
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{it' it : IterM (α := α) m β} {out : β} (h : it.IsPlausibleStep (.yield it' out)) :
it'.IsPlausibleSuccessorOf it :=
_, rfl, h
theorem IterM.isPlausibleSuccessorOf_of_skip
{α m β} [Iterator α m β] {it it' : IterM (α := α) m β}
(h : it.IsPlausibleStep (.skip it')) :
it'.IsPlausibleSuccessorOf it :=
_, rfl, h
end Finite
section Productive
/--
`Productive α m` asserts that `IterM (α := α) m` terminates or emits a value after finitely many
skips. Technically, this means that the relation of plausible successors during skips is
well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySkips` as a termination measure.
-/
class Productive (α m) {β} [Iterator α m β] : Prop where
wf : WellFounded (IterM.IsPlausibleSkipSuccessorOf (α := α) (m := m))
/--
This type is a wrapper around `IterM` so that it becomes a useful termination measure for
recursion over productive iterators. See also `IterM.finitelyManySkips` and `Iter.finitelyManySkips`.
-/
structure IterM.TerminationMeasures.Productive
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
it : IterM (α := α) m β
/--
The relation of plausible successors while skipping on `IterM.TerminationMeasures.Productive`.
It is well-founded if there is a `Productive` instance.
-/
@[expose]
def IterM.TerminationMeasures.Productive.Rel
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] :
TerminationMeasures.Productive α m TerminationMeasures.Productive α m Prop :=
Relation.TransGen <| InvImage IterM.IsPlausibleSkipSuccessorOf IterM.TerminationMeasures.Productive.it
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Productive α m] : WellFoundedRelation (IterM.TerminationMeasures.Productive α m) where
rel := IterM.TerminationMeasures.Productive.Rel
wf := by exact (InvImage.wf _ Productive.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a productive
iterator (see also `Productive`).
-/
@[expose]
def IterM.finitelyManySkips {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `IterM.finitelyManySkips`.
-/
theorem IterM.TerminationMeasures.Productive.rel_of_skip
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{it it' : IterM (α := α) m β} (h : it.IsPlausibleStep (.skip it')) :
Rel it' it :=
.single h
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
first
| exact IterM.TerminationMeasures.Productive.rel_of_skip _
| fail)
@[inherit_doc IterM.finitelyManySkips, expose]
def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Productive α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips
/--
This theorem is used by a `decreasing_trivial` extension. It powers automatic termination proofs
with `Iter.finitelyManySkips`.
-/
theorem Iter.TerminationMeasures.Productive.rel_of_skip
{α : Type w} {β : Type w} [Iterator α Id β]
{it it' : Iter (α := α) β} (h : it.IsPlausibleStep (.skip it')) :
IterM.TerminationMeasures.Productive.Rel it'.toIterM it.toIterM :=
IterM.TerminationMeasures.Productive.rel_of_skip h
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
first
| exact Iter.TerminationMeasures.Productive.rel_of_skip _
| fail)
instance [Iterator α m β] [Finite α m] : Productive α m where
wf := by
apply Subrelation.wf (r := IterM.IsPlausibleSuccessorOf)
· intro it' it h
exact IterM.isPlausibleSuccessorOf_of_skip h
· exact Finite.wf
end Productive
end Iterators
export Iterators (Iter IterM)
end Std

View File

@@ -0,0 +1,13 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Consumers.Monadic
import Init.Data.Iterators.Consumers.Access
import Init.Data.Iterators.Consumers.Collect
import Init.Data.Iterators.Consumers.Loop
import Init.Data.Iterators.Consumers.Partial

View File

@@ -0,0 +1,54 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Consumers.Partial
namespace Std.Iterators
/--
If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
This function requires a `Productive` instance proving that the iterator will always emit a value
after a finite number of skips. If the iterator is not productive or such an instance is not
available, consider using `it.allowNontermination.atIdxSlow?` instead of `it.atIdxSlow?`. However,
it is not possible to formally verify the behavior of the partial variant.
-/
@[specialize]
def Iter.atIdxSlow? {α β} [Iterator α Id β] [Productive α Id]
(n : Nat) (it : Iter (α := α) β) : Option β :=
match it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => it'.atIdxSlow? k
| .skip it' _ => it'.atIdxSlow? n
| .done _ => none
termination_by (n, it.finitelyManySkips)
/--
If possible, takes `n` steps with the iterator `it` and
returns the `n`-th emitted value, or `none` if `it` finished
before emitting `n` values.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Productive` instance, consider using `Iter.atIdxSlow?` instead.
-/
@[specialize]
partial def Iter.Partial.atIdxSlow? {α β} [Iterator α Id β] [Monad Id]
(n : Nat) (it : Iter.Partial (α := α) β) : Option β := do
match it.it.step with
| .yield it' out _ =>
match n with
| 0 => some out
| k + 1 => (it' : Iter.Partial (α := α) β).atIdxSlow? k
| .skip it' _ => (it' : Iter.Partial (α := α) β).atIdxSlow? n
| .done _ => none
end Std.Iterators

View File

@@ -0,0 +1,73 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Basic
import Init.Data.Iterators.Consumers.Partial
import Init.Data.Iterators.Consumers.Monadic.Collect
/-!
# Collectors
This module provides consumers that collect the values emitted by an iterator in a data structure.
Concretely, the following operations are provided:
* `Iter.toList`, collecting the values in a list
* `Iter.toListRev`, collecting the values in a list in reverse order but more efficiently
* `Iter.toArray`, collecting the values in an array
Some operations are implemented using the `IteratorCollect` and `IteratorCollectPartial`
typeclasses.
-/
namespace Std.Iterators
@[always_inline, inline, inherit_doc IterM.toArray]
def Iter.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : Array β :=
it.toIterM.toArray.run
@[always_inline, inline, inherit_doc IterM.Partial.toArray]
def Iter.Partial.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : Array β :=
it.it.toIterM.allowNontermination.toArray.run
@[always_inline, inline, inherit_doc IterM.toListRev]
def Iter.toListRev {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] (it : Iter (α := α) β) : List β :=
it.toIterM.toListRev.run
@[always_inline, inline, inherit_doc IterM.Partial.toListRev]
def Iter.Partial.toListRev {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter.Partial (α := α) β) : List β :=
it.it.toIterM.allowNontermination.toListRev.run
@[always_inline, inline, inherit_doc IterM.toList]
def Iter.toList {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : List β :=
it.toIterM.toList.run
@[always_inline, inline, inherit_doc IterM.Partial.toList]
def Iter.Partial.toList {α : Type w} {β : Type w}
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : List β :=
it.it.toIterM.allowNontermination.toList.run
/--
This class charaterizes how the plausibility behavior (`IsPlausibleStep`) and the actual iteration
behavior (`it.step`) should relate to each other for pure iterators. Intuitively, a step should
only be plausible if it is possible. For simplicity's sake, the actual definition is weaker but
presupposes that the iterator is finite.
This is an experimental instance and it should not be explicitly used downstream of the standard
library.
-/
class LawfulPureIterator (α : Type w) [Iterator α Id β]
[Finite α Id] [IteratorCollect α Id Id] where
mem_toList_iff_isPlausibleIndirectOutput {it : Iter (α := α) β} {out : β} :
out it.toList it.IsPlausibleIndirectOutput out
end Std.Iterators

View File

@@ -0,0 +1,139 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Consumers.Monadic.Loop
import Init.Data.Iterators.Consumers.Partial
/-!
# Loop consumers
This module provides consumers that iterate over a given iterator, applying a certain user-supplied
function in every iteration. Concretely, the following operations are provided:
* `ForIn` instances
* `Iter.fold`, the analogue of `List.foldl`
* `Iter.foldM`, the analogue of `List.foldlM`
These operations are implemented using the `IteratorLoop` and `IteratorLoopPartial` typeclasses.
-/
namespace Std.Iterators
/--
A `ForIn'` instance for iterators. Its generic membership relation is not easy to use,
so this is not marked as `instance`. This way, more convenient instances can be built on top of it
or future library improvements will make it more comfortable.
-/
def Iter.instForIn' {α : Type w} {β : Type w} {n : Type w Type w'} [Monad n]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id n] :
ForIn' n (Iter (α := α) β) β fun it out => it.IsPlausibleIndirectOutput out where
forIn' it init f :=
IteratorLoop.finiteForIn' (fun δ (c : Id δ) => pure c.run) |>.forIn' it.toIterM init
fun out h acc =>
f out (Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM.mpr h) acc
instance (α : Type w) (β : Type w) (n : Type w Type w') [Monad n]
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id n] :
ForIn n (Iter (α := α) β) β :=
haveI : ForIn' n (Iter (α := α) β) β _ := Iter.instForIn'
instForInOfForIn'
instance (α : Type w) (β : Type w) (n : Type w Type w') [Monad n]
[Iterator α Id β] [IteratorLoopPartial α Id n] :
ForIn n (Iter.Partial (α := α) β) β where
forIn it init f :=
letI : MonadLift Id n := pure
ForIn.forIn it.it.toIterM.allowNontermination init f
instance {m : Type w Type w'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] :
ForM m (Iter (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type w Type w'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] :
ForM m (Iter.Partial (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldlM`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.foldM` instead of `it.foldM`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def Iter.foldM {m : Type w Type w'} [Monad m]
{α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id m] (f : γ β m γ)
(init : γ) (it : Iter (α := α) β) : m γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldlM`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.foldM` instead.
-/
@[always_inline, inline]
def Iter.Partial.foldM {m : Type w Type w'} [Monad m]
{α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
[IteratorLoopPartial α Id m] (f : γ β m γ)
(init : γ) (it : Iter.Partial (α := α) β) : m γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.fold` instead of `it.fold`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def Iter.fold {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] (f : γ β γ)
(init : γ) (it : Iter (α := α) β) : γ :=
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.fold` instead.
-/
@[always_inline, inline]
def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (f : γ β γ)
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
@[always_inline, inline, inherit_doc IterM.size]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
(it : Iter (α := α) β) : Nat :=
(IteratorSize.size it.toIterM).run.down
@[always_inline, inline, inherit_doc IterM.Partial.size]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSizePartial α Id]
(it : Iter (α := α) β) : Nat :=
(IteratorSizePartial.size it.toIterM).run.down
end Std.Iterators

View File

@@ -0,0 +1,11 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Consumers.Monadic.Collect
import Init.Data.Iterators.Consumers.Monadic.Loop
import Init.Data.Iterators.Consumers.Monadic.Partial

View File

@@ -0,0 +1,262 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.Data.Iterators.Consumers.Monadic.Partial
import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
/-!
# Collectors
This module provides consumers that collect the values emitted by an iterator in a data structure.
Concretely, the following operations are provided:
* `IterM.toList`, collecting the values in a list
* `IterM.toListRev`, collecting the values in a list in reverse order but more efficiently
* `IterM.toArray`, collecting the values in an array
Some producers and combinators provide specialized implementations. These are captured by the
`IteratorCollect` and `IteratorCollectPartial` typeclasses. They should be implemented by all
types of iterators. A default implementation is provided. The typeclass `LawfulIteratorCollect`
asserts that an `IteratorCollect` instance equals the default implementation.
-/
namespace Std.Iterators
open Std.Internal
section Typeclasses
/--
`IteratorCollect α m` provides efficient implementations of collectors for `α`-based
iterators. Right now, it is limited to a potentially optimized `toArray` implementation.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
Note: For this to be compositional enough to be useful, `toArrayMapped` would need to accept a
termination proof for the specific mapping function used instead of the blanket `Finite α m`
instance. Otherwise, most combinators like `map` cannot implement their own instance relying on
the instance of their base iterators. However, fixing this is currently low priority.
-/
class IteratorCollect (α : Type w) (m : Type w Type w') (n : Type w Type w'')
{β : Type w} [Iterator α m β] where
/--
Maps the emitted values of an iterator using the given function and collects the results in an
`Array`. This is an internal implementation detail. Consider using `it.map f |>.toArray` instead.
-/
toArrayMapped [Finite α m] :
(lift : δ : Type w m δ n δ) {γ : Type w} (β n γ) IterM (α := α) m β n (Array γ)
/--
`IteratorCollectPartial α m` provides efficient implementations of collectors for `α`-based
iterators. Right now, it is limited to a potentially optimized partial `toArray` implementation.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
-/
class IteratorCollectPartial (α : Type w) (m : Type w Type w') (n : Type w Type w'')
{β : Type w} [Iterator α m β] where
/--
Maps the emitted values of an iterator using the given function and collects the results in an
`Array`. This is an internal implementation detail.
Consider using `it.map f |>.allowNontermination.toArray` instead.
-/
toArrayMappedPartial :
(lift : δ : Type w m δ n δ) {γ : Type w} (β n γ) IterM (α := α) m β n (Array γ)
end Typeclasses
section ToArray
/--
This is an internal function used in `IteratorCollect.defaultImplementation`.
It iterates over an iterator and applies `f` whenever a value is emitted before inserting the result
of `f` into an array.
-/
@[always_inline, inline]
def IterM.DefaultConsumers.toArrayMapped {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] [Finite α m]
(lift : α : Type w m α n α) {γ : Type w} (f : β n γ)
(it : IterM (α := α) m β) : n (Array γ) :=
go it #[]
where
@[specialize]
go [Monad n] [Finite α m] (it : IterM (α := α) m β) a := letI : MonadLift m n := lift (α := _); do
match it.step with
| .yield it' b _ => go it' (a.push ( f b))
| .skip it' _ => go it' a
| .done _ => return a
termination_by it.finitelyManySteps
/--
This is the default implementation of the `IteratorLoop` class.
It simply iterates through the iterator using `IterM.step`, incrementally building up the desired
data structure. For certain iterators, more efficient implementations are possible and should be
used instead.
-/
@[always_inline, inline]
def IteratorCollect.defaultImplementation {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] :
IteratorCollect α m n where
toArrayMapped := IterM.DefaultConsumers.toArrayMapped
/--
Asserts that a given `IteratorCollect` instance is equal to `IteratorCollect.defaultImplementation`.
(Even though equal, the given instance might be vastly more efficient.)
-/
class LawfulIteratorCollect (α : Type w) (m : Type w Type w') (n : Type w Type w'')
{β : Type w} [Monad m] [Monad n] [Iterator α m β] [i : IteratorCollect α m n] where
lawful_toArrayMapped : lift [LawfulMonadLiftFunction lift] [Finite α m],
i.toArrayMapped lift (α := α) (γ := γ)
= IteratorCollect.defaultImplementation.toArrayMapped lift
theorem LawfulIteratorCollect.toArrayMapped_eq {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad m] [Monad n] [Iterator α m β] [Finite α m] [IteratorCollect α m n]
[hl : LawfulIteratorCollect α m n] {lift : δ : Type w m δ n δ}
[LawfulMonadLiftFunction lift]
{f : β n γ} {it : IterM (α := α) m β} :
IteratorCollect.toArrayMapped lift f it (m := m) =
IterM.DefaultConsumers.toArrayMapped lift f it (m := m) := by
rw [lawful_toArrayMapped]; rfl
instance (α β : Type w) (m : Type w Type w') (n : Type w Type w'') [Monad n]
[Iterator α m β] [Monad m] [Iterator α m β] [Finite α m] :
haveI : IteratorCollect α m n := .defaultImplementation
LawfulIteratorCollect α m n :=
letI : IteratorCollect α m n := .defaultImplementation
fun _ => rfl
/--
This is an internal function used in `IteratorCollectPartial.defaultImplementation`.
It iterates over an iterator and applies `f` whenever a value is emitted before inserting the result
of `f` into an array.
-/
@[always_inline, inline]
partial def IterM.DefaultConsumers.toArrayMappedPartial {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β]
(lift : {α : Type w} m α n α) {γ : Type w} (f : β n γ)
(it : IterM (α := α) m β) : n (Array γ) :=
go it #[]
where
@[specialize]
go [Monad n] (it : IterM (α := α) m β) a := letI : MonadLift m n := lift; do
match it.step with
| .yield it' b _ => go it' (a.push ( f b))
| .skip it' _ => go it' a
| .done _ => return a
/--
This is the default implementation of the `IteratorLoopPartial` class.
It simply iterates through the iterator using `IterM.step`, incrementally building up the desired
data structure. For certain iterators, more efficient implementations are possible and should be
used instead.
-/
@[always_inline, inline]
def IteratorCollectPartial.defaultImplementation {α β : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] :
IteratorCollectPartial α m n where
toArrayMappedPartial := IterM.DefaultConsumers.toArrayMappedPartial
/--
Traverses the given iterator and stores the emitted values in an array.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.toArray` instead of `it.toArray`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def IterM.toArray {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] [IteratorCollect α m m]
(it : IterM (α := α) m β) : m (Array β) :=
IteratorCollect.toArrayMapped (fun _ => id) pure it
/--
Traverses the given iterator and stores the emitted values in an array.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.toArray` instead.
-/
@[always_inline, inline]
def IterM.Partial.toArray {α : Type w} {m : Type w Type w'} {β : Type w} [Monad m]
[Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorCollectPartial α m m] : m (Array β) :=
IteratorCollectPartial.toArrayMappedPartial (fun _ => id) pure it.it
end ToArray
/--
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.toListRev` instead of `it.toListRev`. However, it is not possible to
formally verify the behavior of the partial variant.
-/
@[inline]
def IterM.toListRev {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [Finite α m] (it : IterM (α := α) m β) : m (List β) :=
go it []
where
go [Finite α m] it bs := do
match it.step with
| .yield it' b _ => go it' (b :: bs)
| .skip it' _ => go it' bs
| .done _ => return bs
termination_by it.finitelyManySteps
/--
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.toListRev` instead.
-/
@[always_inline, inline]
partial def IterM.Partial.toListRev {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] (it : IterM.Partial (α := α) m β) : m (List β) :=
go it.it []
where
@[specialize]
go it bs := do
match it.step with
| .yield it' b _ => go it' (b :: bs)
| .skip it' _ => go it' bs
| .done _ => return bs
/--
Traverses the given iterator and stores the emitted values in a list. Because
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.toList` instead of `it.toList`. However, it is not possible to
formally verify the behavior of the partial variant.
-/
@[always_inline, inline]
def IterM.toList {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [Finite α m] [IteratorCollect α m m] (it : IterM (α := α) m β) : m (List β) :=
Array.toList <$> IterM.toArray it
/--
Traverses the given iterator and stores the emitted values in a list. Because
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.toList` instead.
-/
@[always_inline, inline]
def IterM.Partial.toList {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorCollectPartial α m m] :
m (List β) :=
Array.toList <$> it.toArray
end Std.Iterators

View File

@@ -0,0 +1,458 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.RCases
import Init.Data.Iterators.Basic
import Init.Data.Iterators.Consumers.Monadic.Partial
/-!
# Loop-based consumers
This module provides consumers that iterate over a given iterator, applying a certain user-supplied
function in every iteration. Concretely, the following operations are provided:
* `ForIn` instances
* `IterM.fold`, the analogue of `List.foldl`
* `IterM.foldM`, the analogue of `List.foldlM`
* `IterM.drain`, which iterates over the whole iterator and discards all emitted values. It can
be used to apply the monadic effects of the iterator.
Some producers and combinators provide specialized implementations. These are captured by the
`IteratorLoop` and `IteratorLoopPartial` typeclasses. They should be implemented by all
types of iterators. A default implementation is provided. The typeclass `LawfulIteratorLoop`
asserts that an `IteratorLoop` instance equals the default implementation.
-/
namespace Std.Iterators
section Typeclasses
/--
Relation that needs to be well-formed in order for a loop over an iterator to terminate.
It is assumed that the `plausible_forInStep` predicate relates the input and output of the
stepper function.
-/
def IteratorLoop.rel (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
{γ : Type x} (plausible_forInStep : β γ ForInStep γ Prop)
(p' p : IterM (α := α) m β × γ) : Prop :=
( b, p.1.IsPlausibleStep (.yield p'.1 b) plausible_forInStep b p.2 (.yield p'.2))
(p.1.IsPlausibleStep (.skip p'.1) p'.2 = p.2)
/--
Asserts that `IteratorLoop.rel` is well-founded.
-/
def IteratorLoop.WellFounded (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
{γ : Type x} (plausible_forInStep : β γ ForInStep γ Prop) : Prop :=
_root_.WellFounded (IteratorLoop.rel α m plausible_forInStep)
/--
`IteratorLoop α m` provides efficient implementations of loop-based consumers for `α`-based
iterators. The basis is a `ForIn`-style loop construct with the complication that it can be used
for infinite iterators, too -- given a proof that the given loop will nevertheless terminate.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
-/
class IteratorLoop (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
(n : Type w Type w'') where
forIn : (_lift : (γ : Type w) m γ n γ) (γ : Type w),
(plausible_forInStep : β γ ForInStep γ Prop)
IteratorLoop.WellFounded α m plausible_forInStep
(it : IterM (α := α) m β) γ
((b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (plausible_forInStep b c)))
n γ
/--
`IteratorLoopPartial α m` provides efficient implementations of loop-based consumers for `α`-based
iterators. The basis is a partial, i.e. potentially nonterminating, `ForIn` instance.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
-/
class IteratorLoopPartial (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
(n : Type w Type w'') where
forInPartial : (_lift : (γ : Type w) m γ n γ) {γ : Type w},
(it : IterM (α := α) m β) γ
((b : β) it.IsPlausibleIndirectOutput b (c : γ) n (ForInStep γ)) n γ
/--
`IteratorSize α m` provides an implementation of the `IterM.size` function.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
-/
class IteratorSize (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
size : IterM (α := α) m β m (ULift Nat)
/--
`IteratorSizePartial α m` provides an implementation of the `IterM.Partial.size` function that
can be used as `it.allowTermination.size`.
This class is experimental and users of the iterator API should not explicitly depend on it.
They can, however, assume that consumers that require an instance will work for all iterators
provided by the standard library.
-/
class IteratorSizePartial (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
size : IterM (α := α) m β m (ULift Nat)
end Typeclasses
private def IteratorLoop.WFRel {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{γ : Type x} {plausible_forInStep : β γ ForInStep γ Prop}
(_wf : WellFounded α m plausible_forInStep) :=
IterM (α := α) m β × γ
private def IteratorLoop.WFRel.mk {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{γ : Type x} {plausible_forInStep : β γ ForInStep γ Prop}
(wf : WellFounded α m plausible_forInStep) (it : IterM (α := α) m β) (c : γ) :
IteratorLoop.WFRel wf :=
(it, c)
private instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{γ : Type x} {plausible_forInStep : β γ ForInStep γ Prop}
(wf : IteratorLoop.WellFounded α m plausible_forInStep) :
WellFoundedRelation (IteratorLoop.WFRel wf) where
rel := IteratorLoop.rel α m plausible_forInStep
wf := wf
/--
This is the loop implementation of the default instance `IteratorLoop.defaultImplementation`.
-/
@[specialize]
def IterM.DefaultConsumers.forIn' {m : Type w Type w'} {α : Type w} {β : Type w}
[Iterator α m β]
{n : Type w Type w''} [Monad n]
(lift : γ, m γ n γ) (γ : Type w)
(plausible_forInStep : β γ ForInStep γ Prop)
(wf : IteratorLoop.WellFounded α m plausible_forInStep)
(it : IterM (α := α) m β) (init : γ)
(f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (plausible_forInStep b c))) : n γ :=
haveI : WellFounded _ := wf
letI : MonadLift m n := fun {γ} => lift γ
do
match it.step with
| .yield it' out h =>
match f out (.direct _, h) init with
| .yield c, _ =>
IterM.DefaultConsumers.forIn' lift _ plausible_forInStep wf it' c
(fun out h' acc => f out (.indirect _, rfl, h h') acc)
| .done c, _ => return c
| .skip it' h =>
IterM.DefaultConsumers.forIn' lift _ plausible_forInStep wf it' init
(fun out h' acc => f out (.indirect _, rfl, h h') acc)
| .done _ => return init
termination_by IteratorLoop.WFRel.mk wf it init
decreasing_by
· exact Or.inl out, _, _
· exact Or.inr _, rfl
/--
This is the default implementation of the `IteratorLoop` class.
It simply iterates through the iterator using `IterM.step`. For certain iterators, more efficient
implementations are possible and should be used instead.
-/
@[always_inline, inline]
def IteratorLoop.defaultImplementation {α : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Monad n] [Iterator α m β] :
IteratorLoop α m n where
forIn lift := IterM.DefaultConsumers.forIn' lift
/--
Asserts that a given `IteratorLoop` instance is equal to `IteratorLoop.defaultImplementation`.
(Even though equal, the given instance might be vastly more efficient.)
-/
class LawfulIteratorLoop (α : Type w) (m : Type w Type w') (n : Type w Type w'')
[Monad n] [Iterator α m β] [Finite α m] [i : IteratorLoop α m n] where
lawful : i = .defaultImplementation
/--
This is the loop implementation of the default instance `IteratorLoopPartial.defaultImplementation`.
-/
@[specialize]
partial def IterM.DefaultConsumers.forInPartial {m : Type w Type w'} {α : Type w} {β : Type w}
[Iterator α m β]
{n : Type w Type w''} [Monad n]
(lift : γ, m γ n γ) (γ : Type w)
(it : IterM (α := α) m β) (init : γ)
(f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (ForInStep γ)) : n γ :=
letI : MonadLift m n := fun {γ} => lift γ
do
match it.step with
| .yield it' out h =>
match f out (.direct _, h) init with
| .yield c =>
IterM.DefaultConsumers.forInPartial lift _ it' c
fun out h' acc => f out (.indirect _, rfl, h h') acc
| .done c => return c
| .skip it' h =>
IterM.DefaultConsumers.forInPartial lift _ it' init
fun out h' acc => f out (.indirect _, rfl, h h') acc
| .done _ => return init
/--
This is the default implementation of the `IteratorLoopPartial` class.
It simply iterates through the iterator using `IterM.step`. For certain iterators, more efficient
implementations are possible and should be used instead.
-/
@[always_inline, inline]
def IteratorLoopPartial.defaultImplementation {α : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad m] [Monad n] [Iterator α m β] :
IteratorLoopPartial α m n where
forInPartial lift := IterM.DefaultConsumers.forInPartial lift _
instance (α : Type w) (m : Type w Type w') (n : Type w Type w'')
[Monad m] [Monad n] [Iterator α m β] [Finite α m] :
letI : IteratorLoop α m n := .defaultImplementation
LawfulIteratorLoop α m n :=
letI : IteratorLoop α m n := .defaultImplementation
rfl
theorem IteratorLoop.wellFounded_of_finite {m : Type w Type w'}
{α β γ : Type w} [Iterator α m β] [Finite α m] :
WellFounded α m (γ := γ) fun _ _ _ => True := by
apply Subrelation.wf
(r := InvImage IterM.TerminationMeasures.Finite.Rel (fun p => p.1.finitelyManySteps))
· intro p' p h
apply Relation.TransGen.single
obtain b, h, _ | h, _ := h
· exact .yield p'.fst b, rfl, h
· exact .skip p'.fst, rfl, h
· apply InvImage.wf
exact WellFoundedRelation.wf
/--
This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance.
-/
@[always_inline, inline]
def IteratorLoop.finiteForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
(lift : γ, m γ n γ) :
ForIn' n (IterM (α := α) m β) β fun it out => it.IsPlausibleIndirectOutput out where
forIn' {γ} [Monad n] it init f :=
IteratorLoop.forIn (α := α) (m := m) lift γ (fun _ _ _ => True)
wellFounded_of_finite
it init (fun out h acc => (·, .intro) <$> f out h acc)
/--
A `ForIn'` instance for iterators. Its generic membership relation is not easy to use,
so this is not marked as `instance`. This way, more convenient instances can be built on top of it
or future library improvements will make it more comfortable.
-/
def IterM.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
[MonadLiftT m n] :
ForIn' n (IterM (α := α) m β) β fun it out => it.IsPlausibleIndirectOutput out :=
IteratorLoop.finiteForIn' (fun _ => monadLift)
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
[MonadLiftT m n] :
ForIn n (IterM (α := α) m β) β :=
haveI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
instForInOfForIn'
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [MonadLiftT m n] :
ForIn' n (IterM.Partial (α := α) m β) β fun it out => it.it.IsPlausibleIndirectOutput out where
forIn' it init f :=
IteratorLoopPartial.forInPartial (α := α) (m := m) (fun _ => monadLift) it.it init f
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
[MonadLiftT m n] :
ForM n (IterM (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoopPartial α m n]
[MonadLiftT m n] :
ForM n (IterM.Partial (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
The monadic effects of `f` are interleaved with potential effects caused by the iterator's step
function. Therefore, it may *not* be equivalent to `(← it.toList).foldlM`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.foldM` instead of `it.foldM`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def IterM.foldM {m : Type w Type w'} {n : Type w Type w''} [Monad n]
{α : Type w} {β : Type w} {γ : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
[MonadLiftT m n]
(f : γ β n γ) (init : γ) (it : IterM (α := α) m β) : n γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
The monadic effects of `f` are interleaved with potential effects caused by the iterator's step
function. Therefore, it may *not* be equivalent to `it.toList.foldlM`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.foldM` instead.
-/
@[always_inline, inline]
def IterM.Partial.foldM {m : Type w Type w'} {n : Type w Type w'} [Monad n]
{α : Type w} {β : Type w} {γ : Type w} [Iterator α m β] [IteratorLoopPartial α m n]
[MonadLiftT m n]
(f : γ β n γ) (init : γ) (it : IterM.Partial (α := α) m β) : n γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.fold` instead of `it.fold`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def IterM.fold {m : Type w Type w'} {α : Type w} {β : Type w} {γ : Type w} [Monad m]
[Iterator α m β] [Finite α m] [IteratorLoop α m m]
(f : γ β γ) (init : γ) (it : IterM (α := α) m β) : m γ :=
ForIn.forIn (m := m) it init (fun x acc => pure (ForInStep.yield (f acc x)))
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.fold` instead.
-/
@[always_inline, inline]
def IterM.Partial.fold {m : Type w Type w'} {α : Type w} {β : Type w} {γ : Type w}
[Monad m] [Iterator α m β] [IteratorLoopPartial α m m]
(f : γ β γ) (init : γ) (it : IterM.Partial (α := α) m β) : m γ :=
ForIn.forIn (m := m) it init (fun x acc => pure (ForInStep.yield (f acc x)))
/--
Iterates over the whole iterator, applying the monadic effects of each step, discarding all
emitted values.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.drain` instead of `it.drain`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def IterM.drain {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [Finite α m] (it : IterM (α := α) m β) [IteratorLoop α m m] :
m PUnit :=
it.fold (γ := PUnit) (fun _ _ => .unit) .unit
/--
Iterates over the whole iterator, applying the monadic effects of each step, discarding all
emitted values.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.drain` instead.
-/
@[always_inline, inline]
def IterM.Partial.drain {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] (it : IterM.Partial (α := α) m β) [IteratorLoopPartial α m m] :
m PUnit :=
it.fold (γ := PUnit) (fun _ _ => .unit) .unit
section Size
/--
This is the implementation of the default instance `IteratorSize.defaultImplementation`.
-/
@[always_inline, inline]
def IterM.DefaultConsumers.size {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) :
m (ULift Nat) :=
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
/--
This is the implementation of the default instance `IteratorSizePartial.defaultImplementation`.
-/
@[always_inline, inline]
def IterM.DefaultConsumers.sizePartial {α : Type w} {m : Type w Type w'} [Monad m] {β : Type w}
[Iterator α m β] [IteratorLoopPartial α m m] (it : IterM (α := α) m β) :
m (ULift Nat) :=
it.allowNontermination.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
/--
This is the default implementation of the `IteratorSize` class.
It simply iterates using `IteratorLoop` and counts the elements.
For certain iterators, more efficient implementations are possible and should be used instead.
-/
@[always_inline, inline]
def IteratorSize.defaultImplementation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] [IteratorLoop α m m] :
IteratorSize α m where
size := IterM.DefaultConsumers.size
/--
This is the default implementation of the `IteratorSizePartial` class.
It simply iterates using `IteratorLoopPartial` and counts the elements.
For certain iterators, more efficient implementations are possible and should be used instead.
-/
@[always_inline, inline]
instance IteratorSizePartial.defaultImplementation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoopPartial α m m] :
IteratorSizePartial α m where
size := IterM.DefaultConsumers.sizePartial
/--
Computes how many elements the iterator returns. In monadic situations, it is unclear which effects
are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the
returned value should be. The reference implementation, `IteratorSize.defaultImplementation`,
simply iterates over the whole iterator monadically, counting the number of emitted values.
An `IteratorSize` instance is considered lawful if it is equal to the reference implementation.
**Performance**:
Default performance is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline]
def IterM.size {α : Type} {m : Type Type w'} {β : Type} [Iterator α m β] [Monad m]
(it : IterM (α := α) m β) [IteratorSize α m] : m Nat :=
ULift.down <$> IteratorSize.size it
/--
Computes how many elements the iterator emits.
With monadic iterators (`IterM`), it is unclear which effects
are caused by calling `size`, and if the monad is nondeterministic, it is also unclear what the
returned value should be. The reference implementation, `IteratorSize.defaultImplementation`,
simply iterates over the whole iterator monadically, counting the number of emitted values.
An `IteratorSize` instance is considered lawful if it is equal to the reference implementation.
This is the partial version of `size`. It does not require a proof of finiteness and might loop
forever. It is not possible to verify the behavior in Lean because it uses `partial`.
**Performance**:
Default performance is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline]
def IterM.Partial.size {α : Type} {m : Type Type w'} {β : Type} [Iterator α m β] [Monad m]
(it : IterM.Partial (α := α) m β) [IteratorSizePartial α m] : m Nat :=
ULift.down <$> IteratorSizePartial.size it.it
end Size
end Std.Iterators

Some files were not shown because too many files have changed in this diff Show More